diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..bd297fc9 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @davidcollom diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..1aa00247 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,13 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 0 + + - package-ecosystem: "gomod" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 0 diff --git a/.github/workflows/build-test.yaml b/.github/workflows/build-test.yaml new file mode 100644 index 00000000..7bfe7f72 --- /dev/null +++ b/.github/workflows/build-test.yaml @@ -0,0 +1,89 @@ +name: Test & Build +on: + pull_request: + branches: + - 'main' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + lint: + permissions: + contents: read # for actions/checkout to fetch code + pull-requests: read # for golangci/golangci-lint-action to fetch pull requests + name: Lint Go code + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - name: Setup Golang + uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.0 + - name: Run golangci-lint + uses: golangci/golangci-lint-action@639cd343e1d3b897ff35927a75193d57cfcba299 # v3.6.0 + with: + version: v1.53 + args: --timeout 10m --exclude SA5011 --verbose --issues-exit-code=0 + only-new-issues: true + + test: + name: Run unit tests for Go packages + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 # v3.5.3 + - name: Setup Go + uses: actions/setup-go@v4 + + - name: Download and required packages + run: | + make deps + + - name: Run all unit tests + run: make test + + - name: check test coverage + uses: vladopajic/go-test-coverage@v2 + with: + config: ./.testcoverage.yml + + - name: Generate code coverage artifacts + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + with: + name: code-coverage + path: coverage.out + + build: + needs: + - test + - lint + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + platform: + - linux/amd64 + - linux/arm64 + - linux/arm/v7 + name: Build Images + steps: + - name: Checkout code + uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + platforms: ${{ matrix.platform }} + + - name: Build Images + uses: docker/build-push-action@v4 + with: + context: . + platforms: ${{ matrix.platform }} + push: false + tags: quay.io/jetstack/version-checker:${{github.sha}} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/helm-test.yaml b/.github/workflows/helm-test.yaml new file mode 100644 index 00000000..bdd2a0dc --- /dev/null +++ b/.github/workflows/helm-test.yaml @@ -0,0 +1,104 @@ +name: Test Helm Chart +on: + pull_request: + paths: + - '!*.md' + - 'deploy/charts/version-checker/**' + branches: + - 'main' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + lint: + permissions: + contents: read # for actions/checkout to fetch code + pull-requests: read # for golangci/golangci-lint-action to fetch pull requests + name: Lint Helm Chart + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + + - uses: azure/setup-helm@v3 + + - run: helm lint deploy/charts/version-checker + + docs: + name: Generate Helm Docs + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.head.ref }} + - name: Check for values.yaml changes + uses: dorny/paths-filter@v2 + id: filter + with: + filters: | + values: + - 'deploy/charts/version-checker/values.yaml' + - 'deploy/charts/version-checker/Chart.yaml' + - name: Install Helm Docs + if: steps.filter.outputs.values == 'true' + uses: envoy/install-helm-docs@v1.0.0 + with: + version: 1.11.0 + - name: Update Helm Docs + if: steps.filter.outputs.values == 'true' + run: | + set -ex + cd deploy/charts/version-checker + helm-docs + - name: Check for README.md changes + uses: dorny/paths-filter@v2 + id: filter-readme + with: + base: HEAD + filters: | + readme: + - 'deploy/charts/version-checker/README.md' + - name: Commit Helm Docs + if: steps.filter-readme.outputs.readme == 'true' + run: | + set -ex + git config --local user.email "github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" + git add deploy/charts/version-checker + git commit -m "[HELM] Update helm docs" + - name: Push Changes + if: steps.filter-readme.outputs.readme == 'true' + uses: ad-m/github-push-action@v0.6.0 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + branch: ${{ github.head_ref }} + + + test: + name: Run unit tests for Helm Chart + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + + - uses: azure/setup-helm@v3 + with: + token: ${{ github.token }} + + - name: Install helm Plugins + run: | + if [ ! -e "${HELM_PLUGINS}/helm-unittest" ]; then + helm plugin install https://github.com/helm-unittest/helm-unittest.git + fi + + - name: Run Tests + run: | + if [ ! -e "deploy/charts/version-checker/tests" ]; then + echo "Not running tests, directory doesn't exist: deploy/charts/version-checker/tests" + exit 0 + fi + helm unittest deploy/charts/version-checker diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 00000000..d41b795a --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,160 @@ +name: Version-Checker Release + +on: + push: + branches: + - "release-v*" + tags: + - "*" + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + update-version-files: + # Don't push back to a tag! + if: ${{ !startsWith(github.ref, 'refs/tags/') }} + name: Update Version Numbers + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v2 + - uses: bhowell2/github-substring-action@1.0.2 + id: release_number + with: + value: ${{github.ref_name}} + index_of_str: "release-" + + - name: Find and Replace Helm Chart Version + uses: jacobtomlinson/gha-find-replace@v3 + with: + find: 'v(\d+)\.(\d+)\.(\d+)(-rc(\d)+)?' + replace: "${{steps.release_number.outputs.substring}}" + include: "deploy/charts/version-checker/Chart.yaml" + regex: true + - name: Find and Replace Kubernetes Manifests + uses: jacobtomlinson/gha-find-replace@v3 + with: + find: 'v(\d+)\.(\d+)\.(\d+)(-rc(\d)+)?' + replace: "${{steps.release_number.outputs.substring}}" + include: "deploy/yaml/deploy.yaml" + regex: true + - name: Find and Replace Makefile versions + uses: jacobtomlinson/gha-find-replace@v3 + with: + find: 'v(\d+)\.(\d+)\.(\d+)(-rc(\d)+)?' + replace: "${{steps.release_number.outputs.substring}}" + include: "Makefile" + regex: true + + - name: Detect any Local Changes + uses: dorny/paths-filter@v2 + id: filter + with: + base: HEAD + filters: | + versions: + - 'Makefile' + - 'deploy/yaml/deploy.yaml' + - 'deploy/charts/version-checker/Chart.yaml' + + + - name: Commit files + if: steps.filter.outputs.versions == 'true' + run: | + git config --local user.email "github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" + git status + git commit -a -m "Bump versions to ${{steps.release_number.outputs.substring}} " + - name: Push changes + if: steps.filter.outputs.versions == 'true' + uses: ad-m/github-push-action@v0.6.0 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + branch: ${{ github.ref_name }} + + helm-release: + runs-on: ubuntu-latest + steps: + # Checkout our Repo + - uses: actions/checkout@v3 + with: + path: version-checker + + - name: checkout jetstack-charts + uses: actions/checkout@v3 + with: + token: ${{ secrets.JETSTACK_CHARTS_PAT }} + repository: jetstack/jetstack-charts + ref: main + path: jetstack-charts + + - uses: azure/setup-helm@v3 + with: + token: ${{ github.token }} + + - name: package helm chart + run: | + helm package version-checker/deploy/charts/version-checker -d jetstack-charts/charts/ + + - name: Creating PR + if: startsWith(github.ref, 'refs/tags/') + uses: peter-evans/create-pull-request@v5 + with: + token: ${{ secrets.JETSTACK_CHARTS_PAT }} + title: "Release version-checker ${{github.ref_name }}" + commit-message: "Release version-checker ${{github.ref_name }}" + branch: version-checker/${{github.ref_name}} + path: jetstack-charts + add-paths: charts/*.tgz + delete-branch: true + signoff: true + base: main + draft: ${{ !startsWith(github.ref, 'refs/tags/') }} + + docker-release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + platforms: ${{ matrix.platform }} + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + registry: quay.io + username: ${{ secrets.QUAY_USERNAME }} + password: ${{ secrets.QUAY_ROBOT_TOKEN }} + + - name: Build and push (if applicable) + uses: docker/build-push-action@v4 + with: + context: . + platforms: linux/amd64,linux/arm64,linux/arm/v7 + push: ${{ startsWith(github.ref, 'refs/tags/') }} + pull: true + tags: quay.io/jetstack/version-checker:${{github.ref_name}} + cache-from: type=gha + cache-to: type=gha,mode=max + + github-release: + name: Create/Update GitHub Release + permissions: + contents: write + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Create Release / Change Logs + uses: softprops/action-gh-release@v1 + with: + draft: ${{ !startsWith(github.ref, 'refs/tags/') }} + prerelease: ${{ contains('-rc', github.ref_name) || !startsWith(github.ref, 'refs/tags/') }} + generate_release_notes: true diff --git a/.gitignore b/.gitignore index 5e56e040..3733fa19 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /bin +coverage.out diff --git a/.testcoverage.yml b/.testcoverage.yml new file mode 100644 index 00000000..3e4c51f8 --- /dev/null +++ b/.testcoverage.yml @@ -0,0 +1,38 @@ +# (mandatory) +# Path to coverprofile file (output of `go test -coverprofile` command) +profile: coverage.out + +# (optional) +# When specified reported file paths will not contain local prefix in the output +# local-prefix: "github.com/org/project" + +# Holds coverage thresholds percentages, values should be in range [0-100] +threshold: + # (optional; default 0) + # The minimum coverage that each file should have + file: 0 + + # (optional; default 0) + # The minimum coverage that each package should have + package: 0 + + # (optional; default 0) + # The minimum total coverage project should have + total: 0 + +# Holds regexp rules which will override thresholds for matched files or packages +# override: +# # Increase coverage threshold to 100% for `foo` package (default is 80, as configured above) +# - threshold: 100 +# path: ^pkg/lib/foo$ + +# Holds regexp rules which will exclude matched files or packages from coverage statistics +# exclude: +# # Exclude files or packages matching their paths +# paths: +# - \.pb\.go$ # excludes all protobuf generated files +# - ^pkg/bar # exclude package `pkg/bar` + +# NOTES: +# - symbol `/` in all path regexps will be replaced by +# current OS file path separator to properly work on Windows diff --git a/Dockerfile b/Dockerfile index 0d94e94a..db5f9dc9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,18 @@ -FROM alpine:3.12 +FROM golang:1.20-alpine as builder + +RUN apk --no-cache add make + +COPY . /app/ +WORKDIR /app/ + +RUN make build + + +FROM alpine:3.18.3 LABEL description="Kubernetes utility for exposing used image versions compared to the latest version, as metrics." RUN apk --no-cache add ca-certificates -COPY ./bin/version-checker-linux /usr/bin/version-checker +COPY --from=builder /app/bin/version-checker /usr/bin/version-checker ENTRYPOINT ["/usr/bin/version-checker"] diff --git a/Makefile b/Makefile index 60f876b6..563244bb 100644 --- a/Makefile +++ b/Makefile @@ -4,23 +4,28 @@ ARCH ?= amd64 help: ## display this help @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n\nTargets:\n"} /^[a-zA-Z0-9_-]+:.*?##/ { printf " \033[36m%-20s\033[0m %s\n", $$1, $$2 }' $(MAKEFILE_LIST) -.PHONY: help build docker all clean +.PHONY: help build image all clean -test: ## test version-checker - go test ./... +deps: ## Download all Dependencies + go mod download -build: ## build version-checker +test: deps ## test version-checker + go test ./... -coverprofile=coverage.out + +$(BINDIR): mkdir -p $(BINDIR) + +build: deps $(BINDIR) ## build version-checker CGO_ENABLED=0 go build -o ./bin/version-checker ./cmd/. verify: test build ## tests and builds version-checker image: ## build docker image GOARCH=$(ARCH) GOOS=linux CGO_ENABLED=0 go build -o ./bin/version-checker-linux ./cmd/. - docker build -t quay.io/jetstack/version-checker:v0.2.1 . + docker build -t quay.io/jetstack/version-checker:v0.5.1 . clean: ## clean up created files rm -rf \ $(BINDIR) -all: test build docker ## runs test, build and docker +all: test build image ## runs test, build and image diff --git a/OWNERS b/OWNERS deleted file mode 100644 index ba717594..00000000 --- a/OWNERS +++ /dev/null @@ -1,4 +0,0 @@ -approvers: -- joshvanl -reviewers: -- joshvanl diff --git a/README.md b/README.md index 377a828d..a8ed91f3 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,6 @@ images running in the cluster, as well as the latest available upstream. These checks get exposed as Prometheus metrics to be viewed on a dashboard, or _soft_ alert cluster operators. -> This tool is currently experimental. - -If you're interested in this tool, version checking is a built-in feature -in our [Preflight](https://preflight.jetstack.io/) product. You may want to -check it out if you would like multi-cluster component version checking. - ## Registries version-checker supports the following registries: @@ -40,8 +34,15 @@ $ kubectl apply -k ./deploy/yaml Or through helm; ```sh -$ cd ./deploy/charts/version-checker && kubectl create namespace version-checker -$ helm install version-checker . -n version-checker +$ helm repo add jetstack https://charts.jetstack.io +"jetstack" has been added to your repositories +$ helm install version-checker jetstack/version-checker +NAME: version-checker +LAST DEPLOYED: Wed Jul 12 17:47:41 2023 +NAMESPACE: default +STATUS: deployed +REVISION: 1 +TEST SUITE: None ``` The helm chart supports creating a Prometheus/ServiceMonitor to expose the diff --git a/cmd/app/app.go b/cmd/app/app.go index 125edd65..9cd6ed70 100644 --- a/cmd/app/app.go +++ b/cmd/app/app.go @@ -10,6 +10,7 @@ import ( "k8s.io/client-go/kubernetes" _ "k8s.io/client-go/plugin/pkg/client/auth" // Load all auth plugins + "github.com/jetstack/version-checker/pkg/api" "github.com/jetstack/version-checker/pkg/client" "github.com/jetstack/version-checker/pkg/controller" "github.com/jetstack/version-checker/pkg/metrics" @@ -66,9 +67,9 @@ func NewCommand(ctx context.Context) *cobra.Command { } }() - defaultTestAllInfoMsg := `only containers with the annotation "enable.version-checker/${my-container}=true" will be parsed` + defaultTestAllInfoMsg := fmt.Sprintf(`only containers with the annotation "%s/${my-container}=true" will be parsed`, api.EnableAnnotationKey) if opts.DefaultTestAll { - defaultTestAllInfoMsg = `all containers will be tested, unless they have the annotation "enable.version-checker/${my-container}=false"` + defaultTestAllInfoMsg = fmt.Sprintf(`all containers will be tested, unless they have the annotation "%s/${my-container}=false"`, api.EnableAnnotationKey) } log.Infof("flag --test-all-containers=%t %s", opts.DefaultTestAll, defaultTestAllInfoMsg) diff --git a/cmd/app/options.go b/cmd/app/options.go index 17898e19..1ef87952 100644 --- a/cmd/app/options.go +++ b/cmd/app/options.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "regexp" + "strconv" "strings" "time" @@ -12,6 +13,7 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" cliflag "k8s.io/component-base/cli/flag" + "github.com/jetstack/version-checker/pkg/api" "github.com/jetstack/version-checker/pkg/client" "github.com/jetstack/version-checker/pkg/client/selfhosted" ) @@ -29,20 +31,28 @@ const ( envGCRAccessToken = "GCR_TOKEN" + envGHCRAccessToken = "GHCR_TOKEN" + envQuayToken = "QUAY_TOKEN" - envSelfhostedPrefix = "SELFHOSTED" - envSelfhostedUsername = "USERNAME" - envSelfhostedPassword = "PASSWORD" - envSelfhostedBearer = "TOKEN" - envSelfhostedHost = "HOST" + envSelfhostedPrefix = "SELFHOSTED" + envSelfhostedUsername = "USERNAME" + envSelfhostedPassword = "PASSWORD" + envSelfhostedHost = "HOST" + envSelfhostedBearer = "TOKEN" + envSelfhostedTokenPath = "TOKEN_PATH" + envSelfhostedInsecure = "INSECURE" + envSelfhostedCAPath = "CA_PATH" ) var ( selfhostedHostReg = regexp.MustCompile("^VERSION_CHECKER_SELFHOSTED_HOST_(.*)") selfhostedUsernameReg = regexp.MustCompile("^VERSION_CHECKER_SELFHOSTED_USERNAME_(.*)") selfhostedPasswordReg = regexp.MustCompile("^VERSION_CHECKER_SELFHOSTED_PASSWORD_(.*)") + selfhostedTokenPath = regexp.MustCompile("^VERSION_CHECKER_SELFHOSTED_TOKEN_PATH_(.*)") selfhostedTokenReg = regexp.MustCompile("^VERSION_CHECKER_SELFHOSTED_TOKEN_(.*)") + selfhostedCAPath = regexp.MustCompile("^VERSION_CHECKER_SELFHOSTED_CA_PATH_(.*)") + selfhostedInsecureReg = regexp.MustCompile("^VERSION_CHECKER_SELFHOSTED_INSECURE_(.*)") ) // Options is a struct to hold options for the version-checker @@ -92,7 +102,7 @@ func (o *Options) addAppFlags(fs *pflag.FlagSet) { fs.BoolVarP(&o.DefaultTestAll, "test-all-containers", "a", false, "If enabled, all containers will be tested, unless they have the "+ - `annotation "enable.version-checker/${my-container}=false".`) + fmt.Sprintf(`annotation "%s/${my-container}=false".`, api.EnableAnnotationKey)) fs.DurationVarP(&o.CacheTimeout, "image-cache-timeout", "c", time.Minute*30, @@ -158,6 +168,15 @@ func (o *Options) addAuthFlags(fs *pflag.FlagSet) { )) /// + /// GHCR + fs.StringVar(&o.Client.GHCR.Token, + "gchr-token", "", + fmt.Sprintf( + "Personal Access token for read access to GHCR releases (%s_%s).", + envPrefix, envGHCRAccessToken, + )) + /// + /// Quay fs.StringVar(&o.Client.Quay.Token, "quay-token", "", @@ -187,12 +206,32 @@ func (o *Options) addAuthFlags(fs *pflag.FlagSet) { "username/password (%s_%s).", envPrefix, envSelfhostedBearer, )) + fs.StringVar(&o.selfhosted.TokenPath, + "selfhosted-token-path", "", + fmt.Sprintf( + "Override the default selfhosted registry's token auth path. "+ + "(%s_%s).", + envPrefix, envSelfhostedTokenPath, + )) fs.StringVar(&o.selfhosted.Host, "selfhosted-registry-host", "", fmt.Sprintf( - "Full host of the selfhosted registry. Include http[s] scheme (%s_%s", + "Full host of the selfhosted registry. Include http[s] scheme (%s_%s)", envPrefix, envSelfhostedHost, )) + fs.StringVar(&o.selfhosted.Host, + "selfhosted-registry-ca-path", "", + fmt.Sprintf( + "Absolute path to a PEM encoded x509 certificate chain. (%s_%s)", + envPrefix, envSelfhostedCAPath, + )) + fs.BoolVarP(&o.selfhosted.Insecure, + "selfhosted-insecure", "", false, + fmt.Sprintf( + "Enable/Disable SSL Certificate Validation. WARNING: "+ + "THIS IS NOT RECOMMENDED AND IS INTENDED FOR DEBUGGING (%s_%s)", + envPrefix, envSelfhostedInsecure, + )) /// } @@ -214,6 +253,8 @@ func (o *Options) complete() { {envGCRAccessToken, &o.Client.GCR.Token}, + {envGHCRAccessToken, &o.Client.GHCR.Token}, + {envQuayToken, &o.Client.Quay.Token}, } { for _, env := range envs { @@ -275,11 +316,32 @@ func (o *Options) assignSelfhosted(envs []string) { continue } + if matches := selfhostedTokenPath.FindStringSubmatch(strings.ToUpper(pair[0])); len(matches) == 2 { + initOptions(matches[1]) + o.Client.Selfhosted[matches[1]].TokenPath = pair[1] + continue + } + if matches := selfhostedTokenReg.FindStringSubmatch(strings.ToUpper(pair[0])); len(matches) == 2 { initOptions(matches[1]) o.Client.Selfhosted[matches[1]].Bearer = pair[1] continue } + + if matches := selfhostedInsecureReg.FindStringSubmatch(strings.ToUpper(pair[0])); len(matches) == 2 { + initOptions(matches[1]) + val, err := strconv.ParseBool(pair[1]) + if err == nil { + o.Client.Selfhosted[matches[1]].Insecure = val + } + continue + } + + if matches := selfhostedCAPath.FindStringSubmatch(strings.ToUpper(pair[0])); len(matches) == 2 { + initOptions(matches[1]) + o.Client.Selfhosted[matches[1]].CAPath = pair[1] + continue + } } if len(o.selfhosted.Host) > 0 { diff --git a/cmd/app/options_test.go b/cmd/app/options_test.go index c00ebe03..d5e5a629 100644 --- a/cmd/app/options_test.go +++ b/cmd/app/options_test.go @@ -9,6 +9,7 @@ import ( "github.com/jetstack/version-checker/pkg/client/acr" "github.com/jetstack/version-checker/pkg/client/docker" "github.com/jetstack/version-checker/pkg/client/gcr" + "github.com/jetstack/version-checker/pkg/client/ghcr" "github.com/jetstack/version-checker/pkg/client/quay" "github.com/jetstack/version-checker/pkg/client/selfhosted" ) @@ -33,6 +34,7 @@ func TestComplete(t *testing.T) { {"VERSION_CHECKER_DOCKER_PASSWORD", "docker-password"}, {"VERSION_CHECKER_DOCKER_TOKEN", "docker-token"}, {"VERSION_CHECKER_GCR_TOKEN", "gcr-token"}, + {"VERSION_CHECKER_GHCR_TOKEN", "ghcr-token"}, {"VERSION_CHECKER_QUAY_TOKEN", "quay-token"}, {"VERSION_CHECKER_SELFHOSTED_HOST_FOO", "docker.joshvanl.com"}, {"VERSION_CHECKER_SELFHOSTED_USERNAME_FOO", "joshvanl"}, @@ -53,6 +55,9 @@ func TestComplete(t *testing.T) { GCR: gcr.Options{ Token: "gcr-token", }, + GHCR: ghcr.Options{ + Token: "ghcr-token", + }, Quay: quay.Options{ Token: "quay-token", }, @@ -62,6 +67,7 @@ func TestComplete(t *testing.T) { Username: "joshvanl", Password: "password", Bearer: "my-token", + Insecure: false, }, }, }, @@ -79,11 +85,19 @@ func TestComplete(t *testing.T) { {"VERSION_CHECKER_DOCKER_PASSWORD", "docker-password"}, {"VERSION_CHECKER_DOCKER_TOKEN", "docker-token"}, {"VERSION_CHECKER_GCR_TOKEN", "gcr-token"}, + {"VERSION_CHECKER_GHCR_TOKEN", "ghcr-token"}, {"VERSION_CHECKER_QUAY_TOKEN", "quay-token"}, {"VERSION_CHECKER_SELFHOSTED_HOST_FOO", "docker.joshvanl.com"}, {"VERSION_CHECKER_SELFHOSTED_USERNAME_FOO", "joshvanl"}, {"VERSION_CHECKER_SELFHOSTED_PASSWORD_FOO", "password"}, {"VERSION_CHECKER_SELFHOSTED_TOKEN_FOO", "my-token"}, + {"VERSION_CHECKER_SELFHOSTED_INSECURE_FOO", "true"}, + {"VERSION_CHECKER_SELFHOSTED_HOST_BUZZ", "buzz.docker.jetstack.io"}, + {"VERSION_CHECKER_SELFHOSTED_USERNAME_BUZZ", "buzz.davidcollom"}, + {"VERSION_CHECKER_SELFHOSTED_PASSWORD_BUZZ", "buzz-password"}, + {"VERSION_CHECKER_SELFHOSTED_TOKEN_BUZZ", "my-buzz-token"}, + {"VERSION_CHECKER_SELFHOSTED_INSECURE_BUZZ", "false"}, + {"VERSION_CHECKER_SELFHOSTED_CA_PATH_BUZZ", "/var/run/secrets/buzz/ca.crt"}, }, expOptions: client.Options{ ACR: acr.Options{ @@ -99,6 +113,9 @@ func TestComplete(t *testing.T) { GCR: gcr.Options{ Token: "gcr-token", }, + GHCR: ghcr.Options{ + Token: "ghcr-token", + }, Quay: quay.Options{ Token: "quay-token", }, @@ -108,12 +125,22 @@ func TestComplete(t *testing.T) { Username: "joshvanl", Password: "password", Bearer: "my-token", + Insecure: true, }, "BAR": &selfhosted.Options{ Host: "bar.docker.joshvanl.com", Username: "bar.joshvanl", Password: "bar-password", Bearer: "my-bar-token", + Insecure: false, + }, + "BUZZ": &selfhosted.Options{ + Host: "buzz.docker.jetstack.io", + Username: "buzz.davidcollom", + Password: "buzz-password", + Bearer: "my-buzz-token", + Insecure: false, + CAPath: "/var/run/secrets/buzz/ca.crt", }, }, }, @@ -193,6 +220,32 @@ func TestAssignSelfhosted(t *testing.T) { }, }, }, + "allow token path override": { + envs: []string{ + "VERSION_CHECKER_SELFHOSTED_HOST_FOO=docker.joshvanl.com", + "VERSION_CHECKER_SELFHOSTED_HOST_BAR=hello.world.com", + "VERSION_CHECKER_SELFHOSTED_USERNAME_FOO=joshvanl", + "VERSION_CHECKER_SELFHOSTED_PASSWORD_FOO=password", + "VERSION_CHECKER_SELFHOSTED_TOKEN_FOO=my-token", + "VERSION_CHECKER_SELFHOSTED_TOKEN_BAR=my-bar-token", + "VERSION_CHECKER_SELFHOSTED_TOKEN_PATH_FOO=/artifactory/api/security/token", + }, + expOptions: client.Options{ + Selfhosted: map[string]*selfhosted.Options{ + "FOO": &selfhosted.Options{ + Host: "docker.joshvanl.com", + Username: "joshvanl", + Password: "password", + Bearer: "my-token", + TokenPath: "/artifactory/api/security/token", + }, + "BAR": &selfhosted.Options{ + Host: "hello.world.com", + Bearer: "my-bar-token", + }, + }, + }, + }, "ignore keys with no values": { envs: []string{ "VERSION_CHECKER_SELFHOSTED_HOST_FOO=docker.joshvanl.com", diff --git a/deploy/charts/version-checker/Chart.yaml b/deploy/charts/version-checker/Chart.yaml index 3f46610a..fd0b60da 100644 --- a/deploy/charts/version-checker/Chart.yaml +++ b/deploy/charts/version-checker/Chart.yaml @@ -1,8 +1,8 @@ apiVersion: v1 -appVersion: "v0.3.0" -version: 0.3.0 +appVersion: "v0.5.1" +version: "v0.5.1" description: A Helm chart for version-checker -home: https://github.com/joshvanl/verison-checker +home: https://github.com/jetstack/version-checker name: version-checker maintainers: -- name: joshvanl + - name: davidcollom diff --git a/deploy/charts/version-checker/README.md b/deploy/charts/version-checker/README.md new file mode 100644 index 00000000..48811d5f --- /dev/null +++ b/deploy/charts/version-checker/README.md @@ -0,0 +1,61 @@ +# version-checker + +![Version: v0.5.1](https://img.shields.io/badge/Version-v0.5.1-informational?style=flat-square) ![AppVersion: v0.5.1](https://img.shields.io/badge/AppVersion-v0.5.1-informational?style=flat-square) + +A Helm chart for version-checker + +**Homepage:** + +## Maintainers + +| Name | Email | Url | +| ---- | ------ | --- | +| davidcollom | | | + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| acr | object | `{"password":null,"refreshToken":null,"username":null}` | Azure Container Registry Credentials Configuration | +| acr.password | string | `nil` | | +| acr.refreshToken | string | `nil` | | +| acr.username | string | `nil` | | +| additionalAnnotations | object | `{}` | | +| additionalLabels | object | `{}` | | +| docker.password | string | `nil` | | +| docker.token | string | `nil` | | +| docker.username | string | `nil` | | +| ecr.accessKeyID | string | `nil` | | +| ecr.iamRoleArn | string | `nil` | Provide AWS EKS Iam Role ARN following: [Specify A ServiceAccount Role](https://docs.aws.amazon.com/eks/latest/userguide/specify-service-account-role.html) | +| ecr.secretAccessKey | string | `nil` | | +| ecr.sessionToken | string | `nil` | | +| env | object | `{}` | Can be used to provide custom environment variables e.g. proxy settings | +| gcr.token | string | `nil` | | +| ghcr.token | string | `nil` | | +| image.pullPolicy | string | `"IfNotPresent"` | Set the Image Pull Policy | +| image.repository | string | `"quay.io/jetstack/version-checker"` | | +| image.tag | string | `nil` | Override the chart version | +| livenessProbe | object | `{"enabled":true,"httpGet":{"path":"/readyz","port":8080},"initialDelaySeconds":3,"periodSeconds":3}` | Configure the healthcheck probe for version-checker | +| livenessProbe.enabled | bool | `true` | Enable/Disable the setting of a livenessProbe | +| prometheus | object | `{"enabled":false,"replicas":1,"serviceAccountName":"prometheus"}` | Prometheus Operator | +| prometheus.enabled | bool | `false` | Deploy a Prometheus-Operator Prometheus Object to collect version-checker metrics | +| prometheus.serviceAccountName | string | `"prometheus"` | ServiceAccount for new Prometheus Object | +| quay.token | string | `nil` | | +| readinessProbe | object | `{"enabled":true,"httpGet":{"path":"/readyz","port":8080},"initialDelaySeconds":3,"periodSeconds":3}` | Configure the readiness probe for version-checker | +| readinessProbe.enabled | bool | `true` | Enable/Disable the setting of a readinessProbe | +| replicaCount | int | `1` | Replica Count for version-checker | +| resources | object | `{}` | Setup version-checkers resource requests/limits | +| selfhosted | []{name: "", host: "", username:"", password:"", token:""}] | `[]` | Setup a number of SelfHosted Repositories and their credentials | +| service | object | `{"annotations":{},"labels":{},"port":8080}` | Configure version-checkers Service | +| service.port | int | `8080` | Port to expose within the service | +| serviceMonitor | object | `{"additionalLabels":{},"enabled":false}` | Configure a Prometheus-Operator ServiceMonitor object | +| serviceMonitor.enabled | bool | `false` | Disable/Enable ServiceMonitor Object | +| tolerations | list | `[]` | Configure tolerations | +| versionChecker | object | `{"imageCacheTimeout":"30m","logLevel":"info","metricsServingAddress":"0.0.0.0:8080","testAllContainers":true}` | Configure version-checkers behaviour | +| versionChecker.imageCacheTimeout | string | `"30m"` | How long to hold on to image tags and their versions | +| versionChecker.logLevel | string | `"info"` | Configure version-checkers logging, valid options are: debug, info, warn, error, fatal, panic | +| versionChecker.metricsServingAddress | string | `"0.0.0.0:8080"` | Port/interface to which version-checker should bind too | +| versionChecker.testAllContainers | bool | `true` | Enable/Disable the requirement for an enable.version-checker.io annotation on pods. | + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.11.0](https://github.com/norwoodj/helm-docs/releases/v1.11.0) diff --git a/deploy/charts/version-checker/templates/_helpers.tpl b/deploy/charts/version-checker/templates/_helpers.tpl index 6ce5a80a..11f21aec 100644 --- a/deploy/charts/version-checker/templates/_helpers.tpl +++ b/deploy/charts/version-checker/templates/_helpers.tpl @@ -25,16 +25,3 @@ app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} app.kubernetes.io/managed-by: {{ .Release.Service }} {{- end -}} - -{{/* -Required claims serialized to CLI argument -*/}} -{{- define "requiredClaims" -}} -{{- if .Values.oidc.requiredClaims -}} -{{- $local := (list) -}} -{{- range $k, $v := .Values.oidc.requiredClaims -}} -{{- $local = (printf "%s=%s" $k $v | append $local) -}} -{{- end -}} -{{ join "," $local }} -{{- end -}} -{{- end -}} diff --git a/deploy/charts/version-checker/templates/deployment.yaml b/deploy/charts/version-checker/templates/deployment.yaml index 893c5d80..17866241 100644 --- a/deploy/charts/version-checker/templates/deployment.yaml +++ b/deploy/charts/version-checker/templates/deployment.yaml @@ -8,29 +8,40 @@ kind: Deployment metadata: name: {{ $chartname }} labels: -{{ include "version-checker.labels" . | indent 4 }} + {{ include "version-checker.labels" . | nindent 4 }} spec: replicas: {{ .Values.replicaCount }} selector: matchLabels: - app: {{ $chartname }} + {{- include "version-checker.labels" . | nindent 6 }} template: metadata: labels: - app: {{ $chartname }} + {{- include "version-checker.labels" . | nindent 8 }} + {{- if .Values.additionalLabels }} + {{ toYaml .Values.additionalLabels | nindent 8 }} + {{- end }} annotations: prometheus.io/path: "/metrics" prometheus.io/scrape: "true" prometheus.io/port: "8080" enable.version-checker.io/{{ $chartname }}: "true" + {{- if .Values.additionalAnnotations }} + {{ toYaml .Values.additionalAnnotations | nindent 8 }} + {{- end }} spec: serviceAccountName: {{ $chartname }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | trim | nindent 8 }} + {{- end }} containers: - name: {{ $chartname }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - - containerPort: 8080 + - name: metrics + containerPort: 8080 command: ["version-checker"] args: - "--image-cache-timeout={{.Values.versionChecker.imageCacheTimeout}}" @@ -39,8 +50,20 @@ spec: - "--test-all-containers={{.Values.versionChecker.testAllContainers}}" resources: {{- toYaml .Values.resources | nindent 12 }} + {{- if .Values.livenessProbe.enabled }} + livenessProbe: + {{- omit .Values.livenessProbe "enabled" | toYaml | nindent 10}} + {{- end }} + {{- if .Values.readinessProbe.enabled }} + readinessProbe: + {{- omit .Values.readinessProbe "enabled" | toYaml | nindent 10}} + {{- end }} + {{- if .Values.existingSecret }} + envFrom: + - secretRef: + name: {{.Values.existingSecret}} + {{- end }} env: - # ACR {{- if .Values.acr.refreshToken }} - name: VERSION_CHECKER_ACR_REFRESH_TOKEN @@ -96,6 +119,15 @@ spec: key: gcr.token {{- end }} + # GHCR + {{- if .Values.ghcr.token }} + - name: VERSION_CHECKER_GHCR_TOKEN + valueFrom: + secretKeyRef: + name: {{ $chartname }} + key: ghcr.token + {{- end }} + # Quay {{- if .Values.quay.token }} - name: VERSION_CHECKER_QUAY_TOKEN @@ -146,4 +178,3 @@ spec: secret: secretName: {{ include "version-checker.name" . }} {{ end }} - diff --git a/deploy/charts/version-checker/templates/prometheus.yaml b/deploy/charts/version-checker/templates/prometheus.yaml index 9c2f90e2..cede0a48 100644 --- a/deploy/charts/version-checker/templates/prometheus.yaml +++ b/deploy/charts/version-checker/templates/prometheus.yaml @@ -5,7 +5,7 @@ kind: ServiceAccount metadata: name: {{ .Values.prometheus.serviceAccountName }} --- -apiVersion: rbac.authorization.k8s.io/v1beta1 +apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: prometheus @@ -21,7 +21,7 @@ rules: - configmaps verbs: ["get"] --- -apiVersion: rbac.authorization.k8s.io/v1beta1 +apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: prometheus @@ -39,7 +39,7 @@ metadata: name: {{ include "version-checker.name" . }} labels: prometheus: {{ include "version-checker.name" . }} -{{ include "version-checker.labels" . | indent 4 }} + {{ include "version-checker.labels" . | nindent 4 }} spec: replicas: {{ .Values.prometheus.replicas }} serviceAccountName: {{ .Values.prometheus.serviceAccountName }} diff --git a/deploy/charts/version-checker/templates/secret.yaml b/deploy/charts/version-checker/templates/secret.yaml index 0bc3c45b..0e2c04a5 100644 --- a/deploy/charts/version-checker/templates/secret.yaml +++ b/deploy/charts/version-checker/templates/secret.yaml @@ -28,6 +28,11 @@ data: gcr.token: {{ .Values.gcr.token | b64enc }} {{- end}} + # GHCR + {{- if .Values.ghcr.token }} + ghcr.token: {{ .Values.ghcr.token | b64enc }} + {{- end}} + # Quay {{- if .Values.quay.token }} quay.token: {{ .Values.quay.token | b64enc }} diff --git a/deploy/charts/version-checker/templates/service.yaml b/deploy/charts/version-checker/templates/service.yaml index b7638263..a18d7137 100644 --- a/deploy/charts/version-checker/templates/service.yaml +++ b/deploy/charts/version-checker/templates/service.yaml @@ -4,7 +4,14 @@ metadata: name: {{ include "version-checker.name" . }} labels: app: {{ include "version-checker.name" . }} -{{ include "version-checker.labels" . | indent 4 }} + {{ include "version-checker.labels" . | nindent 4 }} + {{- if .Values.service.labels }} + {{ toYaml .Values.service.labels | nindent 4 }} + {{- end }} + {{- if .Values.service.annotations }} + annotations: + {{ toYaml .Values.service.annotations | nindent 4 }} + {{- end }} spec: ports: - port: {{ .Values.service.port }} @@ -12,4 +19,4 @@ spec: protocol: TCP name: web selector: - app: {{ include "version-checker.name" . }} + {{ include "version-checker.labels" . | nindent 4 }} diff --git a/deploy/charts/version-checker/templates/serviceaccount.yaml b/deploy/charts/version-checker/templates/serviceaccount.yaml index 75703c9c..d9e7cef3 100644 --- a/deploy/charts/version-checker/templates/serviceaccount.yaml +++ b/deploy/charts/version-checker/templates/serviceaccount.yaml @@ -1,6 +1,10 @@ apiVersion: v1 kind: ServiceAccount metadata: + {{- if .Values.ecr.iamRoleArn }} + annotations: + eks.amazonaws.com/role-arn: {{ .Values.ecr.iamRoleArn }} + {{- end }} labels: {{ include "version-checker.labels" . | indent 4 }} {{- with .Values.serviceAccount.annotations }} diff --git a/deploy/charts/version-checker/tests/deployment_test.yaml b/deploy/charts/version-checker/tests/deployment_test.yaml new file mode 100644 index 00000000..418bbd01 --- /dev/null +++ b/deploy/charts/version-checker/tests/deployment_test.yaml @@ -0,0 +1,418 @@ +suite: test deployment +templates: + - deployment.yaml +tests: + - it: should work (defaults) + set: + image.tag: latest + asserts: + - isKind: + of: Deployment + - equal: + path: metadata.name + value: version-checker + - equal: + path: spec.template.spec.containers[0].image + value: quay.io/jetstack/version-checker:latest + - equal: + path: spec.template.spec.containers[0].resources + value: {} + - equal: + path: spec.template.metadata.labels["app.kubernetes.io/name"] + value: version-checker + - equal: + path: spec.template.metadata.labels["app.kubernetes.io/instance"] + value: RELEASE-NAME + - equal: + path: spec.template.spec.containers[0].name + value: version-checker + - equal: + path: spec.template.spec.containers[0].livenessProbe + value: + httpGet: + path: /readyz + port: 8080 + initialDelaySeconds: 3 + periodSeconds: 3 + - equal: + path: spec.template.spec.containers[0].readinessProbe + value: + httpGet: + path: /readyz + port: 8080 + initialDelaySeconds: 3 + periodSeconds: 3 + - isNullOrEmpty: + path: spec.template.spec.volumes + + # Custom Envs + - it: Custom Env vars + set: + env: + - name: CUSTOM_ENV_VAR + value: CUSTOM_ENV_VALUE + asserts: + - contains: + path: spec.template.spec.containers[0].env + count: 1 + content: + name: CUSTOM_ENV_VAR + value: CUSTOM_ENV_VALUE + + # Param changes + - it: imageCacheTimeout + set: + versionChecker.imageCacheTimeout: 60m + asserts: + - contains: + path: spec.template.spec.containers[0].args + count: 1 + content: "--image-cache-timeout=60m" + + - it: logLevel + set: + versionChecker.logLevel: debug + asserts: + - contains: + path: spec.template.spec.containers[0].args + count: 1 + content: "--log-level=debug" + + - it: metricsServingAddress + set: + versionChecker.metricsServingAddress: 0.0.0.0:9999 + asserts: + - contains: + path: spec.template.spec.containers[0].args + count: 1 + content: "--metrics-serving-address=0.0.0.0:9999" + + - it: testAllContainers + set: + versionChecker.testAllContainers: false + asserts: + - contains: + path: spec.template.spec.containers[0].args + count: 1 + content: "--test-all-containers=false" + + # ACR + - it: ACR should work + set: + acr.refreshToken: ajbhvdsbjvh + acr.username: jsgbjkas + acr.password: sgkjnabskjga + asserts: + - contains: + path: spec.template.spec.containers[0].env + count: 1 + content: + name: VERSION_CHECKER_ACR_REFRESH_TOKEN + valueFrom: + secretKeyRef: + key: acr.refreshToken + name: version-checker + - contains: + path: spec.template.spec.containers[0].env + count: 1 + content: + name: VERSION_CHECKER_ACR_USERNAME + valueFrom: + secretKeyRef: + key: acr.username + name: version-checker + - contains: + path: spec.template.spec.containers[0].env + count: 1 + content: + name: VERSION_CHECKER_ACR_PASSWORD + valueFrom: + secretKeyRef: + key: acr.password + name: version-checker + + # ECR + - it: ECR should work + set: + ecr.iamRoleArn: ajbhvdsbjvh + ecr.accessKeyID: jsgbjkas + ecr.secretAccessKey: sgkjnabskjga + ecr.sessionToken: asgjasg + asserts: + - contains: + path: spec.template.spec.containers[0].env + count: 1 + content: + name: VERSION_CHECKER_ECR_IAM_ROLE_ARN + value: ajbhvdsbjvh + - contains: + path: spec.template.spec.containers[0].env + count: 1 + content: + name: VERSION_CHECKER_ECR_ACCESS_KEY_ID + valueFrom: + secretKeyRef: + key: ecr.accessKeyID + name: version-checker + - contains: + path: spec.template.spec.containers[0].env + count: 1 + content: + name: VERSION_CHECKER_ECR_SECRET_ACCESS_KEY + valueFrom: + secretKeyRef: + key: ecr.secretAccessKey + name: version-checker + - contains: + path: spec.template.spec.containers[0].env + count: 1 + content: + name: VERSION_CHECKER_ECR_SESSION_TOKEN + valueFrom: + secretKeyRef: + key: ecr.sessionToken + name: version-checker + + # Docker + - it: Docker should work + set: + docker.token: ajbhvdsbjvh + docker.username: username + docker.password: hunter1 + asserts: + - contains: + path: spec.template.spec.containers[0].env + count: 1 + content: + name: VERSION_CHECKER_DOCKER_TOKEN + valueFrom: + secretKeyRef: + key: docker.token + name: version-checker + - contains: + path: spec.template.spec.containers[0].env + count: 1 + content: + name: VERSION_CHECKER_DOCKER_USERNAME + valueFrom: + secretKeyRef: + key: docker.username + name: version-checker + - contains: + path: spec.template.spec.containers[0].env + count: 1 + content: + name: VERSION_CHECKER_DOCKER_PASSWORD + valueFrom: + secretKeyRef: + key: docker.password + name: version-checker + + # GCR + - it: GCR should work + set: + gcr.token: ajbhvdsbjvh + asserts: + - contains: + path: spec.template.spec.containers[0].env + count: 1 + content: + name: VERSION_CHECKER_GCR_TOKEN + valueFrom: + secretKeyRef: + key: gcr.token + name: version-checker + + # GHCR + - it: GHCR should work + set: + ghcr.token: ajbhvsagsagsdsbjvh + asserts: + - contains: + path: spec.template.spec.containers[0].env + count: 1 + content: + name: VERSION_CHECKER_GHCR_TOKEN + valueFrom: + secretKeyRef: + key: ghcr.token + name: version-checker + + # Quay + - it: Quay should work + set: + quay.token: ajbhvdsbjvh + asserts: + - contains: + path: spec.template.spec.containers[0].env + count: 1 + content: + name: VERSION_CHECKER_QUAY_TOKEN + valueFrom: + secretKeyRef: + key: quay.token + name: version-checker + + # Self Hosted + - it: Self hosted should work + set: + selfhosted: + - name: bob + host: http://example.com + username: asgasasf + password: hunter1 + asserts: + - contains: + path: spec.template.spec.containers[0].env + count: 1 + content: + name: VERSION_CHECKER_SELFHOSTED_HOST_bob + valueFrom: + secretKeyRef: + key: selfhosted.bob.host + name: version-checker + - contains: + path: spec.template.spec.containers[0].env + count: 1 + content: + name: VERSION_CHECKER_SELFHOSTED_USERNAME_bob + valueFrom: + secretKeyRef: + key: selfhosted.bob.username + name: version-checker + - contains: + path: spec.template.spec.containers[0].env + count: 1 + content: + name: VERSION_CHECKER_SELFHOSTED_PASSWORD_bob + valueFrom: + secretKeyRef: + key: selfhosted.bob.password + name: version-checker + - notContains: + path: spec.template.spec.containers[0].env + content: + name: VERSION_CHECKER_SELFHOSTED_TOKEN_bob + valueFrom: + secretKeyRef: + key: selfhosted.bob.password + name: version-checker + + # Multiple Self Hosted + - it: Multiple Self hosted should work + set: + selfhosted: + - name: bob + host: http://example.com + username: asgasasf + password: hunter1 + - name: bill + host: http://sub.example.com + token: askjgnasbjkgas + asserts: + - contains: + path: spec.template.spec.containers[0].env + count: 1 + content: + name: VERSION_CHECKER_SELFHOSTED_HOST_bob + valueFrom: + secretKeyRef: + key: selfhosted.bob.host + name: version-checker + - contains: + path: spec.template.spec.containers[0].env + count: 1 + content: + name: VERSION_CHECKER_SELFHOSTED_USERNAME_bob + valueFrom: + secretKeyRef: + key: selfhosted.bob.username + name: version-checker + - contains: + path: spec.template.spec.containers[0].env + count: 1 + content: + name: VERSION_CHECKER_SELFHOSTED_PASSWORD_bob + valueFrom: + secretKeyRef: + key: selfhosted.bob.password + name: version-checker + - notContains: + path: spec.template.spec.containers[0].env + content: + name: VERSION_CHECKER_SELFHOSTED_TOKEN_bob + valueFrom: + secretKeyRef: + key: selfhosted.bob.password + name: version-checker + - contains: + path: spec.template.spec.containers[0].env + content: + name: VERSION_CHECKER_SELFHOSTED_TOKEN_bill + valueFrom: + secretKeyRef: + key: selfhosted.bill.token + name: version-checker + - contains: + path: spec.template.spec.containers[0].env + content: + name: VERSION_CHECKER_SELFHOSTED_HOST_bill + valueFrom: + secretKeyRef: + key: selfhosted.bill.host + name: version-checker + + # Set Environment variables from existing secrets + - it: Existing Secret for Credentials + set: + existingSecret: preexistingsecret + asserts: + - contains: + path: spec.template.spec.containers[0].envFrom + count: 1 + content: + secretRef: + name: preexistingsecret + + - it: SecretEnabled + set: + acr.refreshToken: asgasga + asserts: + - contains: + path: spec.template.spec.volumes + content: + name: version-checker + secret: + secretName: version-checker + + - it: Resources are reflected + set: + resources: + limits: + cpu: 100m + memory: 512Mi + asserts: + - equal: + path: spec.template.spec.containers[0].resources + value: + limits: + cpu: 100m + memory: 512Mi + + - it: Custom Labels to Pod + set: + additionalLabels: + foo: bar + asserts: + - equal: + path: spec.template.metadata.labels["foo"] + value: bar + + - it: Custom Annotations to Pod + set: + additionalAnnotations: + foo: bar + asserts: + - equal: + path: spec.template.metadata.annotations["foo"] + value: bar diff --git a/deploy/charts/version-checker/tests/prometheus_test.yaml b/deploy/charts/version-checker/tests/prometheus_test.yaml new file mode 100644 index 00000000..e24035c9 --- /dev/null +++ b/deploy/charts/version-checker/tests/prometheus_test.yaml @@ -0,0 +1,68 @@ +suite: test prometheus +templates: + - prometheus.yaml +tests: + - it: should work (defaults) + asserts: + - hasDocuments: + count: 0 + + - it: should work (when enabled) + set: + prometheus.enabled: true + asserts: + - hasDocuments: + count: 4 + + - containsDocument: + kind: ServiceAccount + apiVersion: v1 + documentIndex: 0 + + - documentIndex: 1 + containsDocument: + kind: Role + apiVersion: rbac.authorization.k8s.io/v1 + + - documentIndex: 2 + containsDocument: + kind: RoleBinding + apiVersion: rbac.authorization.k8s.io/v1 + - documentIndex: 2 + equal: + path: subjects[0].kind + value: ServiceAccount + - documentIndex: 2 + equal: + path: subjects[0].name + value: prometheus + - documentIndex: 2 + equal: + path: roleRef.name + value: prometheus + - documentIndex: 2 + equal: + path: roleRef.kind + value: Role + + - documentIndex: 3 + containsDocument: + kind: Prometheus + apiVersion: monitoring.coreos.com/v1 + - documentIndex: 3 + equal: + path: spec.replicas + value: 1 + - documentIndex: 3 + equal: + path: spec.serviceAccountName + value: prometheus + - documentIndex: 3 + equal: + path: spec.version + value: v2.20.1 + - documentIndex: 3 + equal: + path: spec.serviceMonitorSelector.matchLabels + value: + app: version-checker diff --git a/deploy/charts/version-checker/tests/secret_test.yaml b/deploy/charts/version-checker/tests/secret_test.yaml new file mode 100644 index 00000000..c006d8a1 --- /dev/null +++ b/deploy/charts/version-checker/tests/secret_test.yaml @@ -0,0 +1,128 @@ +suite: test deployment +templates: + - secret.yaml +tests: + - it: should not be present (default) + asserts: + - hasDocuments: + count: 0 + + - it: Object Defaults + set: + acr.refreshToken: sakjgnsa + docker.token: sajkgnaskj + ecr.accessKeyID: "21412" + asserts: + - equal: + path: type + value: Opaque + - isNotEmpty: + path: metadata.labels + - containsDocument: + apiVersion: v1 + kind: Secret + name: version-checker + + - it: Empty SelfHosted list + set: + selfHosted: [] + asserts: + - hasDocuments: + count: 0 + + # ACR + - it: ACR + set: + acr.refreshToken: TOKEN + acr.username: USERNAME + acr.password: PASSWORD + asserts: + - hasDocuments: + count: 1 + - equal: + path: data["acr.refreshToken"] + value: VE9LRU4= + - equal: + path: data["acr.username"] + value: VVNFUk5BTUU= + - equal: + path: data["acr.password"] + value: UEFTU1dPUkQ= + + # Docker + - it: Docker + set: + docker.token: TOKEN + docker.username: USERNAME + docker.password: PASSWORD + asserts: + - hasDocuments: + count: 1 + - equal: + path: data["docker.token"] + value: VE9LRU4= + - equal: + path: data["docker.username"] + value: VVNFUk5BTUU= + - equal: + path: data["docker.password"] + value: UEFTU1dPUkQ= + + # GCR: + - it: GCR + set: + gcr.token: aasfas + asserts: + - hasDocuments: + count: 1 + - equal: + path: data["gcr.token"] + value: YWFzZmFz + + # GHCR: + - it: GHCR + set: + ghcr.token: aasfas + asserts: + - hasDocuments: + count: 1 + - equal: + path: data["ghcr.token"] + value: YWFzZmFz + + # SelfHosted: + - it: SelfHosted + set: + selfhosted: + - name: selfhosted1 + host: localhost:8000 + username: user + password: pass + - name: selfhosted2 + host: localhost:5000 + username: user2 + token: token + asserts: + - hasDocuments: + count: 1 + - equal: + path: data["selfhosted.selfhosted1.host"] + value: bG9jYWxob3N0OjgwMDA= + - equal: + path: data["selfhosted.selfhosted1.username"] + value: dXNlcg== + - equal: + path: data["selfhosted.selfhosted1.password"] + value: cGFzcw== + + - equal: + path: data["selfhosted.selfhosted2.host"] + value: bG9jYWxob3N0OjUwMDA= + - equal: + path: data["selfhosted.selfhosted2.token"] + value: dG9rZW4= + - equal: + path: data["selfhosted.selfhosted2.username"] + value: dXNlcjI= + - notExists: + path: data["selfhosted.selfhosted2.password"] diff --git a/deploy/charts/version-checker/tests/service_test.yaml b/deploy/charts/version-checker/tests/service_test.yaml new file mode 100644 index 00000000..397b92ba --- /dev/null +++ b/deploy/charts/version-checker/tests/service_test.yaml @@ -0,0 +1,45 @@ +suite: test service +templates: + - service.yaml +tests: + - it: should work (defaults) + asserts: + - containsDocument: + kind: Service + apiVersion: v1 + name: version-checker + - equal: + path: spec.ports[0].port + value: 8080 + - equal: + path: spec.ports[0].targetPort + value: 8080 + - equal: + path: spec.ports[0].name + value: web + + - it: Custom Port + set: + service.port: 9999 + asserts: + - equal: + path: spec.ports[0].port + value: 9999 + + - it: Custom Labels + set: + service.labels: + foo: bar + asserts: + - equal: + path: metadata.labels["foo"] + value: bar + + - it: Custom Annotations + set: + service.annotations: + foo: bar + asserts: + - equal: + path: metadata.annotations["foo"] + value: bar diff --git a/deploy/charts/version-checker/tests/serviceaccount_test.yaml b/deploy/charts/version-checker/tests/serviceaccount_test.yaml new file mode 100644 index 00000000..9ee79fe1 --- /dev/null +++ b/deploy/charts/version-checker/tests/serviceaccount_test.yaml @@ -0,0 +1,22 @@ +suite: test deployment +templates: + - serviceaccount.yaml +tests: + - it: should work (defaults) + set: + image.tag: latest + asserts: + - hasDocuments: + count: 1 + - containsDocument: + kind: ServiceAccount + apiVersion: v1 + name: version-checker + + - it: with ecr ARN Set + set: + ecr.iamRoleArn: dsjgabjgsg + asserts: + - equal: + path: metadata.annotations["eks.amazonaws.com/role-arn"] + value: dsjgabjgsg diff --git a/deploy/charts/version-checker/tests/servicemonitor_test.yaml b/deploy/charts/version-checker/tests/servicemonitor_test.yaml new file mode 100644 index 00000000..237ddfe7 --- /dev/null +++ b/deploy/charts/version-checker/tests/servicemonitor_test.yaml @@ -0,0 +1,35 @@ +suite: test ServiceMonitor +templates: + - servicemonitor.yaml +tests: + - it: should work (defaults) + asserts: + - hasDocuments: + count: 0 + + - it: works (when enabled) + set: + serviceMonitor.enabled: true + asserts: + - hasDocuments: + count: 1 + - containsDocument: + kind: ServiceMonitor + apiVersion: monitoring.coreos.com/v1 + name: version-checker + + - it: able to provide custom labels + set: + serviceMonitor.enabled: true + serviceMonitor.additionalLabels: + foo: bar + asserts: + - hasDocuments: + count: 1 + - containsDocument: + kind: ServiceMonitor + apiVersion: monitoring.coreos.com/v1 + name: version-checker + - equal: + path: metadata.labels["foo"] + value: bar diff --git a/deploy/charts/version-checker/values.yaml b/deploy/charts/version-checker/values.yaml index ac8f228d..ec488c53 100644 --- a/deploy/charts/version-checker/values.yaml +++ b/deploy/charts/version-checker/values.yaml @@ -2,46 +2,84 @@ # This is a YAML-formatted file. # Declare variables to be passed into your templates. +# -- Replica Count for version-checker replicaCount: 1 +# -- Additional Labels to apply to Service and Deployment/Pod Objects +additionalLabels: {} +# -- Additional Annotations to apply to Service and Deployment/Pod Objects +additionalAnnotations: {} + image: repository: quay.io/jetstack/version-checker - tag: v0.2.1 + # -- Override the chart version + tag: + # -- Set the Image Pull Policy pullPolicy: IfNotPresent +# -- Configure tolerations +tolerations: [] + +# -- Configure version-checkers Service service: + labels: {} + annotations: {} + # -- Port to expose within the service port: 8080 serviceAccount: annotations: {} +# -- Configure version-checkers behaviour versionChecker: + # versionChecker.imageCacheTimeout -- How long to hold on to image tags and their versions imageCacheTimeout: 30m - logLevel: info # debug, info, warn, error, fatal, panic + # versionChecker.logLevel -- Configure version-checkers logging, valid options are: debug, info, warn, error, fatal, panic + logLevel: info + # versionChecker.metricsServingAddress -- Port/interface to which version-checker should bind too metricsServingAddress: 0.0.0.0:8080 - testAllContainers: true # don't require the enable.version-checker.io annotation + # versionChecker.testAllContainers -- Enable/Disable the requirement for an enable.version-checker.io annotation on pods. + testAllContainers: true +# -- Azure Container Registry Credentials Configuration acr: + # acr.username -- (string) username: + # acr.password -- (string) password: + # acr.refreshToken -- (string) refreshToken: +# Docker Hub Credentials Configuration docker: + # docker.username -- (string) username: + # docker.password -- (string) password: + # docker.token -- (string) token: +# Google Container Registry Credentials Configuration gcr: + # gcr.token -- (string) + token: + +# Google Artifact Registry Credentials Configuration +ghcr: + # ghcr.token -- (string) token: +# Quay.io Registry Credentials Configuration quay: + # quay.token -- (string) token: -# Can be used to provide custom environment variables e.g. proxy settings +# -- Can be used to provide custom environment variables e.g. proxy settings env: {} +# -- ([]{name: "", host: "", username:"", password:"", token:""}]) Setup a number of SelfHosted Repositories and their credentials selfhosted: - {} + [] #- name: REGISTRY # host: http://registry:5000 # username: @@ -53,6 +91,7 @@ selfhosted: # password: bar # token: +# -- Setup version-checkers resource requests/limits resources: {} # limits: @@ -63,12 +102,36 @@ resources: # memory: 128Mi # -# Prometheus Operator +# -- Configure the readiness probe for version-checker +readinessProbe: + # readinessProbe.enabled -- Enable/Disable the setting of a readinessProbe + enabled: true + httpGet: + path: /readyz + port: 8080 + initialDelaySeconds: 3 + periodSeconds: 3 + +# -- Configure the healthcheck probe for version-checker +livenessProbe: + # livenessProbe.enabled -- Enable/Disable the setting of a livenessProbe + enabled: true + httpGet: + path: /readyz + port: 8080 + initialDelaySeconds: 3 + periodSeconds: 3 + +# -- Prometheus Operator prometheus: + # -- Deploy a Prometheus-Operator Prometheus Object to collect version-checker metrics enabled: false replicas: 1 + # -- ServiceAccount for new Prometheus Object serviceAccountName: prometheus +# -- Configure a Prometheus-Operator ServiceMonitor object serviceMonitor: + # serviceMonitor.enabled -- Disable/Enable ServiceMonitor Object enabled: false additionalLabels: {} diff --git a/deploy/yaml/deploy.yaml b/deploy/yaml/deploy.yaml index 58411425..b2eee8ba 100644 --- a/deploy/yaml/deploy.yaml +++ b/deploy/yaml/deploy.yaml @@ -49,12 +49,24 @@ spec: spec: serviceAccountName: version-checker containers: - - image: quay.io/jetstack/version-checker:v0.2.1 + - image: quay.io/jetstack/version-checker:v0.5.1 imagePullPolicy: Always ports: - containerPort: 8080 name: version-checker command: ["version-checker"] + livenessProbe: + httpGet: + path: /readyz + port: 8080 + initialDelaySeconds: 3 + periodSeconds: 3 + readinessProbe: + httpGet: + path: /readyz + port: 8080 + initialDelaySeconds: 3 + periodSeconds: 3 --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 diff --git a/go.mod b/go.mod index 3132f309..3ef45c12 100644 --- a/go.mod +++ b/go.mod @@ -1,29 +1,115 @@ module github.com/jetstack/version-checker -go 1.15 +go 1.20 + +// Do not remove this comment: +// please place any replace statements here at the top for visibility and add a +// comment to it as to when it can be removed require ( - cloud.google.com/go v0.57.0 // indirect - github.com/Azure/go-autorest/autorest v0.10.2 - github.com/Azure/go-autorest/autorest/adal v0.8.3 - github.com/aws/aws-sdk-go v1.34.10 - github.com/dgrijalva/jwt-go v3.2.0+incompatible - github.com/google/go-cmp v0.4.1 // indirect - github.com/hashicorp/golang-lru v0.5.3 // indirect - github.com/imdario/mergo v0.3.9 // indirect - github.com/kr/text v0.2.0 // indirect - github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect - github.com/prometheus/client_golang v1.7.1 - github.com/sirupsen/logrus v1.6.0 - github.com/spf13/cobra v1.0.0 + github.com/Azure/go-autorest/autorest v0.11.29 + github.com/Azure/go-autorest/autorest/adal v0.9.23 + github.com/aws/aws-sdk-go v1.47.1 + github.com/golang-jwt/jwt/v5 v5.0.0 + github.com/hashicorp/go-retryablehttp v0.7.4 + github.com/prometheus/client_golang v1.17.0 + github.com/sirupsen/logrus v1.9.3 + github.com/spf13/cobra v1.7.0 github.com/spf13/pflag v1.0.5 - golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect - gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect - gopkg.in/yaml.v2 v2.3.0 // indirect - k8s.io/api v0.19.0 - k8s.io/apimachinery v0.19.0 - k8s.io/cli-runtime v0.19.0 - k8s.io/client-go v0.19.0 - k8s.io/component-base v0.19.0 - k8s.io/utils v0.0.0-20200729134348-d5654de09c73 + golang.org/x/oauth2 v0.13.0 // indirect + k8s.io/api v0.28.3 + k8s.io/apimachinery v0.28.3 + k8s.io/cli-runtime v0.28.3 + k8s.io/client-go v0.28.3 + k8s.io/component-base v0.28.3 + k8s.io/utils v0.0.0-20230726121419-3b25d923346b +) + +require ( + github.com/gofri/go-github-ratelimit v1.1.0 + github.com/google/go-containerregistry v0.16.1 + github.com/google/go-github/v58 v58.0.0 + github.com/k0kubun/pp v3.0.1+incompatible +) + +require ( + github.com/Azure/go-autorest v14.2.0+incompatible // indirect + github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect + github.com/Azure/go-autorest/logger v0.2.1 // indirect + github.com/Azure/go-autorest/tracing v0.6.0 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/containerd/stargz-snapshotter/estargz v0.15.1 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/docker/cli v24.0.0+incompatible // indirect + github.com/docker/distribution v2.8.2+incompatible // indirect + github.com/docker/docker v24.0.0+incompatible // indirect + github.com/docker/docker-credential-helpers v0.7.0 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/evanphx/json-patch v5.7.0+incompatible // indirect + github.com/go-errors/errors v1.5.1 // indirect + github.com/go-logr/logr v1.3.0 // indirect + github.com/go-openapi/jsonpointer v0.20.0 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.4 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt/v4 v4.5.0 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/btree v1.1.2 // indirect + github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/go-querystring v1.1.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect + github.com/google/uuid v1.4.0 // indirect + github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/imdario/mergo v0.3.16 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect + github.com/klauspost/compress v1.17.2 // indirect + github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.0-rc3 // indirect + github.com/peterbourgon/diskv v2.0.1+incompatible // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.45.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + github.com/rogpeppe/go-internal v1.11.0 // indirect + github.com/vbatts/tar-split v0.11.5 // indirect + github.com/xlab/treeprint v1.2.0 // indirect + go.starlark.net v0.0.0-20231101134539-556fd59b42f6 // indirect + golang.org/x/crypto v0.17.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sync v0.4.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/term v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/time v0.3.0 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/evanphx/json-patch.v5 v5.7.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/klog/v2 v2.110.1 // indirect + k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/kustomize/api v0.15.0 // indirect + sigs.k8s.io/kustomize/kyaml v0.15.0 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index f8b4d210..42790188 100644 --- a/go.sum +++ b/go.sum @@ -1,606 +1,310 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.57.0 h1:EpMNVUorLiZIELdMZbCYX/ByTFCdoYopYAGxaGVz9ms= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= -github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630= -github.com/Azure/go-autorest/autorest v0.10.2 h1:NuSF3gXetiHyUbVdneJMEVyPUYAe5wh+aN08JYAf1tI= -github.com/Azure/go-autorest/autorest v0.10.2/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630= -github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= -github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= -github.com/Azure/go-autorest/autorest/adal v0.8.3 h1:O1AGG9Xig71FxdX9HO5pGNyZ7TbSyHaVg+5eJO/jSGw= -github.com/Azure/go-autorest/autorest/adal v0.8.3/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= -github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= -github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM= -github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= -github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc= -github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= -github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY= -github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= -github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k= -github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/aws/aws-sdk-go v1.34.10 h1:VU78gcf/3wA4HNEDCHidK738l7K0Bals4SJnfnvXOtY= -github.com/aws/aws-sdk-go v1.34.10/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw= +github.com/Azure/go-autorest/autorest v0.11.29/go.mod h1:ZtEzC4Jy2JDrZLxvWs8LrBWEBycl1hbT1eknI8MtfAs= +github.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk= +github.com/Azure/go-autorest/autorest/adal v0.9.23 h1:Yepx8CvFxwNKpH6ja7RZ+sKX+DWYNldbLiALMC3BTz8= +github.com/Azure/go-autorest/autorest/adal v0.9.23/go.mod h1:5pcMqFkdPhviJdlEy3kC/v1ZLnQl0MH6XA5YCcMhy4c= +github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= +github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= +github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/aws/aws-sdk-go v1.47.1 h1:j9ih0Ashcw8tQcnfqNimBM8ARQ/CMpoBwjKue1D6Fuk= +github.com/aws/aws-sdk-go v1.47.1/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/containerd/stargz-snapshotter/estargz v0.15.1 h1:eXJjw9RbkLFgioVaTG+G/ZW/0kEe2oEKCdS/ZxIyoCU= +github.com/containerd/stargz-snapshotter/estargz v0.15.1/go.mod h1:gr2RNwukQ/S9Nv33Lt6UC7xEx58C+LHRdoqbEKjz1Kk= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= -github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses= -github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-logr/logr v0.2.0 h1:QvGt2nLcHH0WK9orKa+ppBPAxREcH364nPUedEpK0TY= -github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= -github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= -github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o= -github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/spec v0.19.3 h1:0XRyw8kguri6Yw4SxhsQA/atC88yqrk0+G4YhI2wabc= -github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= -github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1 h1:/exdXoGamhu5ONeUJH0deniYLWYvQwW66yvlfiiKTu0= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/docker/cli v24.0.0+incompatible h1:0+1VshNwBQzQAx9lOl+OYCTCEAD8fKs/qeXMx3O0wqM= +github.com/docker/cli v24.0.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= +github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v24.0.0+incompatible h1:z4bf8HvONXX9Tde5lGBMQ7yCJgNahmJumdrStZAbeY4= +github.com/docker/docker v24.0.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= +github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI= +github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= +github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= +github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/gofri/go-github-ratelimit v1.1.0 h1:ijQ2bcv5pjZXNil5FiwglCg8wc9s8EgjTmNkqjw8nuk= +github.com/gofri/go-github-ratelimit v1.1.0/go.mod h1:OnCi5gV+hAG/LMR7llGhU7yHt44se9sYgKPnafoL7RY= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= +github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= +github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 h1:0VpGH+cDhbDtdcweoyCVsF3fhN8kejK6rFe/2FFX2nU= +github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49/go.mod h1:BkkQ4L1KS1xMt2aWSPStnn55ChGC0DPOn2FQYj+f25M= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-containerregistry v0.16.1 h1:rUEt426sR6nyrL3gt+18ibRcvYpKYdpsa5ZW7MA08dQ= +github.com/google/go-containerregistry v0.16.1/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ= +github.com/google/go-github/v58 v58.0.0 h1:Una7GGERlF/37XfkPwpzYJe0Vp4dt2k1kCjlxwjIvzw= +github.com/google/go-github/v58 v58.0.0/go.mod h1:k4hxDKEfoWpSqFlc8LTpGd9fu2KrV1YAa6Hi6FmDNY4= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gnostic v0.4.1 h1:DLJCy1n/vrD4HPjOvYcT8aYQXpPIzoRZONaYwyycI+I= -github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= -github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg= -github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc= -github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= +github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= +github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA= +github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM= +github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= +github.com/k0kubun/pp v3.0.1+incompatible h1:3tqvf7QgUnZ5tXO6pNAZlrvHgl6DvifjDrd9g2S9Z40= +github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4= +github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= -github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= -github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= +github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/onsi/ginkgo/v2 v2.9.4 h1:xR7vG4IXt5RWx6FfIjyAtsoMAtnc3C/rFXBBd2AjZwE= +github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8= +github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinCts= +github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk= +github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= +github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.starlark.net v0.0.0-20231101134539-556fd59b42f6 h1:+eC0F/k4aBLC4szgOcjd7bDTEnpxADJyWJE0yowgM3E= +go.starlark.net v0.0.0-20231101134539-556fd59b42f6/go.mod h1:LcLNIzVOMp4oV+uusnpk+VU+SzXaJakUuBjoCSWH5dM= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY= +golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= +golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4 h1:5/PjkGUjvEU5Gl6BxmvKRPpqo2uNMv4rcHBMwzk/st8= -golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI= -golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/evanphx/json-patch.v5 v5.7.0 h1:dGKGylPlZ/jus2g1YqhhyzfH0gPy2R8/MYUpW/OslTY= +gopkg.in/evanphx/json-patch.v5 v5.7.0/go.mod h1:/kvTRh1TVm5wuM6OkHxqXtE/1nUZZpihg29RtuIyfvk= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.19.0 h1:XyrFIJqTYZJ2DU7FBE/bSPz7b1HvbVBuBf07oeo6eTc= -k8s.io/api v0.19.0/go.mod h1:I1K45XlvTrDjmj5LoM5LuP/KYrhWbjUKT/SoPG0qTjw= -k8s.io/apimachinery v0.19.0 h1:gjKnAda/HZp5k4xQYjL0K/Yb66IvNqjthCb03QlKpaQ= -k8s.io/apimachinery v0.19.0/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= -k8s.io/cli-runtime v0.19.0 h1:wLe+osHSqcItyS3MYQXVyGFa54fppORVA8Jn7DBGSWw= -k8s.io/cli-runtime v0.19.0/go.mod h1:tun9l0eUklT8IHIM0jors17KmUjcrAxn0myoBYwuNuo= -k8s.io/client-go v0.19.0 h1:1+0E0zfWFIWeyRhQYWzimJOyAk2UT7TiARaLNwJCf7k= -k8s.io/client-go v0.19.0/go.mod h1:H9E/VT95blcFQnlyShFgnFT9ZnJOAceiUHM3MlRC+mU= -k8s.io/component-base v0.19.0 h1:OueXf1q3RW7NlLlUCj2Dimwt7E1ys6ZqRnq53l2YuoE= -k8s.io/component-base v0.19.0/go.mod h1:dKsY8BxkA+9dZIAh2aWJLL/UdASFDNtGYTCItL4LM7Y= -k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.2.0 h1:XRvcwJozkgZ1UQJmfMGpvRthQHOvihEhYtDfAaxMz/A= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6 h1:+WnxoVtG8TMiudHBSEtrVL1egv36TkkJm+bA8AxicmQ= -k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= -k8s.io/utils v0.0.0-20200729134348-d5654de09c73 h1:uJmqzgNWG7XyClnU/mLPBWwfKKF1K8Hf8whTseBgJcg= -k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0= -sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= -sigs.k8s.io/structured-merge-diff/v4 v4.0.1 h1:YXTMot5Qz/X1iBRJhAt+vI+HVttY0WkSqqhKxQ0xVbA= -sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= +k8s.io/api v0.28.3 h1:Gj1HtbSdB4P08C8rs9AR94MfSGpRhJgsS+GF9V26xMM= +k8s.io/api v0.28.3/go.mod h1:MRCV/jr1dW87/qJnZ57U5Pak65LGmQVkKTzf3AtKFHc= +k8s.io/apimachinery v0.28.3 h1:B1wYx8txOaCQG0HmYF6nbpU8dg6HvA06x5tEffvOe7A= +k8s.io/apimachinery v0.28.3/go.mod h1:uQTKmIqs+rAYaq+DFaoD2X7pcjLOqbQX2AOiO0nIpb8= +k8s.io/cli-runtime v0.28.3 h1:lvuJYVkwCqHEvpS6KuTZsUVwPePFjBfSGvuaLl2SxzA= +k8s.io/cli-runtime v0.28.3/go.mod h1:jeX37ZPjIcENVuXDDTskG3+FnVuZms5D9omDXS/2Jjc= +k8s.io/client-go v0.28.3 h1:2OqNb72ZuTZPKCl+4gTKvqao0AMOl9f3o2ijbAj3LI4= +k8s.io/client-go v0.28.3/go.mod h1:LTykbBp9gsA7SwqirlCXBWtK0guzfhpoW4qSm7i9dxo= +k8s.io/component-base v0.28.3 h1:rDy68eHKxq/80RiMb2Ld/tbH8uAE75JdCqJyi6lXMzI= +k8s.io/component-base v0.28.3/go.mod h1:fDJ6vpVNSk6cRo5wmDa6eKIG7UlIQkaFmZN2fYgIUD8= +k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= +k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= +k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= +k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/kustomize/api v0.15.0 h1:6Ca88kEOBVotHDw+y2IsIMYtg9Pvv7MKpW9JMyF/OH4= +sigs.k8s.io/kustomize/api v0.15.0/go.mod h1:p19kb+E14gN7zcIBR/nhByJDAfUa7N8mp6ZdH/mMXbg= +sigs.k8s.io/kustomize/kyaml v0.15.0 h1:ynlLMAxDhrY9otSg5GYE2TcIz31XkGZ2Pkj7SdolD84= +sigs.k8s.io/kustomize/kyaml v0.15.0/go.mod h1:+uMkBahdU1KNOj78Uta4rrXH+iH7wvg+nW7+GULvREA= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/pkg/api/types.go b/pkg/api/types.go index 0c347b08..b7a6cbb0 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -62,9 +62,12 @@ type Options struct { // ImageTag describes a container image tag. type ImageTag struct { - Tag string `json:"tag"` - SHA string `json:"sha"` - Timestamp time.Time `json:"timestamp"` - Architecture string `json:"architecture,omitempty"` - OS string `json:"os,omitempty"` + Tag string `json:"tag"` + SHA string `json:"sha"` + Timestamp time.Time `json:"timestamp"` + OS OS `json:"os,omitempty"` + Architecture Architecture `json:"architecture,omitempty"` } + +type OS string +type Architecture string diff --git a/pkg/client/acr/acr.go b/pkg/client/acr/acr.go index 41971198..de003b6d 100644 --- a/pkg/client/acr/acr.go +++ b/pkg/client/acr/acr.go @@ -5,14 +5,14 @@ import ( "encoding/json" "errors" "fmt" - "io/ioutil" + "io" "net/http" "sync" "time" "github.com/Azure/go-autorest/autorest" "github.com/Azure/go-autorest/autorest/adal" - "github.com/dgrijalva/jwt-go" + jwt "github.com/golang-jwt/jwt/v5" "github.com/jetstack/version-checker/pkg/api" "github.com/jetstack/version-checker/pkg/client/util" @@ -55,7 +55,8 @@ type ACRManifestResponse struct { func New(opts Options) (*Client, error) { client := &http.Client{ - Timeout: time.Second * 5, + Timeout: time.Second * 5, + Transport: &http.Transport{Proxy: http.ProxyFromEnvironment}, } if len(opts.RefreshToken) > 0 && @@ -140,7 +141,7 @@ func (c *Client) getManifestsWithClient(ctx context.Context, client *acrClient, } if resp.StatusCode != 200 { - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("bad request for image host %s", host) } @@ -241,15 +242,18 @@ func (c *Client) getAccessTokenClient(ctx context.Context, host string) (*acrCli }, nil } -func getTokenExpiration(token string) (time.Time, error) { - parser := jwt.Parser{SkipClaimsValidation: true} +func getTokenExpiration(tokenString string) (time.Time, error) { - claims := make(jwt.MapClaims) - _, _, err := parser.ParseUnverified(token, claims) + token, err := jwt.Parse(tokenString, nil, jwt.WithoutClaimsValidation()) if err != nil { return time.Time{}, err } + claims, ok := token.Claims.(jwt.MapClaims) + if !ok { + return time.Time{}, fmt.Errorf("failed to process claims in access token") + } + if exp, ok := claims["exp"].(float64); ok { timestamp := time.Unix(int64(exp), 0) return timestamp, nil diff --git a/pkg/client/client.go b/pkg/client/client.go index 97548044..f34798d5 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -12,6 +12,7 @@ import ( "github.com/jetstack/version-checker/pkg/client/docker" "github.com/jetstack/version-checker/pkg/client/ecr" "github.com/jetstack/version-checker/pkg/client/gcr" + "github.com/jetstack/version-checker/pkg/client/ghcr" "github.com/jetstack/version-checker/pkg/client/quay" "github.com/jetstack/version-checker/pkg/client/selfhosted" ) @@ -46,6 +47,7 @@ type Client struct { type Options struct { ACR acr.Options GCR gcr.Options + GHCR ghcr.Options Docker docker.Options Quay quay.Options Selfhosted map[string]*selfhosted.Options @@ -84,6 +86,7 @@ func New(ctx context.Context, log *logrus.Entry, opts Options) (*Client, error) ecr.New(), dockerClient, gcr.New(opts.GCR), + ghcr.New(opts.GHCR), quay.New(opts.Quay), ), fallbackClient: fallbackClient, @@ -125,6 +128,6 @@ func (c *Client) fromImageURL(imageURL string) (ImageClient, string, string) { } } - // fall back to docker with no path split + // fall back to selfhosted with no path split return c.fallbackClient, host, path } diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go index 72ca7e68..4c931003 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -11,6 +11,7 @@ import ( "github.com/jetstack/version-checker/pkg/client/docker" "github.com/jetstack/version-checker/pkg/client/ecr" "github.com/jetstack/version-checker/pkg/client/gcr" + "github.com/jetstack/version-checker/pkg/client/ghcr" "github.com/jetstack/version-checker/pkg/client/quay" "github.com/jetstack/version-checker/pkg/client/selfhosted" ) @@ -120,6 +121,31 @@ func TestFromImageURL(t *testing.T) { expHost: "us.gcr.io", expPath: "k8s-artifacts-prod/ingress-nginx/nginx", }, + "k8s.io should be gcr": { + url: "k8s.io/sig-storage/csi-node-driver-registrar", + expClient: new(gcr.Client), + expHost: "k8s.io", + expPath: "sig-storage/csi-node-driver-registrar", + }, + "k8s.io with subdomain should be gcr": { + url: "registry.k8s.io/sig-storage/csi-node-driver-registrar", + expClient: new(gcr.Client), + expHost: "registry.k8s.io", + expPath: "sig-storage/csi-node-driver-registrar", + }, + + "ghcr.io should be ghcr": { + url: "ghcr.io/jetstack/version-checker", + expClient: new(ghcr.Client), + expHost: "ghcr.io", + expPath: "jetstack/version-checker", + }, + "gcr.io with subdomain should be ghcr": { + url: "ghcr.io/k8s-artifacts-prod/ingress-nginx/nginx", + expClient: new(ghcr.Client), + expHost: "ghcr.io", + expPath: "k8s-artifacts-prod/ingress-nginx/nginx", + }, "quay.io should be quay": { url: "quay.io/jetstack/version-checker", diff --git a/pkg/client/docker/docker.go b/pkg/client/docker/docker.go index 36ded892..9dcdc364 100644 --- a/pkg/client/docker/docker.go +++ b/pkg/client/docker/docker.go @@ -5,7 +5,7 @@ import ( "encoding/json" "errors" "fmt" - "io/ioutil" + "io" "net/http" "strings" "time" @@ -15,7 +15,7 @@ import ( const ( loginURL = "https://hub.docker.com/v2/users/login/" - lookupURL = "https://registry.hub.docker.com/v2/repositories/%s/%s/tags" + lookupURL = "https://registry.hub.docker.com/v2/repositories/%s/%s/tags?page_size=100" ) type Options struct { @@ -45,14 +45,15 @@ type Result struct { } type Image struct { - Digest string `json:"digest"` - OS string `json:"os"` - Architecture string `json:"Architecture"` + Digest string `json:"digest"` + OS api.OS `json:"os"` + Architecture api.Architecture `json:"Architecture"` } func New(ctx context.Context, opts Options) (*Client, error) { client := &http.Client{ - Timeout: time.Second * 5, + Timeout: time.Second * 10, + Transport: &http.Transport{Proxy: http.ProxyFromEnvironment}, } // Setup Auth if username and password used. @@ -94,9 +95,12 @@ func (c *Client) Tags(ctx context.Context, _, repo, image string) ([]api.ImageTa continue } - timestamp, err := time.Parse(time.RFC3339Nano, result.Timestamp) - if err != nil { - return nil, fmt.Errorf("failed to parse image timestamp: %s", err) + var timestamp time.Time + if len(result.Timestamp) > 0 { + timestamp, err = time.Parse(time.RFC3339Nano, result.Timestamp) + if err != nil { + return nil, fmt.Errorf("failed to parse image timestamp: %s", err) + } } for _, image := range result.Images { @@ -130,7 +134,7 @@ func (c *Client) doRequest(ctx context.Context, url string) (*TagResponse, error req.URL.Scheme = "https" req = req.WithContext(ctx) if len(c.Token) > 0 { - req.Header.Add("Authorization", "Token "+c.Token) + req.Header.Add("Authorization", "Bearer "+c.Token) } resp, err := c.Do(req) @@ -138,7 +142,7 @@ func (c *Client) doRequest(ctx context.Context, url string) (*TagResponse, error return nil, fmt.Errorf("failed to get docker image: %s", err) } - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { return nil, err } @@ -171,7 +175,7 @@ func basicAuthSetup(ctx context.Context, client *http.Client, opts Options) (str return "", err } - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { return "", err } diff --git a/pkg/client/ecr/ecr.go b/pkg/client/ecr/ecr.go index 1f43c80c..cf574150 100644 --- a/pkg/client/ecr/ecr.go +++ b/pkg/client/ecr/ecr.go @@ -3,6 +3,7 @@ package ecr import ( "context" "fmt" + "net/http" "sync" "github.com/aws/aws-sdk-go/aws" @@ -91,6 +92,8 @@ func (c *Client) getClient(region string) (*ecr.ECR, error) { return nil, err } } + // Try and use an HTTP(S) Proxies defined within Environment variables. + client.Config.WithHTTPClient(&http.Client{Transport: &http.Transport{Proxy: http.ProxyFromEnvironment}}) c.cachedRegionClients[region] = client return client, nil diff --git a/pkg/client/gcr/gcr.go b/pkg/client/gcr/gcr.go index ff73bda5..d94a2ba0 100644 --- a/pkg/client/gcr/gcr.go +++ b/pkg/client/gcr/gcr.go @@ -4,7 +4,7 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strconv" "time" @@ -35,11 +35,14 @@ type ManifestItem struct { } func New(opts Options) *Client { + client := &http.Client{ + Timeout: time.Second * 5, + Transport: &http.Transport{Proxy: http.ProxyFromEnvironment}, + } + return &Client{ Options: opts, - Client: &http.Client{ - Timeout: time.Second * 5, - }, + Client: client, } } @@ -70,7 +73,7 @@ func (c *Client) Tags(ctx context.Context, host, repo, image string) ([]api.Imag return nil, fmt.Errorf("failed to get docker image: %s", err) } - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { return nil, err } diff --git a/pkg/client/gcr/path.go b/pkg/client/gcr/path.go index c6d49abf..3c585ffb 100644 --- a/pkg/client/gcr/path.go +++ b/pkg/client/gcr/path.go @@ -6,7 +6,7 @@ import ( ) var ( - reg = regexp.MustCompile(`(^(.*\.)?gcr.io$)`) + reg = regexp.MustCompile(`(^(.*\.)?gcr.io$|^(.*\.)?k8s.io$|^(.+)-docker.pkg.dev$)`) ) func (c *Client) IsHost(host string) bool { diff --git a/pkg/client/gcr/path_test.go b/pkg/client/gcr/path_test.go index e40dc424..703c59a7 100644 --- a/pkg/client/gcr/path_test.go +++ b/pkg/client/gcr/path_test.go @@ -35,6 +35,26 @@ func TestIsHost(t *testing.T) { host: "gcr.iofoo", expIs: false, }, + "just pkg.dev should be false": { + host: "pkg.dev", + expIs: false, + }, + "docker.pkg.dev with random sub domains should be false": { + host: "docker.pkg.dev", + expIs: false, + }, + "eu-docker.pkg.dev with random sub domains should be true": { + host: "eu-docker.pkg.dev", + expIs: true, + }, + "k8s.io should be true": { + host: "k8s.io", + expIs: true, + }, + "registry.k8s.io should be true": { + host: "registry.k8s.io", + expIs: true, + }, } handler := new(Client) diff --git a/pkg/client/ghcr/ghcr.go b/pkg/client/ghcr/ghcr.go new file mode 100644 index 00000000..fbbfd7f6 --- /dev/null +++ b/pkg/client/ghcr/ghcr.go @@ -0,0 +1,145 @@ +package ghcr + +import ( + "context" + "fmt" + "net/http" + "strings" + "time" + + "github.com/gofri/go-github-ratelimit/github_ratelimit" + "github.com/google/go-github/v58/github" + "github.com/jetstack/version-checker/pkg/api" +) + +type Options struct { + Token string +} + +type Client struct { + *http.Client + Options +} + +func New(opts Options) *Client { + return &Client{ + Options: opts, + Client: &http.Client{ + Timeout: time.Second * 5, + Transport: &http.Transport{Proxy: http.ProxyFromEnvironment}, + }, + } +} + +func (c *Client) Name() string { + return "ghcr" +} + +func (c *Client) Tags(ctx context.Context, host, owner, repo string) ([]api.ImageTag, error) { + var err error + var tags []api.ImageTag + + rateLimitDetection := func(ctx *github_ratelimit.CallbackContext) { + fmt.Printf("Hit Github Rate Limit, sleeping for %v", ctx.TotalSleepTime) + } + + ghRatelimitOpts := github_ratelimit.WithLimitDetectedCallback(rateLimitDetection) + ghRateLimiter, err := github_ratelimit.NewRateLimitWaiterClient(nil, ghRatelimitOpts) + if err != nil { + panic(err) + } + client := github.NewClient(ghRateLimiter).WithAuthToken(c.Token) + + if repoExist(client, owner, repo) { + tags, err = getTagsFromRelease(client, owner, repo) + if err != nil { + return nil, err + } + } else { + + tags, err = getTagsFromOrgPackage(client, owner, repo) + if err != nil { + return nil, err + } + } + return tags, nil +} + +func repoExist(client *github.Client, owner string, repo string) bool { + _, _, err := client.Repositories.Get(context.TODO(), owner, repo) + return err == nil +} + +func getTagsFromOrgPackage(client *github.Client, owner string, repo string) ([]api.ImageTag, error) { + var tags []api.ImageTag + packageType := "container" + packageState := "active" + opts := &github.PackageListOptions{ + PackageType: &packageType, + State: &packageState, + ListOptions: github.ListOptions{PerPage: 100}, + } + + for { + versions, resp, err := client.Organizations.PackageGetAllVersions(context.TODO(), owner, packageType, repo, opts) + if err != nil { + return nil, fmt.Errorf("failed to get Org Package Versions: %s", err) + } + + for _, ver := range versions { + if len(ver.Metadata.Container.Tags) == 0 { + continue + } + + sha := "" + if strings.HasPrefix(*ver.Name, "sha") { + sha = *ver.Name + } + + for _, tag := range ver.Metadata.Container.Tags { + if strings.HasSuffix(tag, ".att") { // Skip tags that are attestations + continue + } + + tags = append(tags, api.ImageTag{ + Tag: tag, + SHA: sha, + Timestamp: ver.CreatedAt.Time, + }) + } + } + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + return tags, nil +} + +func getTagsFromRelease(client *github.Client, owner string, repo string) ([]api.ImageTag, error) { + var tags []api.ImageTag + opt := &github.ListOptions{PerPage: 50} + for { + releases, resp, err := client.Repositories.ListReleases(context.TODO(), owner, repo, opt) + if err != nil { + return nil, fmt.Errorf("failed to get github Releases: %s", err) + } + + for _, rel := range releases { + if rel.TagName == nil { + continue + } + tags = append(tags, api.ImageTag{ + Tag: *rel.TagName, + SHA: "", + Timestamp: rel.PublishedAt.Time, + }) + } + + if resp.NextPage == 0 { + break + } + opt.Page = resp.NextPage + } + return tags, nil +} diff --git a/pkg/client/ghcr/path.go b/pkg/client/ghcr/path.go new file mode 100644 index 00000000..f26586ef --- /dev/null +++ b/pkg/client/ghcr/path.go @@ -0,0 +1,20 @@ +package ghcr + +import ( + "regexp" + "strings" +) + +var ( + reg = regexp.MustCompile(`^ghcr.io$`) +) + +func (c *Client) IsHost(host string) bool { + return reg.MatchString(host) +} + +func (c *Client) RepoImageFromPath(path string) (string, string) { + lastIndex := strings.LastIndex(path, "/") + + return path[:lastIndex], path[lastIndex+1:] +} diff --git a/pkg/client/ghcr/path_test.go b/pkg/client/ghcr/path_test.go new file mode 100644 index 00000000..cfec82c2 --- /dev/null +++ b/pkg/client/ghcr/path_test.go @@ -0,0 +1,78 @@ +package ghcr + +import "testing" + +func TestIsHost(t *testing.T) { + tests := map[string]struct { + host string + expIs bool + }{ + "an empty host should be false": { + host: "", + expIs: false, + }, + "random string should be false": { + host: "foobar", + expIs: false, + }, + "random string with dots should be false": { + host: "foobar.foo", + expIs: false, + }, + "just ghcr.io should be true": { + host: "ghcr.io", + expIs: true, + }, + "gcr.io with random sub domains should be false": { + host: "ghcr.gcr.io", + expIs: false, + }, + "foodghcr.io should be false": { + host: "foodghcr.io", + expIs: false, + }, + "ghcr.iofoo should be false": { + host: "ghcr.iofoo", + expIs: false, + }, + } + + handler := new(Client) + for name, test := range tests { + t.Run(name, func(t *testing.T) { + if isHost := handler.IsHost(test.host); isHost != test.expIs { + t.Errorf("%s: unexpected IsHost, exp=%t got=%t", + test.host, test.expIs, isHost) + } + }) + } +} + +func TestRepoImage(t *testing.T) { + tests := map[string]struct { + path string + expRepo, expImage string + }{ + "two segments to path should return both": { + path: "jetstack-cre/version-checker", + expRepo: "jetstack-cre", + expImage: "version-checker", + }, + "multiple segments to path should return all in repo, last segment image": { + path: "k8s-artifacts-prod/ingress-nginx/nginx", + expRepo: "k8s-artifacts-prod/ingress-nginx", + expImage: "nginx", + }, + } + + handler := new(Client) + for name, test := range tests { + t.Run(name, func(t *testing.T) { + repo, image := handler.RepoImageFromPath(test.path) + if repo != test.expRepo && image != test.expImage { + t.Errorf("%s: unexpected repo/image, exp=%s/%s got=%s/%s", + test.path, test.expRepo, test.expImage, repo, image) + } + }) + } +} diff --git a/pkg/client/quay/pager.go b/pkg/client/quay/pager.go new file mode 100644 index 00000000..22a1a678 --- /dev/null +++ b/pkg/client/quay/pager.go @@ -0,0 +1,86 @@ +package quay + +import ( + "context" + "fmt" + "sync" + + "github.com/jetstack/version-checker/pkg/api" +) + +// pager is used for implementing the paging mechanism for fetching image tags +type pager struct { + *Client + + repo, image string + + mu sync.Mutex + wg sync.WaitGroup + + tags []api.ImageTag + errs []error +} + +func (c *Client) newPager(repo, image string) *pager { + return &pager{ + Client: c, + repo: repo, + image: image, + } +} + +func (p *pager) fetchTags(ctx context.Context) error { + var ( + page = 1 + hasAdditional = true + err error + ) + + // Need to set a fair page limit to handle some registries + for hasAdditional && page < 60 { + // Fetch all image tags in this page + hasAdditional, err = p.fetchTagsPaged(ctx, page) + if err != nil { + return err + } + + page++ + } + + p.wg.Wait() + + return nil +} + +// fetchTagsPaged will return the image tags from a given page number, as well +// as if there are more pages +func (p *pager) fetchTagsPaged(ctx context.Context, page int) (bool, error) { + url := fmt.Sprintf(tagURL, p.repo, p.image, page) + var resp responseTag + if err := p.makeRequest(ctx, url, &resp); err != nil { + return false, err + } + + p.wg.Add(len(resp.Tags)) + + // Concurrently fetch all images from a given tag + for i := range resp.Tags { + go func(i int) { + defer p.wg.Done() + + imageTags, err := p.fetchImageManifest(ctx, p.repo, p.image, &resp.Tags[i]) + + p.mu.Lock() + defer p.mu.Unlock() + + if err != nil { + p.errs = append(p.errs, err) + return + } + + p.tags = append(p.tags, imageTags...) + }(i) + } + + return resp.HasAdditional, nil +} diff --git a/pkg/client/quay/quay.go b/pkg/client/quay/quay.go index 8e3b93bd..3578f99e 100644 --- a/pkg/client/quay/quay.go +++ b/pkg/client/quay/quay.go @@ -4,15 +4,18 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" "net/http" "time" + "github.com/hashicorp/go-retryablehttp" + "github.com/jetstack/version-checker/pkg/api" + "github.com/jetstack/version-checker/pkg/client/util" ) const ( - lookupURL = "https://quay.io/api/v1/repository/%s/%s/tag/" + tagURL = "https://quay.io/api/v1/repository/%s/%s/tag/?page=%d" + manifestURL = "https://quay.io/api/v1/repository/%s/%s/manifest/%s" ) type Options struct { @@ -20,26 +23,49 @@ type Options struct { } type Client struct { - *http.Client + *retryablehttp.Client Options } -type Response struct { - Tags []Tag `json:"tags"` +type responseTag struct { + Tags []responseTagItem `json:"tags"` + HasAdditional bool `json:"has_additional"` + Page int `json:"page"` } -type Tag struct { +type responseTagItem struct { Name string `json:"name"` ManifestDigest string `json:"manifest_digest"` LastModified string `json:"last_modified"` + IsManifestList bool `json:"is_manifest_list"` +} + +type responseManifest struct { + ManifestData string `json:"manifest_data"` + Status *int `json:"status,omitempty"` +} + +type responseManifestData struct { + Manifests []responseManifestDataItem `json:"manifests"` +} + +type responseManifestDataItem struct { + Digest string `json:"digest"` + Platform struct { + Architecture api.Architecture `json:"architecture"` + OS api.OS `json:"os"` + } `json:"platform"` } func New(opts Options) *Client { + client := retryablehttp.NewClient() + client.HTTPClient.Transport = &http.Transport{Proxy: http.ProxyFromEnvironment} + client.RetryMax = 10 + client.Logger = nil + return &Client{ Options: opts, - Client: &http.Client{ - Timeout: time.Second * 5, - }, + Client: client, } } @@ -47,49 +73,105 @@ func (c *Client) Name() string { return "quay" } +// Fetch the image tags from an upstream repository and image func (c *Client) Tags(ctx context.Context, _, repo, image string) ([]api.ImageTag, error) { - url := fmt.Sprintf(lookupURL, repo, image) + p := c.newPager(repo, image) - req, err := http.NewRequest(http.MethodGet, url, nil) - if err != nil { + if err := p.fetchTags(ctx); err != nil { return nil, err } - if len(c.Token) > 0 { - req.Header.Add("Authorization", "Bearer "+c.Token) + return p.tags, nil +} + +// fetchImageManifest will lookup all manifests for a tag, if it is a list +func (c *Client) fetchImageManifest(ctx context.Context, repo, image string, tag *responseTagItem) ([]api.ImageTag, error) { + timestamp, err := time.Parse(time.RFC1123Z, tag.LastModified) + if err != nil { + return nil, err } - req.URL.Scheme = "https" - req = req.WithContext(ctx) + // If a multi-arch image, call manifest endpoint + if tag.IsManifestList { + url := fmt.Sprintf(manifestURL, repo, image, tag.ManifestDigest) + tags, err := c.callManifests(ctx, timestamp, tag.Name, url) + if err != nil { + return nil, err + } - resp, err := c.Do(req) - if err != nil { - return nil, fmt.Errorf("failed to get quay image: %s", err) + return tags, nil } - body, err := ioutil.ReadAll(resp.Body) - if err != nil { + // Fallback to not using multi-arch image + + os, arch := util.OSArchFromTag(tag.Name) + + return []api.ImageTag{ + { + Tag: tag.Name, + SHA: tag.ManifestDigest, + Timestamp: timestamp, + OS: os, + Architecture: arch, + }, + }, nil +} + +// callManifests endpoint on the tags image manifest +func (c *Client) callManifests(ctx context.Context, timestamp time.Time, tag, url string) ([]api.ImageTag, error) { + var manifestResp responseManifest + if err := c.makeRequest(ctx, url, &manifestResp); err != nil { return nil, err } - var response Response - if err := json.Unmarshal(body, &response); err != nil { - return nil, err + // Got error on this manifest, ignore + if manifestResp.Status != nil { + return nil, nil } - var tags []api.ImageTag - for _, tag := range response.Tags { - timestamp, err := time.Parse(time.RFC1123Z, tag.LastModified) - if err != nil { - return nil, err - } + var manifestData responseManifestData + if err := json.Unmarshal([]byte(manifestResp.ManifestData), &manifestData); err != nil { + return nil, fmt.Errorf("failed to unmarshal manifest data %s: %#+v: %s", + tag, manifestResp, err) + } + var tags []api.ImageTag + for _, manifest := range manifestData.Manifests { tags = append(tags, api.ImageTag{ - Tag: tag.Name, - SHA: tag.ManifestDigest, - Timestamp: timestamp, + Tag: tag, + SHA: manifest.Digest, + Timestamp: timestamp, + Architecture: manifest.Platform.Architecture, + OS: manifest.Platform.OS, }) } return tags, nil } + +// makeRequest will make a call and write the response to the object. +// Implements backoff. +func (c *Client) makeRequest(ctx context.Context, url string, obj interface{}) error { + req, err := retryablehttp.NewRequest(http.MethodGet, url, nil) + if err != nil { + return err + } + + if len(c.Token) > 0 { + req.Header.Add("Authorization", "Bearer "+c.Token) + } + + req.URL.Scheme = "https" + req = req.WithContext(ctx) + + resp, err := c.Do(req) + if err != nil { + return fmt.Errorf("failed to make quay call %q: %s", url, err) + } + + if err := json.NewDecoder(resp.Body).Decode(obj); err != nil { + return err + } + + return nil +} diff --git a/pkg/client/selfhosted/selfhosted.go b/pkg/client/selfhosted/selfhosted.go index de004a14..da891638 100644 --- a/pkg/client/selfhosted/selfhosted.go +++ b/pkg/client/selfhosted/selfhosted.go @@ -2,11 +2,14 @@ package selfhosted import ( "context" + "crypto/tls" + "crypto/x509" "encoding/json" "errors" "fmt" - "io/ioutil" + "io" "net/http" + "os" "regexp" "strings" "time" @@ -24,7 +27,7 @@ const ( // /v2/{repo/image}/manifests/{tag} manifestPath = "%s/v2/%s/manifests/%s" // Token endpoint - tokenPath = "/v2/token" + defaultTokenPath = "/v2/token" // HTTP headers to request API version dockerAPIv1Header = "application/vnd.docker.distribution.manifest.v1+json" @@ -32,10 +35,13 @@ const ( ) type Options struct { - Host string - Username string - Password string - Bearer string + Host string + Username string + Password string + Bearer string + TokenPath string + Insecure bool + CAPath string } type Client struct { @@ -58,8 +64,8 @@ type TagResponse struct { type ManifestResponse struct { Digest string - Architecture string `json:"architecture"` - History []History `json:"history"` + Architecture api.Architecture `json:"architecture"` + History []History `json:"history"` } type History struct { @@ -94,7 +100,12 @@ func New(ctx context.Context, log *logrus.Entry, opts *Options) (*Client, error) return nil, errors.New("cannot specify Bearer token as well as username/password") } - token, err := client.setupBasicAuth(ctx, opts.Host) + tokenPath := opts.TokenPath + if tokenPath == "" { + tokenPath = defaultTokenPath + } + + token, err := client.setupBasicAuth(ctx, opts.Host, tokenPath) if httpErr, ok := selfhostederrors.IsHTTPError(err); ok { return nil, fmt.Errorf("failed to setup token auth (%d): %s", httpErr.StatusCode, httpErr.Body) @@ -111,6 +122,17 @@ func New(ctx context.Context, log *logrus.Entry, opts *Options) (*Client, error) if client.httpScheme == "" { client.httpScheme = "https" } + if client.httpScheme == "https" { + tlsConfig, err := newTLSConfig(opts.Insecure, opts.CAPath) + if err != nil { + return nil, err + } + + client.Client.Transport = &http.Transport{ + TLSClientConfig: tlsConfig, + Proxy: http.ProxyFromEnvironment, + } + } return client, nil } @@ -208,7 +230,7 @@ func (c *Client) doRequest(ctx context.Context, url, header string, obj interfac return nil, fmt.Errorf("failed to get docker image: %s", err) } - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { return nil, err } @@ -224,7 +246,7 @@ func (c *Client) doRequest(ctx context.Context, url, header string, obj interfac return resp.Header, nil } -func (c *Client) setupBasicAuth(ctx context.Context, url string) (string, error) { +func (c *Client) setupBasicAuth(ctx context.Context, url, tokenPath string) (string, error) { upReader := strings.NewReader( fmt.Sprintf(`{"username": "%s", "password": "%s"}`, c.Username, c.Password, @@ -247,7 +269,7 @@ func (c *Client) setupBasicAuth(ctx context.Context, url string) (string, error) req.URL, err) } - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { return "", err } @@ -263,3 +285,24 @@ func (c *Client) setupBasicAuth(ctx context.Context, url string) (string, error) return response.Token, nil } + +func newTLSConfig(insecure bool, CAPath string) (*tls.Config, error) { + // Load system CA Certs and/or create a new CertPool + rootCAs, _ := x509.SystemCertPool() + if rootCAs == nil { + rootCAs = x509.NewCertPool() + } + + if CAPath != "" { + certs, err := os.ReadFile(CAPath) + if err != nil { + return nil, fmt.Errorf("Failed to append %q to RootCAs: %v", CAPath, err) + } + rootCAs.AppendCertsFromPEM(certs) + } + + return &tls.Config{ + InsecureSkipVerify: insecure, + RootCAs: rootCAs, + }, nil +} diff --git a/pkg/client/util/util.go b/pkg/client/util/util.go index 94d1a74b..14ece724 100644 --- a/pkg/client/util/util.go +++ b/pkg/client/util/util.go @@ -1,5 +1,39 @@ package util +import ( + "strings" + + "github.com/jetstack/version-checker/pkg/api" +) + +var ( + oss = [...]api.OS{ + "linux", + "darwin", + "windows", + "freebsd", + } + + archs = [...]api.Architecture{ + "amd", + "amd64", + "arm", + "arm64", + "arm32v5", + "arm32v6", + "arm32v7", + "arm64v8", + "i386", + "ppc64", + "ppc64le", + "s390x", + "x86", + "x86_64", + "mips", + } +) + +// Join repo and image strings func JoinRepoImage(repo, image string) string { if len(repo) == 0 { return image @@ -10,3 +44,30 @@ func JoinRepoImage(repo, image string) string { return repo + "/" + image } + +// Attempt to determine the OS and Arch, given a tag name +func OSArchFromTag(tag string) (api.OS, api.Architecture) { + var ( + os api.OS + arch api.Architecture + split = strings.Split(tag, "-") + ) + + for _, s := range split { + ss := strings.ToLower(s) + + for _, pos := range oss { + if pos == api.OS(ss) { + os = pos + } + } + + for _, parch := range archs { + if parch == api.Architecture(ss) { + arch = parch + } + } + } + + return os, arch +} diff --git a/pkg/controller/checker/checker.go b/pkg/controller/checker/checker.go index 137c448f..278d429f 100644 --- a/pkg/controller/checker/checker.go +++ b/pkg/controller/checker/checker.go @@ -95,6 +95,19 @@ func (c *Checker) Container(ctx context.Context, log *logrus.Entry, // containerStatusImageSHA will return the containers image SHA, if it is ready func containerStatusImageSHA(pod *corev1.Pod, containerName string) string { + for _, status := range pod.Status.InitContainerStatuses { + if status.Name == containerName { + statusImage, _, statusSHA := urlTagSHAFromImage(status.ImageID) + + // If the image ID contains a URL, use the parsed SHA + if len(statusSHA) > 0 { + return statusSHA + } + + return statusImage + } + } + // Get the SHA of the current image for _, status := range pod.Status.ContainerStatuses { if status.Name == containerName { @@ -112,7 +125,7 @@ func containerStatusImageSHA(pod *corev1.Pod, containerName string) string { return "" } -// isLatestOrEmptyTag will return true if the given tag is '' or 'latest' +// isLatestOrEmptyTag will return true if the given tag is ” or 'latest' func (c *Checker) isLatestOrEmptyTag(tag string) bool { return tag == "" || tag == "latest" } diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index dedc76b7..2f63cec4 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -148,7 +148,8 @@ func (c *Controller) deleteObject(obj interface{}) { for _, container := range pod.Spec.Containers { c.log.Debugf("removing deleted pod containers from metrics: %s/%s/%s", pod.Namespace, pod.Name, container.Name) - c.metrics.RemoveImage(pod.Namespace, pod.Name, container.Name) + c.metrics.RemoveImage(pod.Namespace, pod.Name, container.Name, "init") + c.metrics.RemoveImage(pod.Namespace, pod.Name, container.Name, "container") } } diff --git a/pkg/controller/sync.go b/pkg/controller/sync.go index 03d8123a..397c52b5 100644 --- a/pkg/controller/sync.go +++ b/pkg/controller/sync.go @@ -20,8 +20,13 @@ func (c *Controller) sync(ctx context.Context, pod *corev1.Pod) error { builder := options.New(pod.Annotations) var errs []string + for _, container := range pod.Spec.InitContainers { + if err := c.syncContainer(ctx, log, builder, pod, &container, "init"); err != nil { + errs = append(errs, err.Error()) + } + } for _, container := range pod.Spec.Containers { - if err := c.syncContainer(ctx, log, builder, pod, &container); err != nil { + if err := c.syncContainer(ctx, log, builder, pod, &container, "container"); err != nil { errs = append(errs, err.Error()) } } @@ -36,10 +41,10 @@ func (c *Controller) sync(ctx context.Context, pod *corev1.Pod) error { // syncContainer will enqueue a given container to check the version func (c *Controller) syncContainer(ctx context.Context, log *logrus.Entry, builder *options.Builder, pod *corev1.Pod, - container *corev1.Container) error { + container *corev1.Container, containerType string) error { // If not enabled, exit early if !builder.IsEnabled(c.defaultTestAll, container.Name) { - c.metrics.RemoveImage(pod.Namespace, pod.Name, container.Name) + c.metrics.RemoveImage(pod.Namespace, pod.Name, container.Name, containerType) return nil } @@ -50,9 +55,9 @@ func (c *Controller) syncContainer(ctx context.Context, log *logrus.Entry, build } log = log.WithField("container", container.Name) - log.Debug("processing conainer image") + log.Debug("processing container image") - err = c.checkContainer(ctx, log, pod, container, opts) + err = c.checkContainer(ctx, log, pod, container, containerType, opts) // Don't re-sync, if no version found meeting search criteria if versionerrors.IsNoVersionFound(err) { log.Error(err.Error()) @@ -69,7 +74,7 @@ func (c *Controller) syncContainer(ctx context.Context, log *logrus.Entry, build // checkContainer will check the given container and options, and update // metrics according to the result. func (c *Controller) checkContainer(ctx context.Context, log *logrus.Entry, pod *corev1.Pod, - container *corev1.Container, opts *api.Options) error { + container *corev1.Container, containerType string, opts *api.Options) error { result, err := c.checker.Container(ctx, log, pod, container, opts) if err != nil { return err @@ -89,7 +94,7 @@ func (c *Controller) checkContainer(ctx context.Context, log *logrus.Entry, pod } c.metrics.AddImage(pod.Namespace, pod.Name, - container.Name, result.ImageURL, result.IsLatest, + container.Name, containerType, result.ImageURL, result.IsLatest, result.CurrentVersion, result.LatestVersion) return nil diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index be933620..c2f81bf0 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -44,7 +44,7 @@ func New(log *logrus.Entry) *Metrics { Help: "Where the container in use is using the latest upstream registry version", }, []string{ - "namespace", "pod", "container", "image", "current_version", "latest_version", + "namespace", "pod", "container", "container_type", "image", "current_version", "latest_version", }, ) @@ -63,6 +63,8 @@ func New(log *logrus.Entry) *Metrics { func (m *Metrics) Run(servingAddress string) error { router := http.NewServeMux() router.Handle("/metrics", promhttp.HandlerFor(m.registry, promhttp.HandlerOpts{})) + router.Handle("/healthz", http.HandlerFunc(m.healthzAndReadyzHandler)) + router.Handle("/readyz", http.HandlerFunc(m.healthzAndReadyzHandler)) ln, err := net.Listen("tcp", servingAddress) if err != nil { @@ -89,9 +91,9 @@ func (m *Metrics) Run(servingAddress string) error { return nil } -func (m *Metrics) AddImage(namespace, pod, container, imageURL string, isLatest bool, currentVersion, latestVersion string) { +func (m *Metrics) AddImage(namespace, pod, container, containerType, imageURL string, isLatest bool, currentVersion, latestVersion string) { // Remove old image url/version if it exists - m.RemoveImage(namespace, pod, container) + m.RemoveImage(namespace, pod, container, containerType) m.mu.Lock() defer m.mu.Unlock() @@ -102,7 +104,7 @@ func (m *Metrics) AddImage(namespace, pod, container, imageURL string, isLatest } m.containerImageVersion.With( - m.buildLabels(namespace, pod, container, imageURL, currentVersion, latestVersion), + m.buildLabels(namespace, pod, container, containerType, imageURL, currentVersion, latestVersion), ).Set(isLatestF) index := m.latestImageIndex(namespace, pod, container) @@ -113,7 +115,7 @@ func (m *Metrics) AddImage(namespace, pod, container, imageURL string, isLatest } } -func (m *Metrics) RemoveImage(namespace, pod, container string) { +func (m *Metrics) RemoveImage(namespace, pod, container, containerType string) { m.mu.Lock() defer m.mu.Unlock() @@ -124,7 +126,7 @@ func (m *Metrics) RemoveImage(namespace, pod, container string) { } m.containerImageVersion.Delete( - m.buildLabels(namespace, pod, container, item.image, item.currentVersion, item.latestVersion), + m.buildLabels(namespace, pod, container, containerType, item.image, item.currentVersion, item.latestVersion), ) delete(m.containerCache, index) } @@ -133,10 +135,11 @@ func (m *Metrics) latestImageIndex(namespace, pod, container string) string { return strings.Join([]string{namespace, pod, container}, "") } -func (m *Metrics) buildLabels(namespace, pod, container, imageURL, currentVersion, latestVersion string) prometheus.Labels { +func (m *Metrics) buildLabels(namespace, pod, container, containerType, imageURL, currentVersion, latestVersion string) prometheus.Labels { return prometheus.Labels{ "namespace": namespace, "pod": pod, + "container_type": containerType, "container": container, "image": imageURL, "current_version": currentVersion, @@ -163,3 +166,12 @@ func (m *Metrics) Shutdown() error { return nil } + +func (m *Metrics) healthzAndReadyzHandler(w http.ResponseWriter, r *http.Request) { + // Its not great, but does help ensure that we're alive and ready over + // calling the /metrics endpoint which can be expensive on large payloads + _, err := w.Write([]byte("OK")) + if err != nil { + m.log.Errorf("Failed to send Healthz/Readyz response: %s", err) + } +}