├── acceptance_tests ├── lib │ ├── __init__.py │ ├── Helm.py │ ├── HelmPush.py │ ├── common.py │ └── ChartMuseum.py ├── 01-helm.robot └── 02-chartmuseum.robot ├── testdata ├── charts │ ├── helm2 │ │ └── mychart │ │ │ ├── Chart.yaml │ │ │ ├── charts │ │ │ └── mariadb-5.11.3.tgz │ │ │ ├── requirements.yaml │ │ │ ├── requirements.lock │ │ │ └── templates │ │ │ └── pod.yaml │ └── helm3 │ │ └── my-v3-chart │ │ ├── templates │ │ ├── serviceaccount.yaml │ │ ├── service.yaml │ │ ├── tests │ │ │ └── test-connection.yaml │ │ ├── ingress.yaml │ │ ├── NOTES.txt │ │ ├── deployment.yaml │ │ └── _helpers.tpl │ │ ├── .helmignore │ │ ├── Chart.yaml │ │ ├── values.yaml │ │ └── values.schema.json ├── pgp │ ├── helm-test-key.pub │ ├── helm-test-key.secret │ └── NOTE.txt └── tls │ ├── client.crt │ ├── server.crt │ ├── client_ca.crt │ ├── server_ca.crt │ ├── client.key │ └── server.key ├── .github ├── dependabot.yaml └── workflows │ ├── release.yml │ ├── build.yml │ └── build-pr.yml ├── pkg ├── helm │ ├── serverinfo.go │ ├── version.go │ ├── index_test.go │ ├── chart.go │ ├── index.go │ ├── chart_test.go │ ├── repo_test.go │ └── repo.go └── chartmuseum │ ├── download.go │ ├── client.go │ ├── upload.go │ ├── client_test.go │ ├── option.go │ ├── download_test.go │ └── upload_test.go ├── .gitignore ├── plugin.yaml ├── .goreleaser.yml ├── scripts ├── test.sh ├── acceptance.sh ├── install_plugin.sh └── setup_test_environment.sh ├── Makefile ├── BUILDING.md ├── go.mod ├── cmd └── helm-cm-push │ ├── main_test.go │ └── main.go ├── README.md ├── LICENSE └── go.sum /acceptance_tests/lib/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testdata/charts/helm2/mychart/Chart.yaml: -------------------------------------------------------------------------------- 1 | name: mychart 2 | version: 0.1.0 -------------------------------------------------------------------------------- /testdata/pgp/helm-test-key.pub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chartmuseum/helm-push/HEAD/testdata/pgp/helm-test-key.pub -------------------------------------------------------------------------------- /testdata/pgp/helm-test-key.secret: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chartmuseum/helm-push/HEAD/testdata/pgp/helm-test-key.secret -------------------------------------------------------------------------------- /testdata/pgp/NOTE.txt: -------------------------------------------------------------------------------- 1 | These files were copied directly from github.com/kubernetes/helm repo 2 | in the pkg/provenance/testdata directory -------------------------------------------------------------------------------- /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | updates: 4 | - package-ecosystem: "gomod" 5 | directory: "/" 6 | schedule: 7 | interval: "daily" 8 | -------------------------------------------------------------------------------- /testdata/charts/helm2/mychart/charts/mariadb-5.11.3.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chartmuseum/helm-push/HEAD/testdata/charts/helm2/mychart/charts/mariadb-5.11.3.tgz -------------------------------------------------------------------------------- /testdata/charts/helm2/mychart/requirements.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: mariadb 3 | version: 5.x.x 4 | repository: https://kubernetes-charts.storage.googleapis.com/ -------------------------------------------------------------------------------- /pkg/helm/serverinfo.go: -------------------------------------------------------------------------------- 1 | package helm 2 | 3 | type ( 4 | // ServerInfo describes the server information 5 | ServerInfo struct { 6 | ContextPath string `json:"contextPath"` 7 | } 8 | ) 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/*.pyc 2 | **/*.tgz 3 | **/*.tgz.prov 4 | .chartmuseum.log 5 | .cover/ 6 | .helm2/ 7 | .helm3/ 8 | .idea/ 9 | .robot/ 10 | .venv/ 11 | bin/ 12 | dist/ 13 | releases/ 14 | testbin/ 15 | testdata/**/tmp 16 | vendor/ 17 | -------------------------------------------------------------------------------- /testdata/charts/helm2/mychart/requirements.lock: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: mariadb 3 | repository: https://kubernetes-charts.storage.googleapis.com/ 4 | version: 5.11.3 5 | digest: sha256:e09c8ca7126923a30e39f442c3863b44684d4eb3f7b6dc869f0206da4463f416 6 | generated: "2019-12-16T18:06:12.535235-06:00" 7 | -------------------------------------------------------------------------------- /testdata/charts/helm3/my-v3-chart/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "my-v3-chart.serviceAccountName" . }} 6 | labels: 7 | {{ include "my-v3-chart.labels" . | nindent 4 }} 8 | {{- end -}} 9 | -------------------------------------------------------------------------------- /testdata/charts/helm2/mychart/templates/pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: '{{- printf "%s-%s" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-" -}}' 5 | spec: 6 | containers: 7 | - image: busybox 8 | name: '{{ .Chart.Name }}' 9 | command: ['/bin/sh', '-c', 'while true; do echo {{ .Release.Name }}; sleep 5; done'] -------------------------------------------------------------------------------- /plugin.yaml: -------------------------------------------------------------------------------- 1 | name: "cm-push" 2 | version: "0.10.4" 3 | usage: "Please see https://github.com/chartmuseum/helm-push for usage" 4 | description: "Push chart package to ChartMuseum" 5 | command: "$HELM_PLUGIN_DIR/bin/helm-cm-push" 6 | downloaders: 7 | - command: "bin/helm-cm-push" 8 | protocols: 9 | - "cm" 10 | useTunnel: false 11 | hooks: 12 | install: "cd $HELM_PLUGIN_DIR; scripts/install_plugin.sh" 13 | update: "cd $HELM_PLUGIN_DIR; scripts/install_plugin.sh" 14 | -------------------------------------------------------------------------------- /testdata/charts/helm3/my-v3-chart/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *~ 18 | # Various IDEs 19 | .project 20 | .idea/ 21 | *.tmproj 22 | .vscode/ 23 | -------------------------------------------------------------------------------- /testdata/charts/helm3/my-v3-chart/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "my-v3-chart.fullname" . }} 5 | labels: 6 | {{- include "my-v3-chart.labels" . | nindent 4 }} 7 | spec: 8 | type: {{ .Values.service.type }} 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | targetPort: http 12 | protocol: TCP 13 | name: http 14 | selector: 15 | {{- include "my-v3-chart.selectorLabels" . | nindent 4 }} 16 | -------------------------------------------------------------------------------- /testdata/charts/helm3/my-v3-chart/templates/tests/test-connection.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: "{{ include "my-v3-chart.fullname" . }}-test-connection" 5 | labels: 6 | {{ include "my-v3-chart.labels" . | nindent 4 }} 7 | annotations: 8 | "helm.sh/hook": test-success 9 | spec: 10 | containers: 11 | - name: wget 12 | image: busybox 13 | command: ['wget'] 14 | args: ['{{ include "my-v3-chart.fullname" . }}:{{ .Values.service.port }}'] 15 | restartPolicy: Never 16 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | env: 2 | - GO111MODULE=on 3 | before: 4 | hooks: 5 | - go mod download 6 | builds: 7 | - main: ./cmd/helm-cm-push 8 | binary: ./bin/helm-cm-push 9 | env: 10 | - CGO_ENABLED=0 11 | goos: 12 | - darwin 13 | - linux 14 | - windows 15 | goarch: 16 | - amd64 17 | - arm 18 | - arm64 19 | goarm: 20 | - "6" 21 | - "7" 22 | 23 | archives: 24 | - id: tarball 25 | format: tar.gz 26 | files: 27 | - LICENSE 28 | - plugin.yaml 29 | -------------------------------------------------------------------------------- /scripts/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ex 2 | 3 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | cd $DIR/../ 5 | 6 | rm -rf .cover/ .test/ 7 | mkdir .cover/ .test/ 8 | trap "rm -rf .test/" EXIT 9 | 10 | for pkg in `go list ./... | grep -v /vendor/`; do 11 | HELM_BIN="${PWD}/helm2" go test -v -covermode=atomic \ 12 | -coverprofile=".cover/$(echo $pkg | sed 's/\//_/g').cover.out" $pkg 13 | done 14 | 15 | echo "mode: set" > .cover/cover.out && cat .cover/*.cover.out | grep -v mode: | sort -r | \ 16 | awk '{if($1 != last) {print $0;last=$1}}' >> .cover/cover.out 17 | 18 | go tool cover -html=.cover/cover.out -o=.cover/coverage.html 19 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | goreleaser: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - 13 | name: Checkout 14 | uses: actions/checkout@v4 15 | with: 16 | fetch-depth: 0 17 | - 18 | name: Set up Go 19 | uses: actions/setup-go@v2 20 | with: 21 | go-version: "1.21" 22 | - 23 | name: Run GoReleaser 24 | uses: goreleaser/goreleaser-action@v2 25 | with: 26 | version: latest 27 | args: release --rm-dist 28 | env: 29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | -------------------------------------------------------------------------------- /pkg/helm/version.go: -------------------------------------------------------------------------------- 1 | package helm 2 | 3 | import ( 4 | "os" 5 | "os/exec" 6 | ) 7 | 8 | type ( 9 | HelmMajorVersion int 10 | ) 11 | 12 | const ( 13 | HelmMajorVersion2 = 2 14 | HelmMajorVersion3 = 3 15 | ) 16 | 17 | var ( 18 | helmMajorVersionCurrent HelmMajorVersion 19 | ) 20 | 21 | func HelmMajorVersionCurrent() HelmMajorVersion { 22 | if helmMajorVersionCurrent != 0 { 23 | return helmMajorVersionCurrent 24 | } 25 | helmBin, helmBinVarSet := os.LookupEnv("HELM_BIN") 26 | if !helmBinVarSet { 27 | helmBin = "helm" 28 | } 29 | helmVersion2CheckCmd := exec.Command(helmBin, "version", "-c", "--tls") 30 | err := helmVersion2CheckCmd.Run() 31 | if e, ok := err.(*exec.ExitError); ok && !e.Success() { 32 | helmMajorVersionCurrent = HelmMajorVersion3 33 | } else { 34 | helmMajorVersionCurrent = HelmMajorVersion2 35 | } 36 | return helmMajorVersionCurrent 37 | } 38 | -------------------------------------------------------------------------------- /pkg/helm/index_test.go: -------------------------------------------------------------------------------- 1 | package helm 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestLoadIndex(t *testing.T) { 8 | // No context path 9 | index, err := LoadIndex([]byte("apiVersion: v1\nentries: {}\ngenerated: \"2018-08-08T08:21:33Z\"\n")) 10 | if err != nil { 11 | t.Error("unexpected error loading index", err) 12 | } 13 | if index.ServerInfo.ContextPath != "" { 14 | t.Errorf("expexted empty context path, instead got %s", index.ServerInfo.ContextPath) 15 | } 16 | 17 | // Has context path 18 | index, err = LoadIndex([]byte("apiVersion: v1\nserverInfo:\n contextPath: /helm/v1\nentries: {}\ngenerated: \"2018-08-08T08:21:33Z\"\n")) 19 | if err != nil { 20 | t.Error("unexpected error loading index", err) 21 | } 22 | if index.ServerInfo.ContextPath != "/helm/v1" { 23 | t.Errorf("expexted context path to be /helm/v1, instead got %s", index.ServerInfo.ContextPath) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /scripts/acceptance.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ex 2 | 3 | PY_REQUIRES="robotframework==4.1.1" 4 | 5 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 6 | cd $DIR/../ 7 | 8 | if [ "$(uname)" == "Darwin" ]; then 9 | PLATFORM="darwin" 10 | else 11 | PLATFORM="linux" 12 | fi 13 | 14 | export PATH="$PWD/testbin:$PWD/bin/$PLATFORM/amd64:$PATH" 15 | export HELM_PUSH_PLUGIN_NO_INSTALL_HOOK=1 16 | 17 | export TEST_V2_HELM_HOME="$PWD/.helm2" 18 | HELM_HOME=${TEST_V2_HELM_HOME} helm2 init --client-only 19 | 20 | export TEST_V3_XDG_CACHE_HOME="$PWD/.helm3/xdg/cache" 21 | export TEST_V3_XDG_CONFIG_HOME="$PWD/.helm3/xdg/config" 22 | export TEST_V3_XDG_DATA_HOME="$PWD/.helm3/xdg/data" 23 | 24 | if [ ! -d .venv/ ]; then 25 | virtualenv -p $(which python3) .venv/ 26 | .venv/bin/python .venv/bin/pip3 install $PY_REQUIRES 27 | fi 28 | 29 | mkdir -p .robot/ 30 | .venv/bin/robot --outputdir=.robot/ acceptance_tests/ 31 | -------------------------------------------------------------------------------- /testdata/charts/helm3/my-v3-chart/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: my-v3-chart 3 | description: A Helm chart for Kubernetes 4 | 5 | # A chart can be either an 'application' or a 'library' chart. 6 | # 7 | # Application charts are a collection of templates that can be packaged into versioned archives 8 | # to be deployed. 9 | # 10 | # Library charts provide useful utilities or functions for the chart developer. They're included as 11 | # a dependency of application charts to inject those utilities and functions into the rendering 12 | # pipeline. Library charts do not define any templates and therefore cannot be deployed. 13 | type: application 14 | 15 | # This is the chart version. This version number should be incremented each time you make changes 16 | # to the chart and its templates, including the app version. 17 | version: 0.1.0 18 | 19 | # This is the version number of the application being deployed. This version number should be 20 | # incremented each time you make changes to the application. 21 | appVersion: 1.16.0 22 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: main 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v4 13 | - name: setup go environment 14 | uses: actions/setup-go@v2 15 | with: 16 | go-version: '1.21' 17 | - name: run unit tests 18 | run: sudo pip install virtualenv && make test 19 | - name: build binary 20 | run: make build_linux link_linux 21 | - name: run acceptance tests 22 | run: make acceptance 23 | - name: upload coverage report 24 | uses: actions/upload-artifact@main 25 | with: 26 | name: helmpush-coverage-report-${{ github.sha }} 27 | path: .cover/ 28 | if: always() 29 | - name: upload acceptance report 30 | uses: actions/upload-artifact@main 31 | with: 32 | name: helmpush-acceptance-report-${{ github.sha }} 33 | path: .robot/ 34 | if: always() 35 | -------------------------------------------------------------------------------- /pkg/chartmuseum/download.go: -------------------------------------------------------------------------------- 1 | package chartmuseum 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/url" 7 | "path" 8 | "strings" 9 | ) 10 | 11 | // DownloadFile downloads a file from ChartMuseum 12 | func (client *Client) DownloadFile(filePath string) (*http.Response, error) { 13 | u, err := url.Parse(client.opts.url) 14 | if err != nil { 15 | return nil, err 16 | } 17 | 18 | u.Path = path.Join(client.opts.contextPath, strings.TrimPrefix(u.Path, client.opts.contextPath), filePath) 19 | req, err := http.NewRequest("GET", u.String(), nil) 20 | if err != nil { 21 | return nil, err 22 | } 23 | 24 | if client.opts.accessToken != "" { 25 | if client.opts.authHeader != "" { 26 | req.Header.Set(client.opts.authHeader, client.opts.accessToken) 27 | } else { 28 | req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", client.opts.accessToken)) 29 | } 30 | } else if client.opts.username != "" && client.opts.password != "" { 31 | req.SetBasicAuth(client.opts.username, client.opts.password) 32 | } 33 | 34 | return client.Do(req) 35 | } 36 | -------------------------------------------------------------------------------- /pkg/helm/chart.go: -------------------------------------------------------------------------------- 1 | package helm 2 | 3 | import ( 4 | "helm.sh/helm/v3/pkg/chart" 5 | "helm.sh/helm/v3/pkg/chart/loader" 6 | "helm.sh/helm/v3/pkg/chartutil" 7 | ) 8 | 9 | type ( 10 | // Chart is a helm package that contains metadata 11 | Chart struct { 12 | *chart.Chart 13 | } 14 | ) 15 | 16 | // SetVersion overrides the chart version 17 | func (c *Chart) SetVersion(version string) { 18 | c.Metadata.Version = version 19 | } 20 | 21 | // SetAppVersion overrides the app version 22 | func (c *Chart) SetAppVersion(appVersion string) { 23 | c.Metadata.AppVersion = appVersion 24 | } 25 | 26 | // GetChartByName returns a chart by "name", which can be 27 | // either a directory or .tgz package 28 | func GetChartByName(name string) (*Chart, error) { 29 | cc, err := loader.Load(name) 30 | if err != nil { 31 | return nil, err 32 | } 33 | return &Chart{cc}, nil 34 | } 35 | 36 | // CreateChartPackage creates a new .tgz package in directory 37 | func CreateChartPackage(c *Chart, outDir string) (string, error) { 38 | return chartutil.Save(c.Chart, outDir) 39 | } 40 | -------------------------------------------------------------------------------- /.github/workflows/build-pr.yml: -------------------------------------------------------------------------------- 1 | name: build-pr 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v4 14 | - name: setup go environment 15 | uses: actions/setup-go@v2 16 | with: 17 | go-version: '1.21' 18 | - name: run unit tests 19 | run: sudo pip install virtualenv && make test 20 | - name: build binary 21 | run: make build_linux link_linux 22 | - name: run acceptance tests 23 | run: make acceptance 24 | - name: upload coverage report 25 | uses: actions/upload-artifact@main 26 | with: 27 | name: helmpush-coverage-report-${{ github.sha }} 28 | path: .cover/ 29 | if: always() 30 | - name: upload acceptance report 31 | uses: actions/upload-artifact@main 32 | with: 33 | name: helmpush-acceptance-report-${{ github.sha }} 34 | path: .robot/ 35 | if: always() 36 | -------------------------------------------------------------------------------- /testdata/charts/helm3/my-v3-chart/templates/ingress.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.ingress.enabled -}} 2 | {{- $fullName := include "my-v3-chart.fullname" . -}} 3 | {{- $svcPort := .Values.service.port -}} 4 | {{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} 5 | apiVersion: networking.k8s.io/v1beta1 6 | {{- else -}} 7 | apiVersion: extensions/v1beta1 8 | {{- end }} 9 | kind: Ingress 10 | metadata: 11 | name: {{ $fullName }} 12 | labels: 13 | {{- include "my-v3-chart.labels" . | nindent 4 }} 14 | {{- with .Values.ingress.annotations }} 15 | annotations: 16 | {{- toYaml . | nindent 4 }} 17 | {{- end }} 18 | spec: 19 | {{- if .Values.ingress.tls }} 20 | tls: 21 | {{- range .Values.ingress.tls }} 22 | - hosts: 23 | {{- range .hosts }} 24 | - {{ . | quote }} 25 | {{- end }} 26 | secretName: {{ .secretName }} 27 | {{- end }} 28 | {{- end }} 29 | rules: 30 | {{- range .Values.ingress.hosts }} 31 | - host: {{ .host | quote }} 32 | http: 33 | paths: 34 | {{- range .paths }} 35 | - path: {{ . }} 36 | backend: 37 | serviceName: {{ $fullName }} 38 | servicePort: {{ $svcPort }} 39 | {{- end }} 40 | {{- end }} 41 | {{- end }} 42 | -------------------------------------------------------------------------------- /pkg/helm/index.go: -------------------------------------------------------------------------------- 1 | package helm 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | 8 | "github.com/ghodss/yaml" 9 | "helm.sh/helm/v3/pkg/repo" 10 | ) 11 | 12 | type ( 13 | // Index represents the index file in a chart repository 14 | Index struct { 15 | *repo.IndexFile 16 | ServerInfo ServerInfo `json:"serverInfo"` 17 | } 18 | 19 | // IndexDownloader is a function to download the index 20 | IndexDownloader func() ([]byte, error) 21 | ) 22 | 23 | // GetIndexByRepo returns index by repository 24 | func GetIndexByRepo(repo *Repo, downloadIndex IndexDownloader) (*Index, error) { 25 | if repo.Config.Name != "" { 26 | return GetIndexByDownloader(func() ([]byte, error) { 27 | return os.ReadFile(filepath.Join(repo.CachePath, fmt.Sprintf("%s-index.yaml", repo.Config.Name))) 28 | }) 29 | } 30 | return GetIndexByDownloader(downloadIndex) 31 | } 32 | 33 | // GetIndexByDownloader takes binary data from IndexDownloader and returns an Index object 34 | func GetIndexByDownloader(downloadIndex IndexDownloader) (*Index, error) { 35 | b, err := downloadIndex() 36 | if err != nil { 37 | return nil, err 38 | } 39 | return LoadIndex(b) 40 | } 41 | 42 | // LoadIndex loads an index file 43 | func LoadIndex(data []byte) (*Index, error) { 44 | i := &Index{} 45 | if err := yaml.Unmarshal(data, i); err != nil { 46 | return i, err 47 | } 48 | i.SortEntries() 49 | return i, nil 50 | } 51 | -------------------------------------------------------------------------------- /pkg/chartmuseum/client.go: -------------------------------------------------------------------------------- 1 | package chartmuseum 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | v2tlsutil "k8s.io/helm/pkg/tlsutil" 8 | ) 9 | 10 | type ( 11 | // Client is an HTTP client to connect to ChartMuseum 12 | Client struct { 13 | *http.Client 14 | opts options 15 | } 16 | ) 17 | 18 | // Option configures the client with the provided options. 19 | func (client *Client) Option(opts ...Option) *Client { 20 | for _, opt := range opts { 21 | opt(&client.opts) 22 | } 23 | return client 24 | } 25 | 26 | // NewClient creates a new client. 27 | func NewClient(opts ...Option) (*Client, error) { 28 | var client Client 29 | client.Client = &http.Client{} 30 | client.Option(opts...) 31 | client.Timeout = client.opts.timeout 32 | 33 | //Enable tls config if configured 34 | tr, err := newTransport( 35 | client.opts.certFile, 36 | client.opts.keyFile, 37 | client.opts.caFile, 38 | client.opts.insecureSkipVerify, 39 | ) 40 | if err != nil { 41 | return nil, err 42 | } 43 | 44 | client.Transport = tr 45 | 46 | return &client, nil 47 | } 48 | 49 | //Create transport with TLS config 50 | func newTransport(certFile, keyFile, caFile string, insecureSkipVerify bool) (*http.Transport, error) { 51 | transport := &http.Transport{} 52 | 53 | tlsConf, err := v2tlsutil.NewClientTLS(certFile, keyFile, caFile) 54 | if err != nil { 55 | return nil, fmt.Errorf("can't create TLS config: %s", err.Error()) 56 | } 57 | tlsConf.InsecureSkipVerify = insecureSkipVerify 58 | 59 | transport.TLSClientConfig = tlsConf 60 | transport.Proxy = http.ProxyFromEnvironment 61 | 62 | return transport, nil 63 | } 64 | -------------------------------------------------------------------------------- /acceptance_tests/01-helm.robot: -------------------------------------------------------------------------------- 1 | *** Settings *** 2 | Documentation Tests to verify that helm-push can be installed 3 | ... and run as a Helm plugin etc. 4 | Library lib/Helm.py 5 | Suite Setup Suite Setup 6 | Suite Teardown Suite Teardown 7 | 8 | *** Test Cases *** 9 | Plugin installs on Helm 2 10 | Test plugin installation 2 11 | 12 | Plugin installs on Helm 3 13 | Test plugin installation 3 14 | 15 | *** Keywords *** 16 | Test plugin installation 17 | [Arguments] ${version} 18 | set helm version ${version} 19 | helm-push can be installed as a Helm plugin 20 | helm-push is listed as a Helm plugin after install 21 | helm-push can be run as a Helm plugin 22 | helm-push can be removed 23 | helm-push is not listed as a Helm plugin after removal 24 | 25 | helm-push can be installed as a Helm plugin 26 | install helm plugin 27 | return code should be 0 28 | 29 | helm-push is listed as a Helm plugin after install 30 | check helm plugin 31 | return code should be 0 32 | 33 | helm-push can be run as a Helm plugin 34 | run helm plugin 35 | return code should be 0 36 | 37 | helm-push can be removed 38 | remove helm plugin 39 | return code should be 0 40 | 41 | helm-push is not listed as a Helm plugin after removal 42 | check helm plugin 43 | return code should not be 0 44 | 45 | Suite Setup 46 | set helm version 3 47 | remove helm plugin 48 | set helm version 2 49 | remove helm plugin 50 | 51 | Suite Teardown 52 | set helm version 3 53 | remove helm plugin 54 | set helm version 2 55 | remove helm plugin 56 | -------------------------------------------------------------------------------- /testdata/charts/helm3/my-v3-chart/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | 1. Get the application URL by running these commands: 2 | {{- if .Values.ingress.enabled }} 3 | {{- range $host := .Values.ingress.hosts }} 4 | {{- range .paths }} 5 | http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ . }} 6 | {{- end }} 7 | {{- end }} 8 | {{- else if contains "NodePort" .Values.service.type }} 9 | export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "my-v3-chart.fullname" . }}) 10 | export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") 11 | echo http://$NODE_IP:$NODE_PORT 12 | {{- else if contains "LoadBalancer" .Values.service.type }} 13 | NOTE: It may take a few minutes for the LoadBalancer IP to be available. 14 | You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "my-v3-chart.fullname" . }}' 15 | export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "my-v3-chart.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") 16 | echo http://$SERVICE_IP:{{ .Values.service.port }} 17 | {{- else if contains "ClusterIP" .Values.service.type }} 18 | export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "my-v3-chart.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") 19 | echo "Visit http://127.0.0.1:8080 to use your application" 20 | kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:80 21 | {{- end }} 22 | -------------------------------------------------------------------------------- /acceptance_tests/lib/Helm.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import common 4 | 5 | class Helm(common.CommandRunner): 6 | def set_helm_version(self, version): 7 | version = str(version) 8 | if version == '2': 9 | common.HELM_EXE = 'HELM_HOME=%s helm2' % os.getenv('TEST_V2_HELM_HOME', '') 10 | elif version == '3': 11 | common.HELM_EXE = 'XDG_CACHE_HOME=%s XDG_CONFIG_HOME=%s XDG_DATA_HOME=%s helm3' % \ 12 | (os.getenv('TEST_V3_XDG_CACHE_HOME', ''), os.getenv('TEST_V3_XDG_CONFIG_HOME', ''), 13 | os.getenv('TEST_V3_XDG_DATA_HOME', '')) 14 | else: 15 | raise Exception('invalid Helm version provided: %s' % version) 16 | 17 | def use_test_chart_built_by_same_helm_version(self): 18 | common.USE_OPPOSITE_VERSION = False 19 | 20 | def use_test_chart_built_by_opposite_helm_version(self): 21 | common.USE_OPPOSITE_VERSION = True 22 | 23 | def add_chart_repo(self): 24 | self.remove_chart_repo() 25 | self.run_command('%s repo add %s %s' % (common.HELM_EXE, common.HELM_REPO_NAME, common.HELM_REPO_URL)) 26 | 27 | def remove_chart_repo(self): 28 | self.run_command('%s repo remove %s' % (common.HELM_EXE, common.HELM_REPO_NAME)) 29 | 30 | def install_helm_plugin(self): 31 | self.run_command('%s plugin install %s' % (common.HELM_EXE, self.rootdir)) 32 | 33 | def check_helm_plugin(self): 34 | self.run_command('%s plugin list | grep ^cm-push' % common.HELM_EXE) 35 | 36 | def run_helm_plugin(self): 37 | self.run_command('%s cm-push --check-helm-version' % common.HELM_EXE) 38 | 39 | def remove_helm_plugin(self): 40 | self.run_command('%s plugin remove cm-push' % common.HELM_EXE) 41 | -------------------------------------------------------------------------------- /testdata/charts/helm3/my-v3-chart/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for my-v3-chart. 2 | # This is a YAML-formatted file. 3 | # Declare variables to be passed into your templates. 4 | 5 | replicaCount: 1 6 | 7 | image: 8 | repository: nginx 9 | pullPolicy: IfNotPresent 10 | 11 | imagePullSecrets: [] 12 | nameOverride: "" 13 | fullnameOverride: "" 14 | 15 | serviceAccount: 16 | # Specifies whether a service account should be created 17 | create: true 18 | # The name of the service account to use. 19 | # If not set and create is true, a name is generated using the fullname template 20 | name: 21 | 22 | podSecurityContext: {} 23 | # fsGroup: 2000 24 | 25 | securityContext: {} 26 | # capabilities: 27 | # drop: 28 | # - ALL 29 | # readOnlyRootFilesystem: true 30 | # runAsNonRoot: true 31 | # runAsUser: 1000 32 | 33 | service: 34 | type: ClusterIP 35 | port: 80 36 | 37 | ingress: 38 | enabled: false 39 | annotations: {} 40 | # kubernetes.io/ingress.class: nginx 41 | # kubernetes.io/tls-acme: "true" 42 | hosts: 43 | - host: chart-example.local 44 | paths: [] 45 | tls: [] 46 | # - secretName: chart-example-tls 47 | # hosts: 48 | # - chart-example.local 49 | 50 | resources: {} 51 | # We usually recommend not to specify default resources and to leave this as a conscious 52 | # choice for the user. This also increases chances charts run on environments with little 53 | # resources, such as Minikube. If you do want to specify resources, uncomment the following 54 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'. 55 | # limits: 56 | # cpu: 100m 57 | # memory: 128Mi 58 | # requests: 59 | # cpu: 100m 60 | # memory: 128Mi 61 | 62 | nodeSelector: {} 63 | 64 | tolerations: [] 65 | 66 | affinity: {} 67 | -------------------------------------------------------------------------------- /acceptance_tests/lib/HelmPush.py: -------------------------------------------------------------------------------- 1 | import common 2 | 3 | 4 | class HelmPush(common.CommandRunner): 5 | def _testchart_path(self): 6 | if common.USE_OPPOSITE_VERSION: 7 | if 'helm3' in common.HELM_EXE: 8 | return '%s/helm2/mychart' % common.TESTCHARTS_DIR 9 | return '%s/helm3/my-v3-chart' % common.TESTCHARTS_DIR 10 | else: 11 | if 'helm3' in common.HELM_EXE: 12 | return '%s/helm3/my-v3-chart' % common.TESTCHARTS_DIR 13 | return '%s/helm2/mychart' % common.TESTCHARTS_DIR 14 | 15 | def helm_major_version_detected_by_plugin_is(self, version): 16 | cmd = '%s cm-push --check-helm-version' % common.HELM_EXE 17 | self.run_command(cmd) 18 | self.output_contains(version) 19 | 20 | def push_chart_directory(self, version=''): 21 | cmd = '%s cm-push %s %s' % (common.HELM_EXE, self._testchart_path(), common.HELM_REPO_NAME) 22 | if version: 23 | cmd += " --version=\"%s\"" % version 24 | self.run_command(cmd) 25 | 26 | def push_chart_directory_to_url(self, version=''): 27 | cmd = '%s cm-push %s %s' % (common.HELM_EXE, self._testchart_path(), common.HELM_REPO_URL) 28 | if version: 29 | cmd += " --version=\"%s\"" % version 30 | self.run_command(cmd) 31 | 32 | def push_chart_package(self, version=''): 33 | cmd = '%s cm-push %s/*.tgz %s' % (common.HELM_EXE, self._testchart_path(), common.HELM_REPO_NAME) 34 | if version: 35 | cmd += " --version=\"%s\"" % version 36 | self.run_command(cmd) 37 | 38 | def push_chart_package_to_url(self, version=''): 39 | cmd = '%s cm-push %s %s' % (common.HELM_EXE, self._testchart_path(), common.HELM_REPO_URL) 40 | if version: 41 | cmd += " --version=\"%s\"" % version 42 | self.run_command(cmd) 43 | -------------------------------------------------------------------------------- /testdata/charts/helm3/my-v3-chart/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ include "my-v3-chart.fullname" . }} 5 | labels: 6 | {{- include "my-v3-chart.labels" . | nindent 4 }} 7 | spec: 8 | replicas: {{ .Values.replicaCount }} 9 | selector: 10 | matchLabels: 11 | {{- include "my-v3-chart.selectorLabels" . | nindent 6 }} 12 | template: 13 | metadata: 14 | labels: 15 | {{- include "my-v3-chart.selectorLabels" . | nindent 8 }} 16 | spec: 17 | {{- with .Values.imagePullSecrets }} 18 | imagePullSecrets: 19 | {{- toYaml . | nindent 8 }} 20 | {{- end }} 21 | serviceAccountName: {{ include "my-v3-chart.serviceAccountName" . }} 22 | securityContext: 23 | {{- toYaml .Values.podSecurityContext | nindent 8 }} 24 | containers: 25 | - name: {{ .Chart.Name }} 26 | securityContext: 27 | {{- toYaml .Values.securityContext | nindent 12 }} 28 | image: "{{ .Values.image.repository }}:{{ .Chart.AppVersion }}" 29 | imagePullPolicy: {{ .Values.image.pullPolicy }} 30 | ports: 31 | - name: http 32 | containerPort: 80 33 | protocol: TCP 34 | livenessProbe: 35 | httpGet: 36 | path: / 37 | port: http 38 | readinessProbe: 39 | httpGet: 40 | path: / 41 | port: http 42 | resources: 43 | {{- toYaml .Values.resources | nindent 12 }} 44 | {{- with .Values.nodeSelector }} 45 | nodeSelector: 46 | {{- toYaml . | nindent 8 }} 47 | {{- end }} 48 | {{- with .Values.affinity }} 49 | affinity: 50 | {{- toYaml . | nindent 8 }} 51 | {{- end }} 52 | {{- with .Values.tolerations }} 53 | tolerations: 54 | {{- toYaml . | nindent 8 }} 55 | {{- end }} 56 | -------------------------------------------------------------------------------- /testdata/tls/client.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFHjCCAwagAwIBAgIUVh0GhVN5oZJUfbbKiS9XIH722WowDQYJKoZIhvcNAQEL 3 | BQAwTDELMAkGA1UEBhMCICAxCjAIBgNVBAgMASAxCjAIBgNVBAcMASAxCjAIBgNV 4 | BAoMASAxGTAXBgNVBAMMEEhFTE1DTElFTlRDRVJUQ0EwHhcNMjEwMTAyMDkwNTQ1 5 | WhcNMzAxMjMxMDkwNTQ1WjBGMQswCQYDVQQGEwIgIDEKMAgGA1UECAwBIDEKMAgG 6 | A1UEBwwBIDEKMAgGA1UECgwBIDETMBEGA1UEAwwKSEVMTUNMSUVOVDCCAiIwDQYJ 7 | KoZIhvcNAQEBBQADggIPADCCAgoCggIBAMYC80vkz8mK/cvzvr5x3Ua//2kAV2pd 8 | sJyA4ujFr6KRvqd1Hk9N6V5WbbE7VnYWQ51P5Gx40iv0lzHhzC93t5wHxjIOXYLE 9 | uZqpeJMJBSTJnHz12A6b9lAnhW3psFBfAU4lsi+u4zimLXJTQw+ONKWsyW2C2ZXs 10 | IVpNusg75exMUwFaklCxB0Ib+AH3nH0fs56J7GZwJNokftcB3Op1qx9XAIQAk0dJ 11 | PRnrzZBQ9xp/WHtNdRgTnvJcDab8noI4oeTrLAU3yAFEAHUUxMMPgdpEe61cLbHT 12 | xxATH70zf6v95KpCQN2LvdNsjgcUmPRjNy7pjL+0sSBhmBYHPVvpXnkZdbF2C804 13 | EYGIYrmNRXcSEOQxQOCSiYr9BSQUWK1wC0xRZGrAMQQ/fzda0lfYlkr3guq7DPG1 14 | 2ZCNEc80vZ1YjRjApWQoA+xOOwujJYJLI3XFG+t0/w/sB5f4slXrKwBHP0BxSw4p 15 | I0ab0PeY9GmI5l704QmBgQF5u/mh7tdSR0najj6IkwEyG85UMufyUyaggQHvwTeN 16 | DLP2ItLyshyCA9JB/mnHoEbarUgsgV5/sPQqgMdqYUnPcInuQyz/sTVD7x8YOxI6 17 | xo0o+pnNuN9jMaT2pDQ2+IYzBQihdDNxgxPtS9aZyXE2yUcpiGFtBn4qWMKlOs5y 18 | 1Mu0bXunR6hnAgMBAAEwDQYJKoZIhvcNAQELBQADggIBAKGvPoNmC0d3UwcRdS+x 19 | 6MY4S2nryyclVmen5s9aSEkVqPoDSxH6O5L/1eOblj0BlS8K6budcgseAkJrvw8+ 20 | ZYFXdKBpFninbHv4IySjWupE9rH+rSquNVQrZYK6ZHzfpW5dBfeHEJgyzJNRcu6i 21 | 7ySUbBYraL7CHgJKNR2b38WrIGBKeYkkmb3gEIteq79XhZpSWLxc4OUArVNtzTed 22 | v+5oqZImmAvaCrlRRd7/aCkQFsNzbQ7AJfZnPNrZz7A4mkV1yVRFBRMrllJFc6mH 23 | kCv5Hm77GmVtXaZuYnQE0AqpkX8TLACDER+MfP8KwURje14nBqsng1kr4YOUUMyb 24 | SyVPKtM5b/CCGt3BhX80kyy7unfwK/avDuuFaRcq59zEvfD1XuWtMwc/xaw5gXDi 25 | vqo7jLEpFnEC7PdnU8hK8POZnJESdAG8aZGXDy+tPvYQ2DqEXjLc/7FXMhverbLV 26 | b6LuSOOrTff4HM5eQ517TVJNB+MmSosEOC2mH62lu7gEXpM45UI4UelmJTTP+7cy 27 | LbUaxjs/ff6kA1RhbQVWR0cZyw89Pj73mrbjg6knVKzIw8lU3pTQXhwaVbp8HIzT 28 | zN+i8h7W3WuSjwjx4tzmUcCobzjJFKZJbM54EaAXdgSj/x541o6BSFm/IsZ9a81j 29 | waDb9wYoG3nZtVb/OYDUFU7Z 30 | -----END CERTIFICATE----- 31 | -------------------------------------------------------------------------------- /testdata/tls/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFMzCCAxugAwIBAgIUQGMSwNP26VEf81fG96Gw+p8XDHkwDQYJKoZIhvcNAQEL 3 | BQAwTDELMAkGA1UEBhMCICAxCjAIBgNVBAgMASAxCjAIBgNVBAcMASAxCjAIBgNV 4 | BAoMASAxGTAXBgNVBAMMEEhFTE1TRVJWRVJDRVJUQ0EwHhcNMjEwMTAyMDkwNTQz 5 | WhcNMzAxMjMxMDkwNTQzWjBGMQswCQYDVQQGEwIgIDEKMAgGA1UECAwBIDEKMAgG 6 | A1UEBwwBIDEKMAgGA1UECgwBIDETMBEGA1UEAwwKSEVMTVNFUlZFUjCCAiIwDQYJ 7 | KoZIhvcNAQEBBQADggIPADCCAgoCggIBAMPz8Vr/uPHsmAevay7q/myV4tDrjuIj 8 | xjaIwLTlyngq6tWCFr7zXczYJXYY83rvkTVN0oBMkqev7ZlRy1Q5A8FuTEeKnMuv 9 | kRYEn5I8EF2pUP7rY+Gbn6dS63Jh7+wj2g3HwJ+9RzAnzpvNfh6ZbGxZ2I7ALTWU 10 | KUVpJljkDJO6+HyON7geh6R/mFDsq3v6n+giywr3pp37hqDueKYoyBt7WJ+2D2IH 11 | tlxS5eRDoygpZAiiXhSRoGGZ2oS2jevVhnTkHqzS21zNd11WiXtpe0Yu78+cwiqB 12 | iON7Zy5oWVVF+Og1buLkPslVgxg5BJSbfcs5D6cbVreoJFFM9uaJGhjE1PTlci8C 13 | wYJGgdKULP1bcHpbdnz2G8ZIz0xuOwhlThWHdEzh++Vst/UlpRGRkuLxEC34jsJw 14 | XoRt0WMNeakYQzIbWJtkHCA1CCMrfm+Rzuxh+KfwVHMz9J8fv6thdXQcV/tqMKbN 15 | zzEHi3/2lUJeMvS2bibXeWHfiEOlXzIwTPJs4cwwi9PkhfGXWz6XwgIaMfsS8fOS 16 | l5m5s8vFbEHTh3HUQ1J7njc6AFslv5c/5IYeG+sa5ObWsllYt1pTWVTW9sZsHn8s 17 | kGcjxA3SeH/8tjeAMXbnHIEca7Q9UmTlDilb2BL3TreVdckZUIbIvwmftB+Xh5Mt 18 | XQQhBTZZhnAzAgMBAAGjEzARMA8GA1UdEQQIMAaHBH8AAAEwDQYJKoZIhvcNAQEL 19 | BQADggIBAE1sejJ6SWRzL8udWxfITdqgqlw+Ci8+K2BYwgBd5vJun+us0oommzl6 20 | ogkJyeytdRx1RdnqQi2Gucz4+HS2FJsXsWjNc7S7TCKj7QGt8O6WQQGPxYD6Ypn8 21 | 2q8HHjuPS3aa3w1sID1DxDBjWnDFnz27bF1Fhb7eq51oRJVyXIb8+ip8uoxF0lkR 22 | 1ges0h3SSYRz7gvu3t4dDeUiTyttV0yWDk7yWOEZyUxRqYgKJXmu2nFkeOnyiof5 23 | IyjhdQR4XrR6mWZChJT3WtDNGvTTX1uUfMP35fc4YiENzNUzqGcbBVjaJNl1HzlE 24 | Xzi6tkZFnF+T0I7Av/sIlsCL7L/nqqRDXb7yEGG+bY2N9K1Wwkn2WrdrApwbE32N 25 | jV/3wY6oOtgmDaWoc4FOgzI250Y0SyVaAQlN+LvR7EPLJrGA+/6LaCiOMkaDT/JZ 26 | 0YtIynvjHVL9gThtrWU/BBe8OzN3tXaxJNMdY2OxSyJEYBEyHDBNluIk1pmJcsRA 27 | KQ5p5vS3sBj9kZAeka0W59emv3n08/IS1svAGxlJa+gSFJTBe9XQJcD8f0kSy+8x 28 | 4OAMfFVYrKsQryyS7fRepCTy/niAETp8w/n21cXMCkwMRAFPI9DXx8vEMubDt9dx 29 | NWKOk0Juc2XKIL3Z+IUqUiRopJ7iXyfwsb+8ctcyNcnYT8iaF7f/ 30 | -----END CERTIFICATE----- 31 | -------------------------------------------------------------------------------- /pkg/helm/chart_test.go: -------------------------------------------------------------------------------- 1 | package helm 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "path" 7 | "testing" 8 | ) 9 | 10 | var testTarballPath = "../../testdata/charts/helm2/mychart/mychart-0.1.0.tgz" 11 | 12 | func TestSetVersion(t *testing.T) { 13 | c, err := GetChartByName(testTarballPath) 14 | if err != nil { 15 | t.Error("unexpected error getting test tarball chart", err) 16 | } 17 | c.SetVersion("latest") 18 | if c.Metadata.Version != "latest" { 19 | t.Errorf("expected chart version to be latest, instead got %s", c.Metadata.Version) 20 | } 21 | } 22 | 23 | func TestGetChartByName(t *testing.T) { 24 | // Bad name 25 | _, err := GetChartByName("/non/existant/path/mychart-0.1.0.tgz") 26 | if err == nil { 27 | t.Error("expected error getting chart with bad name, instead got nil") 28 | } 29 | 30 | // Valid name 31 | c, err := GetChartByName(testTarballPath) 32 | if err != nil { 33 | t.Error("unexpected error getting test tarball chart", err) 34 | } 35 | if c.Metadata.Name != "mychart" { 36 | t.Errorf("expexted chart name to be mychart, instead got %s", c.Metadata.Name) 37 | } 38 | if c.Metadata.Version != "0.1.0" { 39 | t.Errorf("expexted chart version to be 0.1.0, instead got %s", c.Metadata.Version) 40 | } 41 | } 42 | 43 | func TestCreateChartPackage(t *testing.T) { 44 | c, err := GetChartByName(testTarballPath) 45 | if err != nil { 46 | t.Error("unexpected error getting test tarball chart", err) 47 | } 48 | 49 | tmp, err := ioutil.TempDir("", "helm-push-test") 50 | if err != nil { 51 | t.Error("unexpected error creating temp test dir", err) 52 | } 53 | defer os.RemoveAll(tmp) 54 | 55 | chartPackagePath, err := CreateChartPackage(c, tmp) 56 | if err != nil { 57 | t.Error("unexpected error creating chart package", err) 58 | } 59 | 60 | expectedPath := path.Join(tmp, "mychart-0.1.0.tgz") 61 | if chartPackagePath != expectedPath { 62 | t.Errorf("expected chart path to be %s, but was %s", expectedPath, chartPackagePath) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /scripts/install_plugin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | # Copied w/ love from the excellent hypnoglow/helm-s3 4 | 5 | if [ -n "${HELM_PUSH_PLUGIN_NO_INSTALL_HOOK}" ]; then 6 | echo "Development mode: not downloading versioned release." 7 | exit 0 8 | fi 9 | 10 | version="$(cat plugin.yaml | grep "version" | cut -d '"' -f 2)" 11 | echo "Downloading and installing helm-push v${version} ..." 12 | 13 | url="" 14 | 15 | # convert architecture of the target system to a compatible GOARCH value. 16 | # Otherwise failes to download of the plugin from github, because the provided 17 | # architecture by `uname -m` is not part of the github release. 18 | arch="" 19 | case $(uname -m) in 20 | x86_64) 21 | arch="amd64" 22 | ;; 23 | armv6*) 24 | arch="armv6" 25 | ;; 26 | # match every arm processor version like armv7h, armv7l and so on. 27 | armv7*) 28 | arch="armv7" 29 | ;; 30 | aarch64 | arm64) 31 | arch="arm64" 32 | ;; 33 | *) 34 | echo "Failed to detect target architecture" 35 | exit 1 36 | ;; 37 | esac 38 | 39 | 40 | if [ "$(uname)" = "Darwin" ]; then 41 | url="https://github.com/chartmuseum/helm-push/releases/download/v${version}/helm-push_${version}_darwin_${arch}.tar.gz" 42 | elif [ "$(uname)" = "Linux" ] ; then 43 | url="https://github.com/chartmuseum/helm-push/releases/download/v${version}/helm-push_${version}_linux_${arch}.tar.gz" 44 | else 45 | url="https://github.com/chartmuseum/helm-push/releases/download/v${version}/helm-push_${version}_windows_${arch}.tar.gz" 46 | fi 47 | 48 | echo $url 49 | 50 | mkdir -p "bin" 51 | mkdir -p "releases/v${version}" 52 | 53 | # Download with curl if possible. 54 | if [ -x "$(which curl 2>/dev/null)" ]; then 55 | curl -sSL "${url}" -o "releases/v${version}.tar.gz" 56 | else 57 | wget -q "${url}" -O "releases/v${version}.tar.gz" 58 | fi 59 | tar xzf "releases/v${version}.tar.gz" -C "releases/v${version}" 60 | mv "releases/v${version}/bin/helm-cm-push" "bin/helm-cm-push" || \ 61 | mv "releases/v${version}/bin/helm-cm-push.exe" "bin/helm-cm-push" 62 | -------------------------------------------------------------------------------- /pkg/chartmuseum/upload.go: -------------------------------------------------------------------------------- 1 | package chartmuseum 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "mime/multipart" 8 | "net/http" 9 | "net/url" 10 | "os" 11 | "path" 12 | "strings" 13 | ) 14 | 15 | // UploadChartPackage uploads a chart package to ChartMuseum (POST /api/charts) 16 | func (client *Client) UploadChartPackage(chartPackagePath string, force bool) (*http.Response, error) { 17 | u, err := url.Parse(client.opts.url) 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | u.Path = path.Join(client.opts.contextPath, "api", strings.TrimPrefix(u.Path, client.opts.contextPath), "charts") 23 | req, err := http.NewRequest("POST", u.String(), nil) 24 | if err != nil { 25 | return nil, err 26 | } 27 | 28 | // Add ?force to request querystring to force an upload if chart version already exists 29 | if force { 30 | req.URL.RawQuery = "force" 31 | } 32 | 33 | err = setUploadChartPackageRequestBody(req, chartPackagePath) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | if client.opts.accessToken != "" { 39 | if client.opts.authHeader != "" { 40 | req.Header.Set(client.opts.authHeader, client.opts.accessToken) 41 | } else { 42 | req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", client.opts.accessToken)) 43 | } 44 | } else if client.opts.username != "" && client.opts.password != "" { 45 | req.SetBasicAuth(client.opts.username, client.opts.password) 46 | } 47 | 48 | return client.Do(req) 49 | } 50 | 51 | func setUploadChartPackageRequestBody(req *http.Request, chartPackagePath string) error { 52 | var body bytes.Buffer 53 | w := multipart.NewWriter(&body) 54 | defer w.Close() 55 | fw, err := w.CreateFormFile("chart", chartPackagePath) 56 | if err != nil { 57 | return err 58 | } 59 | w.FormDataContentType() 60 | fd, err := os.Open(chartPackagePath) 61 | if err != nil { 62 | return err 63 | } 64 | defer fd.Close() 65 | _, err = io.Copy(fw, fd) 66 | if err != nil { 67 | return err 68 | } 69 | req.Header.Set("Content-Type", w.FormDataContentType()) 70 | req.Body = io.NopCloser(&body) 71 | return nil 72 | } 73 | -------------------------------------------------------------------------------- /testdata/tls/client_ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFeTCCA2GgAwIBAgIUR/7hT0+Kju66vSp9UuBk/c0vdoAwDQYJKoZIhvcNAQEL 3 | BQAwTDELMAkGA1UEBhMCICAxCjAIBgNVBAgMASAxCjAIBgNVBAcMASAxCjAIBgNV 4 | BAoMASAxGTAXBgNVBAMMEEhFTE1DTElFTlRDRVJUQ0EwHhcNMjEwMTAyMDkwNTQ1 5 | WhcNMzAxMjMxMDkwNTQ1WjBMMQswCQYDVQQGEwIgIDEKMAgGA1UECAwBIDEKMAgG 6 | A1UEBwwBIDEKMAgGA1UECgwBIDEZMBcGA1UEAwwQSEVMTUNMSUVOVENFUlRDQTCC 7 | AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMzrmoo1onmXMzRCbAzt0j1I 8 | HFWQfW8T7kuBwoo5tO5fHKoWOg6ztnsX4TjXA2q3NXPCUFcuzp3L1xmgwVUm6iBc 9 | TDMAUmbOVFXQWQWUSi9YGMkTvQrjqyO+eY1tfnh6ZxiF29T92XDVAH8O9mHwsfMQ 10 | 8g3Bn6of86o8cchQ2kM1n9kKs58MdWaKlFQAOnKDdDX8C9E39GcWrtcAASDOHAOm 11 | UYMhIAY3fw/yuuSgxDRcuYuN8jp6cNYgNoiMidFrgDdkeky+Y2n1RQMWsT2GoC2S 12 | 6ZV1//7Awnbny8MCdT74LTQ9bN2j/4KfwLTwE5EwCOmf0MrnYeVSK6fIWFGPzJhs 13 | eeGwykTgPazkcCJkSIQGQkgSQON7U1XdsiCj5r4tUDfSXSteAPad0dYvEIwBUkpd 14 | i+MaPE1oC4D6I3ye7LkhfBbzwzx7vYRjDh/fxA6ZH/hRkE+JW7tLEUDM6EXeMhKL 15 | JoUMYVKn5gypbpwItmbVyfhjlh31e+11dz0xk+2iBRbL3t2b67mjvao9LJl+PLP/ 16 | Pk9NbX8bAGtsMFKSyHAumUzy5CDYhK7dVPPdUvKTiH57kExScsIZe0+zuzcT/sx1 17 | z57yb8xPh+IoN8HPJYz6wpFEn00rmUprELUjZTtHgZE7st+GIP+oEj31BgRmrFIy 18 | mLIjgblZxoFTCWFe6trtAgMBAAGjUzBRMB0GA1UdDgQWBBQT8nXy1NvG/E8KvGD/ 19 | aoadNdL+BjAfBgNVHSMEGDAWgBQT8nXy1NvG/E8KvGD/aoadNdL+BjAPBgNVHRMB 20 | Af8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQB8nYSII0pXbhld3pOIarYOiYTo 21 | 5aOIOZaUHxgJk9EqNMkZUDX0EeKnFSwigtERfUza4APCM36+m3m58Rq7hzFCdoit 22 | /sTuH0ez3Cne8H3tnr8aaXZRU+As857PS5xE6v29NwwRBGdSensCEk+IgZxkxjnS 23 | 6p0oSZza0+yrCwQOJJbdSQ/qJHwPdN+oTuW0yMbt83AR1ycUkic5LcXneieaTdUr 24 | PGbzXD5tTC7thl+0h/EtoqfFdAiFDSR4B0lPdOJw64xhDMk952FB2MSFd0HLZNbc 25 | dwYEjCYMqiDXCpXeL4vvPyxN8bzQrCpeSc2hdV+55ejMdaJMBGXNCfdROx8LhoSi 26 | 0G63F1Bex0R5vpUHlCALvldg0B5ZhLJ3t+O8O7rj2fTlhfNqSa6b7tSn5UlkWp9F 27 | /AZxzENo25LVi8/7A8x/Defdl2Dq4JHaEtdHpROfLPPYe0H8zSHTTOnUlrKjNSSK 28 | NIZOETD4hvxKg2ZcZZUyt5tSo31yzFJS8tF8HqE6RK/dt407fM53rTOuY5PEa1f0 29 | /RwVf7b2hjgkTf7+uFeslbBZxdkO1CzE44XBmWaFf4kZFVdl06eQ8uE70MfIkmb5 30 | VDcLz6zf6YE4jm7nUMowZ93/C+OrdUAq6qZOFgcwfrdMr1MJ/z82hCIfTRRSyM57 31 | RfQSytf5T17r5K+yWg== 32 | -----END CERTIFICATE----- 33 | -------------------------------------------------------------------------------- /testdata/tls/server_ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFeTCCA2GgAwIBAgIUEUxeJXnV2hjeuSvl99tONAWFtPEwDQYJKoZIhvcNAQEL 3 | BQAwTDELMAkGA1UEBhMCICAxCjAIBgNVBAgMASAxCjAIBgNVBAcMASAxCjAIBgNV 4 | BAoMASAxGTAXBgNVBAMMEEhFTE1TRVJWRVJDRVJUQ0EwHhcNMjEwMTAyMDkwNTQw 5 | WhcNMzAxMjMxMDkwNTQwWjBMMQswCQYDVQQGEwIgIDEKMAgGA1UECAwBIDEKMAgG 6 | A1UEBwwBIDEKMAgGA1UECgwBIDEZMBcGA1UEAwwQSEVMTVNFUlZFUkNFUlRDQTCC 7 | AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMD+TGmj+RveoUTh3Kjym+Fe 8 | aI5gJIRHj76/bSL0n6oApXeHV7c576NtihDWLq9CPiGAoYo38I4AKO8nH35ifDl5 9 | uPeaqoczbXc7Jr1liLfq7uQ/ZpPK4LvnmkeWJwb/iFZc1lPC1KGR76Sf5NVXDHfp 10 | xuGau6ExCah+ng3NbpvUcQ+fLPJ2OuS/hVkQNO+GqALYxiFObv0VyMSpg+WKh7p7 11 | AioidJPTLQa3a1NK09SYZD4kjAxErIxoX2xTFcsPcS4fuoTgbvLHubT7mOamTD6+ 12 | E1c/1EzcyzLuh2BHqcjkzQhWVMa8VX2mjPGqTdykZYrbsp9sTInqytWxp47dcZhw 13 | fUE7RikEPj33Oj5AJ9DSxaka24tZstraaaxF0VSgegzIPu37c762n2x3luX3Iwsg 14 | 6yodhAYtnl+pVvjmqbOJ7eEfAIPU44cHDvyHmPIBFspmBIP/NqgrD548rgEC5Jrl 15 | X/DKsOJYln7Czf5RumRTlFGea2A3oYBizhcLOMxv0BalpWmpNS14iKwN3h1KXDFK 16 | inPvnknQTveKQ/ZkvayDJoc5l49W2Igz5uM0jwCV7/n8yI/l9N1DRUrMiOdpLWPA 17 | n6G6PC91JG6BoHFOKAzxJru19gq1wi7knZF/3EinqRrUZqt2AUT5EM0MPhx7G7Dl 18 | 7uAsZAMXVNDHbjrEpiZPAgMBAAGjUzBRMB0GA1UdDgQWBBRJ/z9FE6IUNCxTsjCZ 19 | vmM36ruK4TAfBgNVHSMEGDAWgBRJ/z9FE6IUNCxTsjCZvmM36ruK4TAPBgNVHRMB 20 | Af8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQCQzHHn8LAGIRHEng/zFrL39Izv 21 | 8/LOWmPpAX9uJU1sxyafw7MmappBaK6nRdDble9zC66UQKYXOvuXS7O6aIh9MWv6 22 | 42ThvFaNWhNRa2bZyMWRlGzT3bOKw1G8TM4TYRA73jm6gfGADPi0po3vGI9PUlsT 23 | lQ5yuR5Vno7VorZ0kGOAu0skH/7X8UIhhIZONCpWnPMoBsQS+iHgjHjqBF1IkY+3 24 | pPBCe+WPj4NqE3Roz60dabO29PiPEjITrFWyuSFPtyvA9XReJIc758SznheOn0X9 25 | pfzvU9MQeqNwtVx7s7mkcB60TX+w3Ey2auKLulG44gEt1rvbjs/n/isJ8CfAAI23 26 | S0wJvPcvc1iZ5QiftXiYIP7t8TqVKVItt6h+vyHsAkUZv/CGC3SGXhxva//3sbbT 27 | gRiBxci6dNzGO5a0Br1z7MNvaRSMRGqog2szX2Jkx2WhxLtuv+jd8IzJcrmb5X7v 28 | VmSFGdklIxeGG/y95d+VgOl9WURvQi+VuiOIouvG0TG7KNnZkp6jF7vMwp206CjB 29 | f38ReS5TANI1QTZbY5HC/HDl1KIHE9II7KKQJBxLsOlUgoZnGtWJviUeq8JH0pCM 30 | YYGU0aKWrTRqexl/kA8P8R+nmnGys3iVdOUMSp38nOrkJ8GLnDOYVB5Z7E5J8cis 31 | 6IJa1euccl1ArgxBbg== 32 | -----END CERTIFICATE----- 33 | -------------------------------------------------------------------------------- /pkg/chartmuseum/client_test.go: -------------------------------------------------------------------------------- 1 | package chartmuseum 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestNewClient(t *testing.T) { 9 | cmClient, err := NewClient( 10 | URL("http://localhost:8080"), 11 | Username("user"), 12 | Password("pass"), 13 | ContextPath("/my/context/path"), 14 | Timeout(60), 15 | CAFile("../../testdata/tls/server_ca.crt"), 16 | KeyFile("../../testdata/tls/server.key"), 17 | CertFile("../../testdata/tls/server.crt"), 18 | InsecureSkipVerify(true), 19 | ) 20 | 21 | if err != nil { 22 | t.Fatalf("expect creating a client instance but met error: %s", err) 23 | } 24 | 25 | if cmClient.opts.url != "http://localhost:8080" { 26 | t.Errorf("expected url to be http://localhost:8080, got %v", cmClient.opts.url) 27 | } 28 | 29 | if cmClient.opts.username != "user" { 30 | t.Errorf("expected username to be user, got %v", cmClient.opts.username) 31 | } 32 | 33 | if cmClient.opts.password != "pass" { 34 | t.Errorf("expected password to be pass, got %v", cmClient.opts.password) 35 | } 36 | 37 | if cmClient.opts.contextPath != "/my/context/path" { 38 | t.Errorf("expected context path to be /my/context/path, got %v", cmClient.opts.contextPath) 39 | } 40 | 41 | if cmClient.opts.timeout != time.Minute { 42 | t.Errorf("expected timeout duration to be 1 minute, got %v", cmClient.opts.timeout) 43 | } 44 | 45 | if cmClient.opts.caFile != "../../testdata/tls/server_ca.crt" { 46 | t.Errorf("expected ca file path to be '../../testdata/tls/server_ca.crt' but got %v", cmClient.opts.caFile) 47 | } 48 | 49 | if cmClient.opts.certFile != "../../testdata/tls/server.crt" { 50 | t.Errorf("expected cert file path to be '../../testdata/tls/server.crt' but got %v", cmClient.opts.certFile) 51 | } 52 | 53 | if cmClient.opts.keyFile != "../../testdata/tls/server.key" { 54 | t.Errorf("expected key file path to be '../../testdata/tls/server.key' but got %v", cmClient.opts.keyFile) 55 | } 56 | 57 | if !cmClient.opts.insecureSkipVerify { 58 | t.Errorf("expected insecure flag to be 'true' but got %v", cmClient.opts.insecureSkipVerify) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /acceptance_tests/lib/common.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | import time 4 | 5 | NOW = time.strftime('%Y%m%d%H%M%S') 6 | PORT = 28080 7 | HELM_REPO_NAME = 'helm-push-test' 8 | HELM_REPO_URL = 'http://localhost:%d' % PORT 9 | TESTCHARTS_DIR = 'testdata/charts' 10 | ACCEPTANCE_DIR = '.acceptance/' 11 | STORAGE_DIR = os.path.join(ACCEPTANCE_DIR, 'storage/') 12 | LOGFILE = '.chartmuseum.log' 13 | HELM_EXE = 'HELM_HOME=%s helm2' % os.getenv('TEST_HELM_HOME', '') 14 | USE_OPPOSITE_VERSION = False 15 | 16 | 17 | class CommandRunner(object): 18 | def __init__(self): 19 | self.rc = 0 20 | self.pid = 0 21 | self.stdout = '' 22 | self.rootdir = os.path.realpath(os.path.join(__file__, '../../../')) 23 | 24 | def return_code_should_be(self, expected_rc): 25 | if int(expected_rc) != self.rc: 26 | raise AssertionError('Expected return code to be "%s" but was "%s".' 27 | % (expected_rc, self.rc)) 28 | 29 | def return_code_should_not_be(self, expected_rc): 30 | if int(expected_rc) == self.rc: 31 | raise AssertionError('Expected return code not to be "%s".' % expected_rc) 32 | 33 | def output_contains(self, s): 34 | if s not in self.stdout: 35 | raise AssertionError('Output does not contain "%s".' % s) 36 | 37 | def output_does_not_contain(self, s): 38 | if s in self.stdout: 39 | raise AssertionError('Output contains "%s".' % s) 40 | 41 | def run_command(self, command, detach=False): 42 | process = subprocess.Popen(['/bin/bash', '-xc', command], 43 | stdout=subprocess.PIPE, 44 | stderr=subprocess.STDOUT) 45 | if not detach: 46 | stdout = process.communicate()[0].strip().decode() 47 | self.rc = process.returncode 48 | tmp = [] 49 | for x in stdout.split('\n'): 50 | print(x) 51 | if not x.startswith('+ '): # Remove debug lines that start with "+ " 52 | tmp.append(x) 53 | self.stdout = '\n'.join(tmp) 54 | -------------------------------------------------------------------------------- /testdata/charts/helm3/my-v3-chart/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "my-v3-chart.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "my-v3-chart.fullname" -}} 15 | {{- if .Values.fullnameOverride -}} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} 17 | {{- else -}} 18 | {{- $name := default .Chart.Name .Values.nameOverride -}} 19 | {{- if contains $name .Release.Name -}} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" -}} 21 | {{- else -}} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 23 | {{- end -}} 24 | {{- end -}} 25 | {{- end -}} 26 | 27 | {{/* 28 | Create chart name and version as used by the chart label. 29 | */}} 30 | {{- define "my-v3-chart.chart" -}} 31 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} 32 | {{- end -}} 33 | 34 | {{/* 35 | Common labels 36 | */}} 37 | {{- define "my-v3-chart.labels" -}} 38 | helm.sh/chart: {{ include "my-v3-chart.chart" . }} 39 | {{ include "my-v3-chart.selectorLabels" . }} 40 | {{- if .Chart.AppVersion }} 41 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 42 | {{- end }} 43 | app.kubernetes.io/managed-by: {{ .Release.Service }} 44 | {{- end -}} 45 | 46 | {{/* 47 | Selector labels 48 | */}} 49 | {{- define "my-v3-chart.selectorLabels" -}} 50 | app.kubernetes.io/name: {{ include "my-v3-chart.name" . }} 51 | app.kubernetes.io/instance: {{ .Release.Name }} 52 | {{- end -}} 53 | 54 | {{/* 55 | Create the name of the service account to use 56 | */}} 57 | {{- define "my-v3-chart.serviceAccountName" -}} 58 | {{- if .Values.serviceAccount.create -}} 59 | {{ default (include "my-v3-chart.fullname" .) .Values.serviceAccount.name }} 60 | {{- else -}} 61 | {{ default "default" .Values.serviceAccount.name }} 62 | {{- end -}} 63 | {{- end -}} 64 | -------------------------------------------------------------------------------- /acceptance_tests/lib/ChartMuseum.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | 4 | import common 5 | 6 | 7 | class ChartMuseum(common.CommandRunner): 8 | def start_chartmuseum(self): 9 | self.stop_chartmuseum() 10 | os.chdir(self.rootdir) 11 | shutil.rmtree(common.STORAGE_DIR, ignore_errors=True) 12 | cmd = 'chartmuseum --debug --port=%d --storage="local" ' % common.PORT 13 | cmd += '--storage-local-rootdir=%s >> %s 2>&1' % (common.STORAGE_DIR, common.LOGFILE) 14 | print(cmd) 15 | self.run_command(cmd, detach=True) 16 | 17 | def stop_chartmuseum(self): 18 | self.run_command('pkill -9 chartmuseum') 19 | shutil.rmtree(common.STORAGE_DIR, ignore_errors=True) 20 | 21 | def remove_chartmuseum_logs(self): 22 | os.chdir(self.rootdir) 23 | self.run_command('rm -f %s' % common.LOGFILE) 24 | 25 | def print_chartmuseum_logs(self): 26 | os.chdir(self.rootdir) 27 | self.run_command('cat %s' % common.LOGFILE) 28 | 29 | def package_exists_in_chartmuseum_storage(self, find=''): 30 | self.run_command('find %s -maxdepth 1 -name "*%s.tgz" | grep tgz' % (common.STORAGE_DIR, find)) 31 | 32 | def package_contains_expected_files(self): 33 | # Check for requirements.yaml in Helm 2 (a Helm 2-specific file) 34 | checkRequirementsYamlCmd = '(cd %s && mkdir -p tmp && tar -xf *.tgz --directory tmp && find tmp -name requirements.yaml | grep requirements.yaml)' % common.STORAGE_DIR 35 | 36 | # Check for values.schema.json in Helm 3 (a Helm 3-specific file) 37 | checkValuesSchemaJsonCmd = '(cd %s && mkdir -p tmp && tar -xf *.tgz --directory tmp && find tmp -name values.schema.json | grep values.schema.json)' % common.STORAGE_DIR 38 | 39 | if common.USE_OPPOSITE_VERSION: 40 | if 'helm3' in common.HELM_EXE: 41 | self.run_command(checkRequirementsYamlCmd) 42 | else: 43 | self.run_command(checkValuesSchemaJsonCmd) 44 | else: 45 | if 'helm3' in common.HELM_EXE: 46 | self.run_command(checkValuesSchemaJsonCmd) 47 | else: 48 | self.run_command(checkRequirementsYamlCmd) 49 | 50 | def clear_chartmuseum_storage(self): 51 | self.run_command('rm %s*.tgz' % common.STORAGE_DIR) 52 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PLUGIN_NAME := cm-push 2 | 3 | HAS_PIP := $(shell command -v pip3;) 4 | HAS_VENV := $(shell command -v virtualenv;) 5 | 6 | .PHONY: build 7 | build: build_linux build_mac build_windows 8 | 9 | build_windows: export GOARCH=amd64 10 | build_windows: export GO111MODULE=on 11 | build_windows: 12 | @GOOS=windows go build -v --ldflags="-w -X main.Version=$(VERSION) -X main.Revision=$(REVISION)" \ 13 | -o bin/windows/amd64/helm-cm-push cmd/helm-cm-push/main.go # windows 14 | 15 | link_windows: 16 | @cp bin/windows/amd64/helm-cm-push ./bin/helm-cm-push 17 | 18 | build_linux: export GOARCH=amd64 19 | build_linux: export CGO_ENABLED=0 20 | build_linux: export GO111MODULE=on 21 | build_linux: 22 | @GOOS=linux go build -v --ldflags="-w -X main.Version=$(VERSION) -X main.Revision=$(REVISION)" \ 23 | -o bin/linux/amd64/helm-cm-push cmd/helm-cm-push/main.go # linux 24 | 25 | link_linux: 26 | @cp bin/linux/amd64/helm-cm-push ./bin/helm-cm-push 27 | 28 | build_mac: export GOARCH=amd64 29 | build_mac: export CGO_ENABLED=0 30 | build_mac: export GO111MODULE=on 31 | build_mac: 32 | @GOOS=darwin go build -v --ldflags="-w -X main.Version=$(VERSION) -X main.Revision=$(REVISION)" \ 33 | -o bin/darwin/amd64/helm-cm-push cmd/helm-cm-push/main.go # mac osx 34 | @cp bin/darwin/amd64/helm-cm-push ./bin/helm-cm-push # For use w make install 35 | 36 | link_mac: 37 | @cp bin/darwin/amd64/helm-cm-push ./bin/helm-cm-push 38 | 39 | .PHONY: clean 40 | clean: 41 | @git status --ignored --short | grep '^!! ' | sed 's/!! //' | xargs rm -rf 42 | 43 | .PHONY: test 44 | test: setup-test-environment 45 | @./scripts/test.sh 46 | 47 | .PHONY: covhtml 48 | covhtml: 49 | @go tool cover -html=.cover/cover.out 50 | 51 | .PHONY: tree 52 | tree: 53 | @tree -I vendor 54 | 55 | .PHONY: release 56 | release: 57 | @scripts/release.sh $(VERSION) 58 | 59 | .PHONY: install 60 | install: 61 | HELM_PUSH_PLUGIN_NO_INSTALL_HOOK=1 helm plugin install $(shell pwd) 62 | 63 | .PHONY: remove 64 | remove: 65 | helm plugin remove $(PLUGIN_NAME) 66 | 67 | .PHONY: setup-test-environment 68 | setup-test-environment: 69 | ifndef HAS_PIP 70 | @apt-get update && apt-get install -y python-pip 71 | endif 72 | ifndef HAS_VENV 73 | @pip install virtualenv 74 | endif 75 | @./scripts/setup_test_environment.sh 76 | 77 | .PHONY: acceptance 78 | acceptance: setup-test-environment 79 | @./scripts/acceptance.sh 80 | -------------------------------------------------------------------------------- /pkg/helm/repo_test.go: -------------------------------------------------------------------------------- 1 | package helm 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "testing" 7 | 8 | "k8s.io/helm/pkg/getter" 9 | helm_env "k8s.io/helm/pkg/helm/environment" 10 | "k8s.io/helm/pkg/helm/helmpath" 11 | "k8s.io/helm/pkg/repo" 12 | ) 13 | 14 | var ( 15 | settings helm_env.EnvSettings 16 | ) 17 | 18 | func TestGetRepoByName(t *testing.T) { 19 | // Non-existant repo 20 | _, err := GetRepoByName("nonexistantrepo") 21 | if err == nil { 22 | t.Error("expecting error with bad repo name, instead got nil") 23 | } 24 | 25 | // Create new Helm home w/ test repo 26 | tmp, err := ioutil.TempDir("", "helm-push-test") 27 | if err != nil { 28 | t.Error("unexpected error creating temp test dir", err) 29 | } 30 | defer os.RemoveAll(tmp) 31 | 32 | home := helmpath.Home(tmp) 33 | f := repo.NewRepoFile() 34 | 35 | entry := repo.Entry{} 36 | entry.Name = "helm-push-test" 37 | entry.URL = "http://localhost:8080" 38 | 39 | _, err = repo.NewChartRepository(&entry, getter.All(settings)) 40 | if err != nil { 41 | t.Error("unexpected error created test repository", err) 42 | } 43 | 44 | f.Update(&entry) 45 | os.MkdirAll(home.Repository(), 0777) 46 | f.WriteFile(home.RepositoryFile(), 0644) 47 | 48 | os.Setenv("HELM_HOME", home.String()) 49 | 50 | // Retrieve test repo 51 | _, err = GetRepoByName("helm-push-test") 52 | if err != nil { 53 | t.Error("unexpected error getting test repo", err) 54 | } 55 | 56 | // Err, missing repofile 57 | os.RemoveAll(tmp) 58 | _, err = GetRepoByName("helm-push-test") 59 | if err == nil { 60 | t.Error("expecting error getting test repo after removed, instead got nil") 61 | } 62 | 63 | } 64 | 65 | func TestTempRepoFromURL(t *testing.T) { 66 | url := "https://my.chart.repo.com" 67 | repo, err := TempRepoFromURL(url) 68 | if err != nil { 69 | t.Error("unexpected error getting temp repo from URL", err) 70 | } 71 | if repo.Config.URL != url { 72 | t.Error("expecting repo URL to match what was provided") 73 | } 74 | 75 | url = "https://user:p@ss@my.chart.repo.com/a/b/c/" 76 | repo, err = TempRepoFromURL(url) 77 | if err != nil { 78 | t.Error("unexpected error getting temp repo from URL, with basic auth", err) 79 | } 80 | if repo.Config.URL != "https://my.chart.repo.com/a/b/c/" { 81 | t.Error("expecting repo URL to have basic auth removed") 82 | } 83 | if repo.Config.Username != "user" { 84 | t.Error("expecting repo username to be extracted from URL") 85 | } 86 | if repo.Config.Password != "p@ss" { 87 | t.Error("expecting repo password to be extracted from URL") 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /BUILDING.md: -------------------------------------------------------------------------------- 1 | # Building 2 | 3 | ## TLS Certificates for Testing purposes 4 | 5 | We use some test / dummy TLS certificates when running test servers and clients. 6 | Below is how we generate these certificates 7 | 8 | `server-csr.conf` 9 | 10 | ```bash 11 | # server-csr.conf 12 | cat > server-csr.conf < client-csr.conf < 3 | 4 | [![GitHub Actions status](https://github.com/chartmuseum/helm-push/workflows/build/badge.svg)](https://github.com/chartmuseum/helm-push/actions?query=workflow%3Abuild) 5 | 6 | Helm plugin to push chart package to [ChartMuseum](https://github.com/helm/chartmuseum) 7 | 8 | ## Install 9 | Based on the version in `plugin.yaml`, release binary will be downloaded from GitHub: 10 | 11 | ``` 12 | $ helm plugin install https://github.com/chartmuseum/helm-push 13 | Downloading and installing helm-push v0.10.1 ... 14 | https://github.com/chartmuseum/helm-push/releases/download/v0.10.1/helm-push_0.10.1_darwin_amd64.tar.gz 15 | Installed plugin: cm-push 16 | ``` 17 | 18 | ## Usage 19 | Start by adding a ChartMuseum-backed repo via Helm CLI (if not already added) 20 | ``` 21 | $ helm repo add chartmuseum http://localhost:8080 22 | ``` 23 | For all available plugin options, please run 24 | ``` 25 | $ helm cm-push --help 26 | ``` 27 | 28 | ### Pushing a directory 29 | Point to a directory containing a valid `Chart.yaml` and the chart will be packaged and uploaded: 30 | ``` 31 | $ cat mychart/Chart.yaml 32 | name: mychart 33 | version: 0.3.2 34 | ``` 35 | ``` 36 | $ helm cm-push mychart/ chartmuseum 37 | Pushing mychart-0.3.2.tgz to chartmuseum... 38 | Done. 39 | ``` 40 | 41 | ### Pushing with a custom version 42 | The `--version` flag can be provided, which will push the package with a custom version: 43 | 44 | ``` 45 | $ helm cm-push mychart/ --version="1.2.3" chartmuseum 46 | Pushing mychart-1.2.3.tgz to chartmuseum... 47 | Done. 48 | ``` 49 | 50 | If you want to enable something like `--version="9.9.9-dev1"`, which you intend to push regularly, you will need to run your ChartMuseum server with `ALLOW_OVERWRITE=true`. 51 | 52 | ### Push .tgz package 53 | This workflow does not require the use of `helm package`, but pushing .tgzs is still supported: 54 | ``` 55 | $ helm cm-push mychart-0.3.2.tgz chartmuseum 56 | Pushing mychart-0.3.2.tgz to chartmuseum... 57 | Done. 58 | ``` 59 | 60 | ### Force push 61 | If your ChartMuseum install is configured with `ALLOW_OVERWRITE=true`, chart versions will be automatically overwritten upon re-upload. 62 | 63 | Otherwise, unless your install is configured with `DISABLE_FORCE_OVERWRITE=true` (ChartMuseum > v0.7.1), you can use the `--force`/`-f` option to to force an upload: 64 | ``` 65 | $ helm cm-push --force mychart-0.3.2.tgz chartmuseum 66 | Pushing mychart-0.3.2.tgz to chartmuseum... 67 | Done. 68 | ``` 69 | 70 | ### Pushing directly to URL 71 | If the second argument provided resembles a URL, you are not required to add the repo prior to push: 72 | ``` 73 | $ helm cm-push mychart-0.3.2.tgz http://localhost:8080 74 | Pushing mychart-0.3.2.tgz to http://localhost:8080... 75 | Done. 76 | ``` 77 | 78 | ## Context Path 79 | 80 | If you are running ChartMuseum behind a proxy that adds a route prefix, for example: 81 | ``` 82 | https://my.chart.repo.com/helm/v1/index.yaml -> http://chartmuseum-svc/index.yaml 83 | ``` 84 | 85 | You can use the `--context-path=` option or `HELM_REPO_CONTEXT_PATH` env var in order for the plugin to construct the upload URL correctly: 86 | ``` 87 | helm repo add chartmuseum https://my.chart.repo.com/helm/v1 88 | helm cm-push --context-path=/helm/v1 mychart-0.3.2.tgz chartmuseum 89 | ``` 90 | 91 | Alternatively, you can add `serverInfo.contextPath` to your index.yaml: 92 | ``` 93 | apiVersion: v1 94 | entries:{} 95 | generated: "2018-08-09T11:08:21-05:00" 96 | serverInfo: 97 | contextPath: /helm/v1 98 | ``` 99 | 100 | In ChartMuseum server (>0.7.1) this will automatically be added to index.yaml if the `--context-path` option is provided. 101 | 102 | ## Authentication 103 | ### Basic Auth 104 | If you have added your repo with the `--username`/`--password` flags (Helm 2.9+), or have added your repo with the basic auth username/password in the URL (e.g. `https://myuser:mypass@my.chart.repo.com`), no further setup is required. 105 | 106 | The plugin will use the auth info located in `~/.helm/repository/repositories.yaml` (for Helm 2) or `~/.config/helm/repositories.yaml` (for Helm 3) in order to authenticate. 107 | 108 | If you are running ChartMuseum with `AUTH_ANONYMOUS_GET=true`, and have added your repo without authentication, the plugin recognizes the following environment variables for basic auth on push operations: 109 | ``` 110 | $ export HELM_REPO_USERNAME="myuser" 111 | $ export HELM_REPO_PASSWORD="mypass" 112 | ``` 113 | 114 | With this setup, you can enable people to use your repo for installing charts etc. without allowing them to upload to it. 115 | 116 | ### Token 117 | 118 | *ChartMuseum token-auth is currently in progress. Please see [auth-server-example](https://github.com/chartmuseum/auth-server-example) for more info.* 119 | 120 | Although ChartMuseum server does not define or accept a token format (yet), if you are running it behind a proxy that accepts access tokens, you can provide the following env var: 121 | ``` 122 | $ export HELM_REPO_ACCESS_TOKEN="" 123 | ``` 124 | 125 | This will result in all basic auth options above being ignored, and the plugin will send the token in the header: 126 | ``` 127 | Authorization: Bearer 128 | ``` 129 | 130 | If you require a custom header to be used for passing the token, you can the following env var: 131 | ``` 132 | $ export HELM_REPO_AUTH_HEADER="" 133 | ``` 134 | 135 | This will then be used in place of `Authorization: Bearer`: 136 | ``` 137 | : 138 | ``` 139 | 140 | #### Token config file (~/.cfconfig) 141 | For users of [Managed Helm Repositories](https://codefresh.io/codefresh-news/introducing-managed-helm-repositories/) (Codefresh), the plugin is able to auto-detect your API key from `~/.cfconfig`. This file is managed by [Codefresh CLI](https://codefresh-io.github.io/cli/). 142 | 143 | If detected, this API key will be used for token-based auth, overriding basic auth options described above. 144 | 145 | The format of this file is the following: 146 | 147 | ``` 148 | contexts: 149 | default: 150 | name: default 151 | token: 152 | current-context: default 153 | ``` 154 | 155 | ### TLS Client Cert Auth 156 | 157 | ChartMuseum server does not yet have options to setup TLS client cert authentication (please see [chartmuseum#79](https://github.com/helm/chartmuseum/issues/79)). 158 | 159 | If you are running ChartMuseum behind a frontend that does, the following options are available: 160 | 161 | ``` 162 | --ca-file string Verify certificates of HTTPS-enabled servers using this CA bundle [$HELM_REPO_CA_FILE] 163 | --cert-file string Identify HTTPS client using this SSL certificate file [$HELM_REPO_CERT_FILE] 164 | --key-file string Identify HTTPS client using this SSL key file [$HELM_REPO_KEY_FILE] 165 | --insecure Connect to server with an insecure way by skipping certificate verification [$HELM_REPO_INSECURE] 166 | ``` 167 | 168 | ## Custom Downloader 169 | This plugin also defines the `cm://` protocol that you may specify when adding a repo: 170 | ``` 171 | $ helm repo add chartmuseum cm://my.chart.repo.com 172 | ``` 173 | 174 | The only real difference with this vs. simply using http/https, is that the environment variables above are recognized by the plugin and used to set the `Authorization` header appropriately. As in, if you do not add your repo in this way, you are unable to use token-based auth for GET requests (downloading index.yaml, chart .tgzs, etc). 175 | 176 | By default, `cm://` translates to `https://`. If you must use `http://`, you can set the following env var: 177 | ``` 178 | $ export HELM_REPO_USE_HTTP="true" 179 | ``` 180 | -------------------------------------------------------------------------------- /pkg/chartmuseum/upload_test.go: -------------------------------------------------------------------------------- 1 | package chartmuseum 2 | 3 | import ( 4 | "crypto/rand" 5 | "crypto/tls" 6 | "encoding/base64" 7 | "net/http" 8 | "net/http/httptest" 9 | "strings" 10 | "testing" 11 | 12 | "k8s.io/helm/pkg/tlsutil" 13 | ) 14 | 15 | var ( 16 | testTarballPath = "../../testdata/charts/helm2/mychart/mychart-0.1.0.tgz" 17 | testServerCertPath = "../../testdata/tls/server.crt" 18 | testServerKeyPath = "../../testdata/tls/server.key" 19 | testServerCAPath = "../../testdata/tls/server_ca.crt" 20 | testClientCAPath = "../../testdata/tls/client_ca.crt" 21 | testClientCertPath = "../../testdata/tls/client.crt" 22 | testClientKeyPath = "../../testdata/tls/client.key" 23 | ) 24 | 25 | func TestUploadChartPackage(t *testing.T) { 26 | chartUploaded := false 27 | 28 | basicAuthHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte("user:pass")) 29 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 30 | if !strings.HasPrefix(r.URL.String(), "/my/context/path") { 31 | w.WriteHeader(404) 32 | } else if r.Header.Get("Authorization") != basicAuthHeader { 33 | w.WriteHeader(401) 34 | } else if chartUploaded { 35 | if _, ok := r.URL.Query()["force"]; ok { 36 | w.WriteHeader(201) 37 | } else { 38 | w.WriteHeader(409) 39 | } 40 | } else { 41 | chartUploaded = true 42 | w.WriteHeader(201) 43 | } 44 | })) 45 | defer ts.Close() 46 | 47 | // Happy path 48 | cmClient, err := NewClient( 49 | URL(ts.URL), 50 | Username("user"), 51 | Password("pass"), 52 | ContextPath("/my/context/path"), 53 | ) 54 | if err != nil { 55 | t.Fatalf("[happy path] expect creating a client instance but met error: %s", err) 56 | } 57 | resp, err := cmClient.UploadChartPackage(testTarballPath, false) 58 | if err != nil { 59 | t.Error("error uploading chart package", err) 60 | } 61 | if resp.StatusCode != 201 { 62 | t.Errorf("expecting 201 instead got %d", resp.StatusCode) 63 | } 64 | 65 | // Attempt to re-upload without force, trigger 409 66 | resp, err = cmClient.UploadChartPackage(testTarballPath, false) 67 | if err != nil { 68 | t.Error("error uploading chart package", err) 69 | } 70 | if resp.StatusCode != 409 { 71 | t.Errorf("expecting 409 instead got %d", resp.StatusCode) 72 | } 73 | 74 | // Upload with force 75 | resp, err = cmClient.UploadChartPackage(testTarballPath, true) 76 | if err != nil { 77 | t.Error("error uploading chart package", err) 78 | } 79 | if resp.StatusCode != 201 { 80 | t.Errorf("expecting 201 instead got %d", resp.StatusCode) 81 | } 82 | 83 | // Bad package path 84 | resp, err = cmClient.UploadChartPackage("/non/existant/path/mychart-0.1.0.tgz", false) 85 | if err == nil { 86 | t.Error("expecting error with bad package path, instead got nil") 87 | } 88 | 89 | // Bad URL 90 | cmClient, _ = NewClient(URL("jaswehfgew")) 91 | _, err = cmClient.UploadChartPackage(testTarballPath, false) 92 | if err == nil { 93 | t.Error("[bad URL] expecting error with bad package path, instead got nil") 94 | } 95 | 96 | // Bad context path 97 | cmClient, err = NewClient( 98 | URL(ts.URL), 99 | Username("user"), 100 | Password("pass"), 101 | ContextPath("/my/crappy/context/path"), 102 | Timeout(5), 103 | ) 104 | if err != nil { 105 | t.Fatalf("[bad context path] expect creating a client instance but met error: %s", err) 106 | } 107 | 108 | resp, err = cmClient.UploadChartPackage(testTarballPath, false) 109 | if err != nil { 110 | t.Error("unexpected error with bad context path", err) 111 | } 112 | if resp.StatusCode != 404 { 113 | t.Errorf("expecting 404 instead got %d", resp.StatusCode) 114 | } 115 | 116 | // Unauthorized, invalid user/pass combo (basic auth) 117 | cmClient, err = NewClient( 118 | URL(ts.URL), 119 | Username("baduser"), 120 | Password("badpass"), 121 | ContextPath("/my/context/path"), 122 | ) 123 | if err != nil { 124 | t.Fatalf("[unauthorized: invalid user/pass] expect creating a client instance but met error: %s", err) 125 | } 126 | resp, err = cmClient.UploadChartPackage(testTarballPath, false) 127 | if err != nil { 128 | t.Error("unexpected error with invalid user/pass combo (basic auth)", err) 129 | } 130 | if resp.StatusCode != 401 { 131 | t.Errorf("expecting 401 instead got %d", resp.StatusCode) 132 | } 133 | 134 | // Unauthorized, missing user/pass combo (basic auth) 135 | cmClient, err = NewClient( 136 | URL(ts.URL), 137 | ContextPath("/my/context/path"), 138 | ) 139 | if err != nil { 140 | t.Fatalf("[unauthorized: missing user/pass] expect creating a client instance but met error: %s", err) 141 | } 142 | resp, err = cmClient.UploadChartPackage(testTarballPath, false) 143 | if err != nil { 144 | t.Error("unexpected error with missing user/pass combo (basic auth)", err) 145 | } 146 | if resp.StatusCode != 401 { 147 | t.Errorf("expecting 401 instead got %d", resp.StatusCode) 148 | } 149 | } 150 | 151 | func TestUploadChartPackageWithTlsServer(t *testing.T) { 152 | basicAuthHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte("user:pass")) 153 | ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 154 | if !strings.HasPrefix(r.URL.String(), "/my/context/path") { 155 | w.WriteHeader(404) 156 | } else if r.Header.Get("Authorization") != basicAuthHeader { 157 | w.WriteHeader(401) 158 | } else { 159 | w.WriteHeader(201) 160 | } 161 | })) 162 | 163 | cert, err := tls.LoadX509KeyPair(testServerCertPath, testServerKeyPath) 164 | if err != nil { 165 | t.Fatalf("failed to load certificate and key with error: %s", err.Error()) 166 | } 167 | 168 | ts.TLS = &tls.Config{ 169 | Certificates: []tls.Certificate{cert}, 170 | } 171 | ts.StartTLS() 172 | defer ts.Close() 173 | 174 | //Without certificate 175 | cmClient, err := NewClient( 176 | URL(ts.URL), 177 | Username("user"), 178 | Password("pass"), 179 | ContextPath("/my/context/path"), 180 | ) 181 | if err != nil { 182 | t.Fatalf("[without certificate] expect creating a client instance but met error: %s", err) 183 | } 184 | 185 | _, err = cmClient.UploadChartPackage(testTarballPath, false) 186 | if err == nil { 187 | t.Fatal("expected error returned when uploading package without cert to tls enabled https server") 188 | } 189 | 190 | //Enable insecure flag 191 | cmClient, err = NewClient( 192 | URL(ts.URL), 193 | Username("user"), 194 | Password("pass"), 195 | ContextPath("/my/context/path"), 196 | InsecureSkipVerify(true), 197 | ) 198 | if err != nil { 199 | t.Fatalf("[enable insecure flag] expect creating a client instance but met error: %s", err) 200 | } 201 | 202 | resp, err := cmClient.UploadChartPackage(testTarballPath, false) 203 | if err != nil { 204 | t.Fatalf("[enable insecure flag] expected nil error but got %s", err.Error()) 205 | } 206 | if resp.StatusCode != http.StatusCreated { 207 | t.Fatalf("[enable insecure flag] expect status code 201 but got %d", resp.StatusCode) 208 | } 209 | 210 | //Upload with ca file 211 | cmClient, err = NewClient( 212 | URL(ts.URL), 213 | Username("user"), 214 | Password("pass"), 215 | ContextPath("/my/context/path"), 216 | CAFile(testServerCAPath), 217 | ) 218 | if err != nil { 219 | t.Fatalf("[upload with ca file] expect creating a client instance but met error: %s", err) 220 | } 221 | 222 | resp, err = cmClient.UploadChartPackage(testTarballPath, false) 223 | if err != nil { 224 | t.Fatalf("[upload with ca file] expected nil error but got %s", err.Error()) 225 | } 226 | if resp.StatusCode != http.StatusCreated { 227 | t.Fatalf("[upload with ca file] expect status code 201 but got %d", resp.StatusCode) 228 | } 229 | } 230 | 231 | func TestUploadChartPackageWithVerifyingClientCert(t *testing.T) { 232 | basicAuthHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte("user:pass")) 233 | ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 234 | if !strings.HasPrefix(r.URL.String(), "/my/context/path") { 235 | w.WriteHeader(404) 236 | } else if r.Header.Get("Authorization") != basicAuthHeader { 237 | w.WriteHeader(401) 238 | } else { 239 | w.WriteHeader(201) 240 | } 241 | })) 242 | 243 | cert, err := tls.LoadX509KeyPair(testServerCertPath, testServerKeyPath) 244 | if err != nil { 245 | t.Fatalf("failed to load certificate and key with error: %s", err.Error()) 246 | } 247 | 248 | clientCaCertPool, err := tlsutil.CertPoolFromFile(testClientCAPath) 249 | if err != nil { 250 | t.Fatalf("load server CA file failed with error: %s", err.Error()) 251 | } 252 | 253 | ts.TLS = &tls.Config{ 254 | ClientCAs: clientCaCertPool, 255 | ClientAuth: tls.RequireAndVerifyClientCert, 256 | Certificates: []tls.Certificate{cert}, 257 | Rand: rand.Reader, 258 | } 259 | ts.StartTLS() 260 | defer ts.Close() 261 | 262 | //Upload with cert and key files 263 | cmClient, err := NewClient( 264 | URL(ts.URL), 265 | Username("user"), 266 | Password("pass"), 267 | ContextPath("/my/context/path"), 268 | KeyFile(testClientKeyPath), 269 | CertFile(testClientCertPath), 270 | CAFile(testServerCAPath), 271 | ) 272 | if err != nil { 273 | t.Fatalf("[upload with cert and key files] expect creating a client instance but met error: %s", err) 274 | } 275 | 276 | resp, err := cmClient.UploadChartPackage(testTarballPath, false) 277 | if err != nil { 278 | t.Fatalf("[upload with cert and key files] expected nil error but got %s", err.Error()) 279 | } 280 | if resp.StatusCode != http.StatusCreated { 281 | t.Fatalf("[upload with cert and key files] expect status code 201 but got %d", resp.StatusCode) 282 | } 283 | } 284 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /cmd/helm-cm-push/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "net/http" 9 | "net/url" 10 | "os" 11 | "os/user" 12 | "path" 13 | "path/filepath" 14 | "regexp" 15 | "strconv" 16 | "strings" 17 | 18 | cm "github.com/chartmuseum/helm-push/pkg/chartmuseum" 19 | "github.com/chartmuseum/helm-push/pkg/helm" 20 | "github.com/ghodss/yaml" 21 | "github.com/spf13/cobra" 22 | "helm.sh/helm/v3/pkg/chartutil" 23 | "helm.sh/helm/v3/pkg/cli" 24 | "helm.sh/helm/v3/pkg/downloader" 25 | "helm.sh/helm/v3/pkg/getter" 26 | v2downloader "k8s.io/helm/pkg/downloader" 27 | v2getter "k8s.io/helm/pkg/getter" 28 | v2environment "k8s.io/helm/pkg/helm/environment" 29 | ) 30 | 31 | type ( 32 | pushCmd struct { 33 | chartName string 34 | appVersion string 35 | chartVersion string 36 | repoName string 37 | username string 38 | password string 39 | accessToken string 40 | authHeader string 41 | contextPath string 42 | forceUpload bool 43 | useHTTP bool 44 | checkHelmVersion bool 45 | caFile string 46 | certFile string 47 | keyFile string 48 | insecureSkipVerify bool 49 | keyring string 50 | dependencyUpdate bool 51 | out io.Writer 52 | timeout int64 53 | } 54 | 55 | config struct { 56 | CurrentContext string `json:"current-context"` 57 | Contexts map[string]context `json:"contexts"` 58 | } 59 | 60 | context struct { 61 | Name string `json:"name"` 62 | Token string `json:"token"` 63 | } 64 | ) 65 | 66 | var ( 67 | v2settings v2environment.EnvSettings 68 | settings = cli.New() 69 | globalUsage = `Helm plugin to push chart package to ChartMuseum 70 | 71 | Examples: 72 | 73 | $ helm cm-push mychart-0.1.0.tgz chartmuseum # push .tgz from "helm package" 74 | $ helm cm-push . chartmuseum # package and push chart directory 75 | $ helm cm-push . --version="1.2.3" chartmuseum # override version in Chart.yaml 76 | $ helm cm-push . https://my.chart.repo.com # push directly to chart repo URL 77 | ` 78 | ) 79 | 80 | func newPushCmd(args []string) *cobra.Command { 81 | p := &pushCmd{} 82 | cmd := &cobra.Command{ 83 | Use: "helm cm-push", 84 | Short: "Helm plugin to push chart package to ChartMuseum", 85 | Long: globalUsage, 86 | SilenceUsage: false, 87 | RunE: func(cmd *cobra.Command, args []string) error { 88 | // If the --check-helm-version flag is provided, short circuit 89 | if p.checkHelmVersion { 90 | fmt.Println(helm.HelmMajorVersionCurrent()) 91 | return nil 92 | } 93 | 94 | p.out = cmd.OutOrStdout() 95 | 96 | // If there are 4 args, this is likely being used as a downloader for cm:// protocol 97 | if len(args) == 4 && strings.HasPrefix(args[3], "cm://") { 98 | p.setFieldsFromEnv() 99 | return p.download(args[3]) 100 | } 101 | 102 | if len(args) != 2 { 103 | return errors.New("this command needs 2 arguments: name of chart, name of chart repository (or repo URL)") 104 | } 105 | p.chartName = args[0] 106 | p.repoName = args[1] 107 | p.setFieldsFromEnv() 108 | return p.push() 109 | }, 110 | } 111 | f := cmd.Flags() 112 | f.StringVarP(&p.chartVersion, "version", "v", "", "Override chart version pre-push") 113 | f.StringVarP(&p.appVersion, "app-version", "a", "", "Override app version pre-push") 114 | f.StringVarP(&p.username, "username", "u", "", "Override HTTP basic auth username [$HELM_REPO_USERNAME]") 115 | f.StringVarP(&p.password, "password", "p", "", "Override HTTP basic auth password [$HELM_REPO_PASSWORD]") 116 | f.StringVarP(&p.accessToken, "access-token", "", "", "Send token in Authorization header [$HELM_REPO_ACCESS_TOKEN]") 117 | f.StringVarP(&p.authHeader, "auth-header", "", "", "Alternative header to use for token auth [$HELM_REPO_AUTH_HEADER]") 118 | f.StringVarP(&p.contextPath, "context-path", "", "", "ChartMuseum context path [$HELM_REPO_CONTEXT_PATH]") 119 | f.StringVarP(&p.caFile, "ca-file", "", "", "Verify certificates of HTTPS-enabled servers using this CA bundle [$HELM_REPO_CA_FILE]") 120 | f.StringVarP(&p.certFile, "cert-file", "", "", "Identify HTTPS client using this SSL certificate file [$HELM_REPO_CERT_FILE]") 121 | f.StringVarP(&p.keyFile, "key-file", "", "", "Identify HTTPS client using this SSL key file [$HELM_REPO_KEY_FILE]") 122 | f.StringVar(&p.keyring, "keyring", defaultKeyring(), "location of a public keyring") 123 | f.BoolVarP(&p.insecureSkipVerify, "insecure", "", false, "Connect to server with an insecure way by skipping certificate verification [$HELM_REPO_INSECURE]") 124 | f.BoolVarP(&p.forceUpload, "force", "f", false, "Force upload even if chart version exists") 125 | f.BoolVarP(&p.dependencyUpdate, "dependency-update", "d", false, `update dependencies from "requirements.yaml" to dir "charts/" before packaging`) 126 | f.BoolVarP(&p.checkHelmVersion, "check-helm-version", "", false, `outputs either "2" or "3" indicating the current Helm major version`) 127 | f.Int64VarP(&p.timeout, "timeout", "t", 30, "The duration (in seconds) Helm will wait to get response from chartmuseum") 128 | // fallback to cobra's built-in help , we don't need to handle this flag 129 | f.BoolP("help", "h", false, "") 130 | err := f.Parse(args) 131 | if err != nil { 132 | fmt.Fprintf(os.Stderr, "cannot parse flags: %s\n", err) 133 | // still return other flags , do not panic the following code 134 | return cmd 135 | } 136 | 137 | v2settings.AddFlags(f) 138 | v2settings.Init(f) 139 | 140 | return cmd 141 | } 142 | 143 | func (p *pushCmd) setFieldsFromEnv() { 144 | if v, ok := os.LookupEnv("HELM_REPO_USERNAME"); ok && p.username == "" { 145 | p.username = v 146 | } 147 | if v, ok := os.LookupEnv("HELM_REPO_PASSWORD"); ok && p.password == "" { 148 | p.password = v 149 | } 150 | if v, ok := os.LookupEnv("HELM_REPO_ACCESS_TOKEN"); ok && p.accessToken == "" { 151 | p.accessToken = v 152 | } 153 | if v, ok := os.LookupEnv("HELM_REPO_AUTH_HEADER"); ok && p.authHeader == "" { 154 | p.authHeader = v 155 | } 156 | if v, ok := os.LookupEnv("HELM_REPO_CONTEXT_PATH"); ok && p.contextPath == "" { 157 | p.contextPath = v 158 | } 159 | if v, ok := os.LookupEnv("HELM_REPO_USE_HTTP"); ok { 160 | p.useHTTP, _ = strconv.ParseBool(v) 161 | } 162 | if v, ok := os.LookupEnv("HELM_REPO_CA_FILE"); ok && p.caFile == "" { 163 | p.caFile = v 164 | } 165 | if v, ok := os.LookupEnv("HELM_REPO_CERT_FILE"); ok && p.certFile == "" { 166 | p.certFile = v 167 | } 168 | if v, ok := os.LookupEnv("HELM_REPO_KEY_FILE"); ok && p.keyFile == "" { 169 | p.keyFile = v 170 | } 171 | if v, ok := os.LookupEnv("HELM_REPO_INSECURE"); ok { 172 | p.insecureSkipVerify, _ = strconv.ParseBool(v) 173 | } 174 | 175 | if p.accessToken == "" { 176 | p.setAccessTokenFromConfigFile() 177 | } 178 | } 179 | 180 | func (p *pushCmd) setAccessTokenFromConfigFile() { 181 | usr, err := user.Current() 182 | if err != nil { 183 | return 184 | } 185 | configPath := path.Join(usr.HomeDir, ".cfconfig") 186 | if _, err := os.Stat(configPath); os.IsNotExist(err) { 187 | return 188 | } 189 | var c config 190 | yamlFile, err := os.ReadFile(configPath) 191 | if err != nil { 192 | return 193 | } 194 | if err = yaml.Unmarshal(yamlFile, &c); err != nil { 195 | return 196 | } 197 | for _, context := range c.Contexts { 198 | if context.Name == c.CurrentContext { 199 | p.accessToken = context.Token 200 | break 201 | } 202 | } 203 | } 204 | 205 | func (p *pushCmd) push() error { 206 | var repo *helm.Repo 207 | var err error 208 | 209 | // If the argument looks like a URL, just create a temp repo object 210 | // instead of looking for the entry in the local repository list 211 | if regexp.MustCompile(`^https?://`).MatchString(p.repoName) { 212 | repo, err = helm.TempRepoFromURL(p.repoName) 213 | p.repoName = repo.Config.URL 214 | } else { 215 | repo, err = helm.GetRepoByName(p.repoName) 216 | } 217 | 218 | if err != nil { 219 | return err 220 | } 221 | 222 | if p.dependencyUpdate { 223 | name := filepath.FromSlash(p.chartName) 224 | fi, err := os.Stat(name) 225 | if err != nil { 226 | return err 227 | } 228 | if fi.IsDir() { 229 | if validChart, err := chartutil.IsChartDir(name); !validChart { 230 | return err 231 | } 232 | chartPath, err := filepath.Abs(p.chartName) 233 | if err != nil { 234 | return err 235 | } 236 | if helm.HelmMajorVersionCurrent() == helm.HelmMajorVersion2 { 237 | v2downloadManager := &v2downloader.Manager{ 238 | Out: p.out, 239 | ChartPath: chartPath, 240 | HelmHome: v2settings.Home, 241 | Keyring: p.keyring, 242 | Getters: v2getter.All(v2settings), 243 | Debug: v2settings.Debug, 244 | } 245 | if err := v2downloadManager.Update(); err != nil { 246 | return err 247 | } 248 | } else { 249 | downloadManager := &downloader.Manager{ 250 | Out: p.out, 251 | ChartPath: chartPath, 252 | Keyring: p.keyring, 253 | Getters: getter.All(settings), 254 | Debug: v2settings.Debug, 255 | } 256 | if err := downloadManager.Update(); err != nil { 257 | return err 258 | } 259 | } 260 | } 261 | } 262 | 263 | chart, err := helm.GetChartByName(p.chartName) 264 | if err != nil { 265 | return err 266 | } 267 | 268 | // version override 269 | if p.chartVersion != "" { 270 | chart.SetVersion(p.chartVersion) 271 | } 272 | 273 | // app version override 274 | if p.appVersion != "" { 275 | chart.SetAppVersion(p.appVersion) 276 | } 277 | 278 | // username/password override(s) 279 | username := repo.Config.Username 280 | password := repo.Config.Password 281 | if p.username != "" { 282 | username = p.username 283 | } 284 | if p.password != "" { 285 | password = p.password 286 | } 287 | 288 | // unset accessToken if repo credentials are provided 289 | if username != "" && password != "" { 290 | p.accessToken = "" 291 | } 292 | 293 | // in case the repo is stored with cm:// protocol, remove it 294 | var url string 295 | if p.useHTTP { 296 | url = strings.Replace(repo.Config.URL, "cm://", "http://", 1) 297 | } else { 298 | url = strings.Replace(repo.Config.URL, "cm://", "https://", 1) 299 | } 300 | 301 | client, err := cm.NewClient( 302 | cm.URL(url), 303 | cm.Username(username), 304 | cm.Password(password), 305 | cm.AccessToken(p.accessToken), 306 | cm.AuthHeader(p.authHeader), 307 | cm.ContextPath(p.contextPath), 308 | cm.CAFile(p.caFile), 309 | cm.CertFile(p.certFile), 310 | cm.KeyFile(p.keyFile), 311 | cm.InsecureSkipVerify(p.insecureSkipVerify), 312 | cm.Timeout(p.timeout), 313 | ) 314 | if err != nil { 315 | return err 316 | } 317 | 318 | // update context path if not override 319 | if p.contextPath == "" { 320 | index, err := helm.GetIndexByRepo(repo, getIndexDownloader(client)) 321 | if err != nil { 322 | return err 323 | } 324 | client.Option(cm.ContextPath(index.ServerInfo.ContextPath)) 325 | } 326 | 327 | tmp, err := os.MkdirTemp("", "helm-push-") 328 | if err != nil { 329 | return err 330 | } 331 | defer func(path string) { 332 | err := os.RemoveAll(path) 333 | if err != nil { 334 | fmt.Printf("Failed to remove temporary directory %s: %s", path, err) 335 | } 336 | }(tmp) 337 | 338 | chartPackagePath, err := helm.CreateChartPackage(chart, tmp) 339 | if err != nil { 340 | return err 341 | } 342 | 343 | fmt.Printf("Pushing %s to %s...\n", filepath.Base(chartPackagePath), p.repoName) 344 | resp, err := client.UploadChartPackage(chartPackagePath, p.forceUpload) 345 | if err != nil { 346 | return err 347 | } 348 | 349 | return handlePushResponse(resp) 350 | } 351 | 352 | func (p *pushCmd) download(fileURL string) error { 353 | parsedURL, err := url.Parse(fileURL) 354 | if err != nil { 355 | return err 356 | } 357 | 358 | parts := strings.Split(parsedURL.Path, "/") 359 | numParts := len(parts) 360 | if numParts <= 1 { 361 | return fmt.Errorf("invalid file url: %s", fileURL) 362 | } 363 | 364 | filePath := parts[numParts-1] 365 | 366 | numRemoveParts := 1 367 | if parts[numParts-2] == "charts" { 368 | numRemoveParts++ 369 | filePath = "charts/" + filePath 370 | } 371 | 372 | parsedURL.Path = strings.Join(parts[:numParts-numRemoveParts], "/") 373 | 374 | if p.useHTTP { 375 | parsedURL.Scheme = "http" 376 | } else { 377 | parsedURL.Scheme = "https" 378 | } 379 | 380 | client, err := cm.NewClient( 381 | cm.URL(parsedURL.String()), 382 | cm.Username(p.username), 383 | cm.Password(p.password), 384 | cm.AccessToken(p.accessToken), 385 | cm.AuthHeader(p.authHeader), 386 | cm.ContextPath(p.contextPath), 387 | cm.CAFile(p.caFile), 388 | cm.CertFile(p.certFile), 389 | cm.KeyFile(p.keyFile), 390 | cm.InsecureSkipVerify(p.insecureSkipVerify), 391 | ) 392 | if err != nil { 393 | return err 394 | } 395 | 396 | resp, err := client.DownloadFile(filePath) 397 | if err != nil { 398 | return err 399 | } 400 | 401 | return handleDownloadResponse(resp) 402 | } 403 | 404 | func handlePushResponse(resp *http.Response) error { 405 | if resp.StatusCode != 201 && resp.StatusCode != 202 { 406 | b, err := io.ReadAll(resp.Body) 407 | if err != nil { 408 | return err 409 | } 410 | return getChartmuseumError(b, resp.StatusCode) 411 | } 412 | fmt.Println("Done.") 413 | return nil 414 | } 415 | 416 | func handleDownloadResponse(resp *http.Response) error { 417 | b, err := io.ReadAll(resp.Body) 418 | defer resp.Body.Close() 419 | if err != nil { 420 | return err 421 | } 422 | if resp.StatusCode != 200 { 423 | return getChartmuseumError(b, resp.StatusCode) 424 | } 425 | fmt.Print(string(b)) 426 | return nil 427 | } 428 | 429 | func getChartmuseumError(b []byte, code int) error { 430 | var er struct { 431 | Error string `json:"error"` 432 | } 433 | err := json.Unmarshal(b, &er) 434 | if err != nil || er.Error == "" { 435 | return fmt.Errorf("%d: could not properly parse response JSON: %s", code, string(b)) 436 | } 437 | return fmt.Errorf("%d: %s", code, er.Error) 438 | } 439 | 440 | func getIndexDownloader(client *cm.Client) helm.IndexDownloader { 441 | return func() ([]byte, error) { 442 | resp, err := client.DownloadFile("index.yaml") 443 | if err != nil { 444 | return nil, err 445 | } 446 | defer resp.Body.Close() 447 | b, err := io.ReadAll(resp.Body) 448 | if err != nil { 449 | return nil, err 450 | } 451 | if resp.StatusCode != 200 { 452 | return nil, getChartmuseumError(b, resp.StatusCode) 453 | } 454 | return b, nil 455 | } 456 | } 457 | 458 | func main() { 459 | cmd := newPushCmd(os.Args[1:]) 460 | if err := cmd.Execute(); err != nil { 461 | os.Exit(1) 462 | } 463 | } 464 | 465 | // defaultKeyring returns the expanded path to the default keyring. 466 | func defaultKeyring() string { 467 | return os.ExpandEnv("$HOME/.gnupg/pubring.gpg") 468 | } 469 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 7 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 8 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 9 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= 10 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 11 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= 12 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= 13 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= 14 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= 15 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= 16 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 17 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 18 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 19 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= 20 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= 21 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 22 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 23 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 24 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 25 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 26 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 27 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= 28 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 29 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 30 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 31 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= 32 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 33 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 34 | github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= 35 | github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= 36 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 37 | github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= 38 | github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= 39 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 40 | github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= 41 | github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= 42 | github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= 43 | github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= 44 | github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= 45 | github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= 46 | github.com/Microsoft/hcsshim v0.9.6 h1:VwnDOgLeoi2du6dAznfmspNqTiwczvjv4K7NxuY9jsY= 47 | github.com/Microsoft/hcsshim v0.9.6/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= 48 | github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= 49 | github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= 50 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 51 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 52 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 53 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 54 | github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= 55 | github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= 56 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 57 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 58 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 59 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 60 | github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= 61 | github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= 62 | github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng= 63 | github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= 64 | github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ= 65 | github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= 66 | github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= 67 | github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= 68 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 69 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 70 | github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= 71 | github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 72 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 73 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 74 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 75 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 76 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 77 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 78 | github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= 79 | github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 80 | github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 81 | github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 82 | github.com/containerd/cgroups v1.0.4 h1:jN/mbWBEaz+T1pi5OFtnkQ+8qnmEbAr1Oo1FRm5B0dA= 83 | github.com/containerd/cgroups v1.0.4/go.mod h1:nLNQtsF7Sl2HxNebu77i1R0oDlhiTG+kO4JTrUzo6IA= 84 | github.com/containerd/containerd v1.6.15 h1:4wWexxzLNHNE46aIETc6ge4TofO550v+BlLoANrbses= 85 | github.com/containerd/containerd v1.6.15/go.mod h1:U2NnBPIhzJDm59xF7xB2MMHnKtggpZ+phKg8o2TKj2c= 86 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 87 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 88 | github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= 89 | github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= 90 | github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= 91 | github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= 92 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 93 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 94 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 95 | github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aBfCb7iqHmDEIp6fBvC/hQUddQfg+3qdYjwzaiP9Hnc= 96 | github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2/go.mod h1:WHNsWjnIn2V1LYOrME7e8KxSeKunYHsxEm4am0BUtcI= 97 | github.com/docker/cli v20.10.24+incompatible h1:vfV+1kv9yD0/cpL6wWY9cE+Y9J8hL/NqJDGob0B3RVw= 98 | github.com/docker/cli v20.10.24+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= 99 | github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= 100 | github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= 101 | github.com/docker/docker v20.10.24+incompatible h1:Ugvxm7a8+Gz6vqQYQQ2W7GYq5EUPaAiuPgIfVyI3dYE= 102 | github.com/docker/docker v20.10.24+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= 103 | github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= 104 | github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= 105 | github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= 106 | github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= 107 | github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= 108 | github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= 109 | github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= 110 | github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= 111 | github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= 112 | github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 113 | github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= 114 | github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= 115 | github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= 116 | github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= 117 | github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= 118 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 119 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 120 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 121 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 122 | github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= 123 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 124 | github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= 125 | github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= 126 | github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= 127 | github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= 128 | github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= 129 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 130 | github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= 131 | github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= 132 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 133 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 134 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 135 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 136 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 137 | github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= 138 | github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= 139 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 140 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 141 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 142 | github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= 143 | github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 144 | github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= 145 | github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 146 | github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= 147 | github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= 148 | github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= 149 | github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= 150 | github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= 151 | github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= 152 | github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= 153 | github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= 154 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 155 | github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= 156 | github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= 157 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 158 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 159 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 160 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 161 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 162 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 163 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 164 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= 165 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 166 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 167 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 168 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 169 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 170 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 171 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 172 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 173 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 174 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 175 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 176 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 177 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 178 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 179 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 180 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 181 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 182 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 183 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 184 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 185 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 186 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 187 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 188 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 189 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 190 | github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k= 191 | github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= 192 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 193 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 194 | github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= 195 | github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= 196 | github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= 197 | github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= 198 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 199 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 200 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 201 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 202 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 203 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 204 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 205 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 206 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 207 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 208 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 209 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 210 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 211 | github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= 212 | github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 213 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 214 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 215 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 216 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 217 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 218 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 219 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 220 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 221 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 222 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 223 | github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= 224 | github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= 225 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 226 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 227 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 228 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 229 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 230 | github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= 231 | github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= 232 | github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= 233 | github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= 234 | github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= 235 | github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= 236 | github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 237 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 238 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 239 | github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= 240 | github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= 241 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 242 | github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= 243 | github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= 244 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 245 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 246 | github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= 247 | github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 248 | github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= 249 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 250 | github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 251 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 252 | github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 253 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 254 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 255 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 256 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 257 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 258 | github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 259 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 260 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 261 | github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= 262 | github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= 263 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 264 | github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 265 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 266 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 267 | github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 268 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 269 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 270 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 271 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 272 | github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= 273 | github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= 274 | github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 275 | github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 276 | github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= 277 | github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 278 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 279 | github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= 280 | github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= 281 | github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= 282 | github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= 283 | github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= 284 | github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 285 | github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= 286 | github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= 287 | github.com/moby/sys/mountinfo v0.5.0 h1:2Ks8/r6lopsxWi9m58nlwjaeSzUX9iiL1vj5qB/9ObI= 288 | github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= 289 | github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= 290 | github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= 291 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 292 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 293 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 294 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 295 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 296 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 297 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 298 | github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= 299 | github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= 300 | github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= 301 | github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= 302 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= 303 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 304 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 305 | github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 306 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= 307 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 308 | github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= 309 | github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= 310 | github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= 311 | github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= 312 | github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= 313 | github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= 314 | github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= 315 | github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= 316 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 317 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 318 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 319 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 320 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 321 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 322 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 323 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 324 | github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= 325 | github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= 326 | github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= 327 | github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= 328 | github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= 329 | github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= 330 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 331 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 332 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 333 | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 334 | github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= 335 | github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= 336 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 337 | github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= 338 | github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= 339 | github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= 340 | github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= 341 | github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= 342 | github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= 343 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 344 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 345 | github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= 346 | github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= 347 | github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= 348 | github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= 349 | github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= 350 | github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= 351 | github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 352 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 353 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 354 | github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= 355 | github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= 356 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 357 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 358 | github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= 359 | github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= 360 | github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 361 | github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= 362 | github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= 363 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 364 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 365 | github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= 366 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 367 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 368 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 369 | github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= 370 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 371 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 372 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 373 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 374 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 375 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 376 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 377 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 378 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 379 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 380 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 381 | github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= 382 | github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= 383 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= 384 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= 385 | github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= 386 | github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= 387 | github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= 388 | github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= 389 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 390 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 391 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 392 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 393 | github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI= 394 | github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= 395 | github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE= 396 | github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= 397 | github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= 398 | github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= 399 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 400 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 401 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 402 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 403 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 404 | go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= 405 | go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= 406 | go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= 407 | go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc= 408 | go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= 409 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 410 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 411 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 412 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 413 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 414 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 415 | golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= 416 | golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= 417 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 418 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 419 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 420 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 421 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 422 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 423 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 424 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 425 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 426 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 427 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 428 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 429 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 430 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 431 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 432 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 433 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 434 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 435 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 436 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 437 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 438 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 439 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 440 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 441 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 442 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 443 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 444 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 445 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 446 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 447 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 448 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 449 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 450 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 451 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 452 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 453 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 454 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 455 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 456 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 457 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 458 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 459 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 460 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 461 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 462 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 463 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 464 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 465 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 466 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 467 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 468 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 469 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 470 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 471 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 472 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 473 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 474 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 475 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 476 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 477 | golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 478 | golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 479 | golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 480 | golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= 481 | golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= 482 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 483 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 484 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 485 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 486 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 487 | golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 488 | golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg= 489 | golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= 490 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 491 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 492 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 493 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 494 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 495 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 496 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 497 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 498 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 499 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 500 | golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= 501 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 502 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 503 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 504 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 505 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 506 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 507 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 508 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 509 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 510 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 511 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 512 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 513 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 514 | golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 515 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 516 | golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 517 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 518 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 519 | golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 520 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 521 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 522 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 523 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 524 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 525 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 526 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 527 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 528 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 529 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 530 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 531 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 532 | golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 533 | golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 534 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 535 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 536 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 537 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 538 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 539 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 540 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 541 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 542 | golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 543 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 544 | golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 545 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 546 | golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 547 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 548 | golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= 549 | golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 550 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 551 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 552 | golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= 553 | golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= 554 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 555 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 556 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 557 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 558 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 559 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 560 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 561 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 562 | golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= 563 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 564 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 565 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 566 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 567 | golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= 568 | golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 569 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 570 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 571 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 572 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 573 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 574 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 575 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 576 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 577 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 578 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 579 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 580 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 581 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 582 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 583 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 584 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 585 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 586 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 587 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 588 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 589 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 590 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 591 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 592 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 593 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 594 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 595 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 596 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 597 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 598 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 599 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 600 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 601 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 602 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 603 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 604 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 605 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 606 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 607 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 608 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 609 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 610 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 611 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 612 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 613 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 614 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 615 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 616 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 617 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 618 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 619 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 620 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 621 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 622 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 623 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 624 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 625 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 626 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 627 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 628 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 629 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 630 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 631 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 632 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 633 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 634 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 635 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 636 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 637 | google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= 638 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 639 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 640 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 641 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 642 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 643 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 644 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 645 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 646 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 647 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 648 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 649 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 650 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 651 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 652 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 653 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 654 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 655 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 656 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 657 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 658 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 659 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 660 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 661 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 662 | google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 663 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 664 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 665 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 666 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 667 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 668 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 669 | google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 670 | google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 h1:hrbNEivu7Zn1pxvHk6MBrq9iE22woVILTHqexqBxe6I= 671 | google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= 672 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 673 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 674 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 675 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 676 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 677 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 678 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 679 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 680 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 681 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 682 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 683 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 684 | google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= 685 | google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 686 | google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= 687 | google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw= 688 | google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= 689 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 690 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 691 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 692 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 693 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 694 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 695 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 696 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 697 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 698 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 699 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 700 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 701 | google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 702 | google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 703 | google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= 704 | google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 705 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 706 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 707 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 708 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 709 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= 710 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 711 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 712 | gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= 713 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 714 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 715 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 716 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 717 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 718 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 719 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 720 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 721 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 722 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 723 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 724 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 725 | gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 726 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 727 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 728 | gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= 729 | gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= 730 | helm.sh/helm/v3 v3.11.2 h1:P3cLaFxfoxaGLGJVnoPrhf1j86LC5EDINSpYSpMUkkA= 731 | helm.sh/helm/v3 v3.11.2/go.mod h1:Hw+09mfpDiRRKAgAIZlFkPSeOkvv7Acl5McBvQyNPVw= 732 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 733 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 734 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 735 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 736 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 737 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 738 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 739 | k8s.io/api v0.26.0 h1:IpPlZnxBpV1xl7TGk/X6lFtpgjgntCg8PJ+qrPHAC7I= 740 | k8s.io/api v0.26.0/go.mod h1:k6HDTaIFC8yn1i6pSClSqIwLABIcLV9l5Q4EcngKnQg= 741 | k8s.io/apiextensions-apiserver v0.26.0 h1:Gy93Xo1eg2ZIkNX/8vy5xviVSxwQulsnUdQ00nEdpDo= 742 | k8s.io/apiextensions-apiserver v0.26.0/go.mod h1:7ez0LTiyW5nq3vADtK6C3kMESxadD51Bh6uz3JOlqWQ= 743 | k8s.io/apimachinery v0.26.0 h1:1feANjElT7MvPqp0JT6F3Ss6TWDwmcjLypwoPpEf7zg= 744 | k8s.io/apimachinery v0.26.0/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74= 745 | k8s.io/cli-runtime v0.26.0 h1:aQHa1SyUhpqxAw1fY21x2z2OS5RLtMJOCj7tN4oq8mw= 746 | k8s.io/cli-runtime v0.26.0/go.mod h1:o+4KmwHzO/UK0wepE1qpRk6l3o60/txUZ1fEXWGIKTY= 747 | k8s.io/client-go v0.26.0 h1:lT1D3OfO+wIi9UFolCrifbjUUgu7CpLca0AD8ghRLI8= 748 | k8s.io/client-go v0.26.0/go.mod h1:I2Sh57A79EQsDmn7F7ASpmru1cceh3ocVT9KlX2jEZg= 749 | k8s.io/helm v2.17.0+incompatible h1:Bpn6o1wKLYqKM3+Osh8e+1/K2g/GsQJ4F4yNF2+deao= 750 | k8s.io/helm v2.17.0+incompatible/go.mod h1:LZzlS4LQBHfciFOurYBFkCMTaZ0D1l+p0teMg7TSULI= 751 | k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= 752 | k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= 753 | k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= 754 | k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= 755 | k8s.io/utils v0.0.0-20221107191617-1a15be271d1d h1:0Smp/HP1OH4Rvhe+4B8nWGERtlqAGSftbSbbmm45oFs= 756 | k8s.io/utils v0.0.0-20221107191617-1a15be271d1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= 757 | oras.land/oras-go v1.2.2 h1:0E9tOHUfrNH7TCDk5KU0jVBEzCqbfdyuVfGmJ7ZeRPE= 758 | oras.land/oras-go v1.2.2/go.mod h1:Apa81sKoZPpP7CDciE006tSZ0x3Q3+dOoBcMZ/aNxvw= 759 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 760 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 761 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 762 | sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= 763 | sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= 764 | sigs.k8s.io/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM= 765 | sigs.k8s.io/kustomize/api v0.12.1/go.mod h1:y3JUhimkZkR6sbLNwfJHxvo1TCLwuwm14sCYnkH6S1s= 766 | sigs.k8s.io/kustomize/kyaml v0.13.9 h1:Qz53EAaFFANyNgyOEJbT/yoIHygK40/ZcvU3rgry2Tk= 767 | sigs.k8s.io/kustomize/kyaml v0.13.9/go.mod h1:QsRbD0/KcU+wdk0/L0fIp2KLnohkVzs6fQ85/nOXac4= 768 | sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= 769 | sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= 770 | sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= 771 | sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= 772 | --------------------------------------------------------------------------------