From aa4c7d331191387abfd499b4fbd83bd56b41c131 Mon Sep 17 00:00:00 2001 From: Massimiliano Giovagnoli Date: Mon, 27 Nov 2023 21:01:35 +0100 Subject: [PATCH] initial commit Signed-off-by: Massimiliano Giovagnoli --- .dockerignore | 4 + .github/workflows/lint-code.yaml | 20 + .github/workflows/lint-commit.yml | 23 + .github/workflows/lint-ghactions.yml | 24 + .github/workflows/lint-pullrequest.yml | 37 ++ .github/workflows/release.yaml | 49 ++ .github/workflows/report-codecov.yml | 38 ++ .github/workflows/scan-code.yml | 24 + .gitignore | 26 + .golangci.yml | 43 ++ .goreleaser.yaml | 129 ++++ cmd/constants.go | 5 + cmd/manager/manager.go | 111 ++++ cmd/root.go | 21 + controllers/serviceaccount/constants.go | 21 + controllers/serviceaccount/errors.go | 9 + .../serviceaccount/globaltenantresources.go | 46 ++ controllers/serviceaccount/rolebindings.go | 113 ++++ controllers/serviceaccount/serviceaccount.go | 232 +++++++ controllers/serviceaccount/tokens.go | 71 +++ e2e/serviceaccount_test.go | 310 +++++++++ e2e/suite_test.go | 163 +++++ e2e/utils_test.go | 250 ++++++++ go.mod | 158 +++++ go.sum | 588 ++++++++++++++++++ main.go | 15 + pkg/indexer/indexer.go | 25 + 27 files changed, 2555 insertions(+) create mode 100644 .dockerignore create mode 100644 .github/workflows/lint-code.yaml create mode 100644 .github/workflows/lint-commit.yml create mode 100644 .github/workflows/lint-ghactions.yml create mode 100644 .github/workflows/lint-pullrequest.yml create mode 100644 .github/workflows/release.yaml create mode 100644 .github/workflows/report-codecov.yml create mode 100644 .github/workflows/scan-code.yml create mode 100644 .gitignore create mode 100644 .golangci.yml create mode 100644 .goreleaser.yaml create mode 100644 cmd/constants.go create mode 100644 cmd/manager/manager.go create mode 100644 cmd/root.go create mode 100644 controllers/serviceaccount/constants.go create mode 100644 controllers/serviceaccount/errors.go create mode 100644 controllers/serviceaccount/globaltenantresources.go create mode 100644 controllers/serviceaccount/rolebindings.go create mode 100644 controllers/serviceaccount/serviceaccount.go create mode 100644 controllers/serviceaccount/tokens.go create mode 100644 e2e/serviceaccount_test.go create mode 100644 e2e/suite_test.go create mode 100644 e2e/utils_test.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 pkg/indexer/indexer.go diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..0f04682 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +# More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file +# Ignore build and test binaries. +bin/ +testbin/ diff --git a/.github/workflows/lint-code.yaml b/.github/workflows/lint-code.yaml new file mode 100644 index 0000000..5e99dc4 --- /dev/null +++ b/.github/workflows/lint-code.yaml @@ -0,0 +1,20 @@ +name: Lint code + +on: + push: + branches: [ "*" ] + pull_request: + branches: [ "*" ] + +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Run golangci-lint + uses: golangci/golangci-lint-action@v3.2.0 + with: + version: v1.50.0 + only-new-issues: false + args: --config .golangci.yml --timeout=5m diff --git a/.github/workflows/lint-commit.yml b/.github/workflows/lint-commit.yml new file mode 100644 index 0000000..f17ad1e --- /dev/null +++ b/.github/workflows/lint-commit.yml @@ -0,0 +1,23 @@ +name: Lint commits +permissions: {} + +on: + push: + branches: [ "*" ] + pull_request: + branches: [ "*" ] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + commit_lint: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + fetch-depth: 0 + - uses: wagoid/commitlint-github-action@0d749a1a91d4770e983a7b8f83d4a3f0e7e0874e #v5.4.4 + with: + firstParent: true diff --git a/.github/workflows/lint-ghactions.yml b/.github/workflows/lint-ghactions.yml new file mode 100644 index 0000000..0f6955f --- /dev/null +++ b/.github/workflows/lint-ghactions.yml @@ -0,0 +1,24 @@ +name: Lint GitHub Actions +permissions: {} + +on: + pull_request: + branches: [ "main" ] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + check: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - name: Ensure SHA pinned actions + uses: zgosalvez/github-actions-ensure-sha-pinned-actions@b35f285b9bb7e80de0967367cee66d3b6d50ceca # v3.0.1 + with: + # slsa-github-generator requires using a semver tag for reusable workflows. + # See: https://github.com/slsa-framework/slsa-github-generator#referencing-slsa-builders-and-generators + allowlist: | + slsa-framework/slsa-github-generator \ No newline at end of file diff --git a/.github/workflows/lint-pullrequest.yml b/.github/workflows/lint-pullrequest.yml new file mode 100644 index 0000000..5bc5e02 --- /dev/null +++ b/.github/workflows/lint-pullrequest.yml @@ -0,0 +1,37 @@ +name: Lint pull request + +on: + pull_request_target: + types: + - opened + - edited + - synchronize + +permissions: + pull-requests: write + +jobs: + main: + name: Validate PR title + runs-on: ubuntu-latest + steps: + - uses: amannn/action-semantic-pull-request@e9fabac35e210fea40ca5b14c0da95a099eff26f + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + types: | + chore + ci + docs + feat + fix + test + sec + requireScope: false + wip: false + # If the PR only contains a single commit, the action will validate that + # it matches the configured pattern. + validateSingleCommit: true + # Related to `validateSingleCommit` you can opt-in to validate that the PR + # title matches a single commit to avoid confusion. + validateSingleCommitMatchesPrTitle: true diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..aa42dc4 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,49 @@ +name: Release + +on: + push: + tags: + - v* + +permissions: + contents: write # needed to write releases + id-token: write # needed for keyless signing + packages: write # needed for ghcr access + +jobs: + goreleaser: + runs-on: ubuntu-22.04 + steps: + + - name: Checkout + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + fetch-depth: 0 + + - name: Fetch + run: git fetch --prune --force --tags + + - name: Setup Go + uses: actions/setup-go@84cbf8094393cdc5fe1fe1671ff2647332956b1a #v3.2.1 + with: + go-version: '1.20' + + - uses: creekorful/goreportcard-action@1f35ced8cdac2cba28c9a2f2288a16aacfd507f9 # v1.0 + + - uses: sigstore/cosign-installer@1fc5bd396d372bee37d608f955b336615edf79c8 # v3.2.0 + + - uses: anchore/sbom-action/download-syft@5ecf649a417b8ae17dc8383dc32d46c03f2312df #v0.15.1 + + - uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8 # v5.0.0 + with: + version: latest + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/report-codecov.yml b/.github/workflows/report-codecov.yml new file mode 100644 index 0000000..8f4d6d6 --- /dev/null +++ b/.github/workflows/report-codecov.yml @@ -0,0 +1,38 @@ +name: Report Codecov +permissions: {} + +on: + pull_request: + branches: [ "main" ] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + codecov: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - name: Setup caches + uses: ./.github/actions/setup-caches + timeout-minutes: 5 + continue-on-error: true + with: + build-cache-key: codecov + - name: Check secret + id: checksecret + uses: ./.github/actions/exists + with: + value: ${{ secrets.CODECOV_TOKEN }} + - name: Generate Code Coverage Report + if: steps.checksecret.outputs.result == 'true' + run: make test + - name: Upload Report to Codecov + if: steps.checksecret.outputs.result == 'true' + uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4 + with: + file: ./coverage.out + fail_ci_if_error: true + verbose: true diff --git a/.github/workflows/scan-code.yml b/.github/workflows/scan-code.yml new file mode 100644 index 0000000..ecbe6a3 --- /dev/null +++ b/.github/workflows/scan-code.yml @@ -0,0 +1,24 @@ +name: Scan code +permissions: {} +on: + push: + branches: [ "*" ] + pull_request: + branches: [ "*" ] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + tests: + runs-on: ubuntu-20.04 + env: + GO111MODULE: on + steps: + - name: Checkout Source + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - name: Run Gosec Security Scanner + uses: securego/gosec@55d79496019a560e16e73e1948dee20a1fad631a # v2.18.2 + with: + args: ./... diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e917e5c --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ + +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib +bin +testbin/* +Dockerfile.cross + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Kubernetes Generated files - skip generated files, except for vendored files + +!vendor/**/zz_generated.* + +# editor and IDE paraphernalia +.idea +*.swp +*.swo +*~ diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..6c01323 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,43 @@ +linters-settings: + cyclop: + max-complexity: 27 + gci: + sections: + - standard # Captures all standard packages if they do not match another section. + - default # Contains all imports that could not be matched to another section type. + - prefix(github.com/maxgio92/capsule-addon-fluxcd) # Groups all imports with the specified Prefix. + goconst: + min-len: 2 + min-occurrences: 3 + goheader: + template: |- + Copyright 2020-2023 Project Capsule Authors. + SPDX-License-Identifier: Apache-2.0 + govet: + check-shadowing: true + maligned: + suggest-new: true + +linters: + enable-all: true + disable: + - interfacer + - godox + - golint + - scopelint + - maligned + - gochecknoglobals + - gochecknoinits + - exhaustivestruct + - exhaustruct + - ireturn + - lll + - nonamedreturns + - wrapcheck + - varnamelen + +issues: + exclude-rules: + - path: / + linters: + - typecheck diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..969ff55 --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,129 @@ +project_name: capsule-addon-fluxcd + +before: + hooks: + - go mod tidy + +builds: + - env: + - CGO_ENABLED=0 + - GO111MODULE=on + goos: + - linux + goarch: + - amd64 + - arm64 + ldflags: | + -s + -w + -X main.buildCommit={{ .Commit }} + -X main.buildDate={{ .CommitDate }} + -X main.buildVersion={{ .Version }} + mod_timestamp: '{{ .CommitTimestamp }}' + +# Publish sources. +source: + enabled: true + +# Publish the checksums file of the release. +checksum: + name_template: '{{ .ProjectName }}_{{ .Version }}_checksums.txt' + +# Sign the checksums file. +# All files (including the sboms) are included in the checksum, +# so we don't need to sign each one if we don't want to. +# https://goreleaser.com/customization/sign +signs: + - cmd: cosign + certificate: '${artifact}.pem' + args: + - sign-blob + - '--output-certificate=${certificate}' + - '--output-signature=${signature}' + - '${artifact}' + - '--yes' # needed on cosign 2.0.0+ + artifacts: all + output: true + +# Use ko to build and publish container image. +# https://goreleaser.com/customization/ko/?h=kos +kos: + - base_image: cgr.dev/chainguard/static + repository: ghcr.io/maxgio92/capsule-addon-fluxcd + bare: true + tags: + - '{{ .Version }}' + - '{{ .Major }}.{{ .Minor }}' + - latest + platforms: + - linux/amd64 + - linux/arm64 + sbom: spdx + +# Sign the container image. +# https://goreleaser.com/customization/docker_sig +docker_signs: + - cmd: cosign + artifacts: all + output: true + args: + - 'sign' + - '${artifact}' + - '--yes' + +# Create SBOMs of all archives and the source tarball using syft. +# https://goreleaser.com/customization/sbom +sboms: + - id: archive + artifacts: archive + - id: source + artifacts: source + +# Generate a GitHub release with a customized description. +release: + footer: | + ## Thanks! + + Those were the changes on {{ .Tag }}! + + **Full Changelog**: https://github.com/maxgio92/{{ .ProjectName }}/compare/{{ .PreviousTag }}...{{ .Tag }} + + **Container images** + - `ghcr.io/projectcapsule/{{ .ProjectName }}:{{ .Tag }}` + - `ghcr.io/projectcapsule/{{ .ProjectName }}:latest` + +# Generate a GitHub release with a customized changelog. +changelog: + sort: asc + use: github + filters: + exclude: + - '^test:' + - '^chore' + - '^rebase:' + - 'merge conflict' + - Merge pull request + - Merge remote-tracking branch + - Merge branch + groups: + # https://github.com/conventional-changelog/commitlint/tree/master/%40commitlint/config-conventional + - title: '🛠 Dependency updates' + regexp: '^.*?(feat|fix)\(deps\)!?:.+$' + order: 300 + - title: '✨ New Features' + regexp: '^.*?feat(\([[:word:]]+\))??!?:.+$' + order: 100 + - title: '🐛 Bug fixes' + regexp: '^.*?fix(\([[:word:]]+\))??!?:.+$' + order: 200 + - title: '📖 Documentation updates' + regexp: ^.*?docs(\([[:word:]]+\))??!?:.+$ + order: 400 + - title: '🛡️ Security updates' + regexp: ^.*?(sec)(\([[:word:]]+\))??!?:.+$ + order: 500 + - title: '🚀 Build process updates' + regexp: ^.*?(build|ci)(\([[:word:]]+\))??!?:.+$ + order: 600 + - title: '📦 Other work' + order: 9999 diff --git a/cmd/constants.go b/cmd/constants.go new file mode 100644 index 0000000..77e0064 --- /dev/null +++ b/cmd/constants.go @@ -0,0 +1,5 @@ +package cmd + +const ( + commandName = "capsule-addon-flux" +) diff --git a/cmd/manager/manager.go b/cmd/manager/manager.go new file mode 100644 index 0000000..df2e356 --- /dev/null +++ b/cmd/manager/manager.go @@ -0,0 +1,111 @@ +package manager + +import ( + "flag" + "os" + + "github.com/go-logr/logr" + "github.com/pkg/errors" + capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2" + "github.com/spf13/cobra" + "go.uber.org/zap/zapcore" + "k8s.io/apimachinery/pkg/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + "github.com/maxgio92/capsule-addon-flux/controllers/serviceaccount" + "github.com/maxgio92/capsule-addon-flux/pkg/indexer" +) + +type Options struct { + ProxyURL string + ProxyCAPath string + + SetupLog logr.Logger + Zo *zap.Options +} + +func New() *cobra.Command { + setupLog := ctrl.Log.WithName("setup") + + zo := &zap.Options{ + EncoderConfigOptions: append([]zap.EncoderConfigOption{}, func(config *zapcore.EncoderConfig) { + config.EncodeTime = zapcore.ISO8601TimeEncoder + }), + } + + opts := &Options{ + SetupLog: setupLog, + Zo: zo, + } + + cmd := &cobra.Command{ + Use: "manager", + Short: "Starts the manager of the Capsule addon for FluxCD", + RunE: opts.Run, + } + + // Add Proxy options. + cmd.Flags().StringVar(&opts.ProxyURL, "proxy-url", "https://capsule-proxy.capsule-system.svc:9001", "Kubernetes Service URL on which Capsule Proxy is waiting for connections") + cmd.Flags().StringVar(&opts.ProxyCAPath, "proxy-ca-path", "/tmp/ca.crt", "File containing the Certificate Authority used by Capsule Proxy") + + // Add Zap options. + var fs flag.FlagSet + opts.Zo.BindFlags(&fs) + cmd.Flags().AddGoFlagSet(&fs) + + return cmd +} + +func (o *Options) Run(_ *cobra.Command, _ []string) error { + scheme := runtime.NewScheme() + clientgoscheme.AddToScheme(scheme) + capsulev1beta2.AddToScheme(scheme) + + ctrl.SetLogger(zap.New(zap.UseFlagOptions(o.Zo))) + + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ + Scheme: scheme, + NewClient: func(config *rest.Config, options client.Options) (client.Client, error) { + options.Cache.Unstructured = true + + return client.New(config, options) + }, + }) + if err != nil { + o.SetupLog.Error(err, "unable to create manager") + return errors.Wrap(err, "unable to create manager") + } + + proxyCA, err := os.ReadFile(o.ProxyCAPath) + if err != nil { + return errors.Wrap(err, "unable to read the CA file") + } + + ctx := ctrl.SetupSignalHandler() + + if err = indexer.AddToManager(ctx, o.SetupLog, mgr); err != nil { + o.SetupLog.Error(err, "unable to setup indexers") + return errors.Wrap(err, "unable to setup indexers") + } + + if err = serviceaccount.NewServiceAccountReconciler( + serviceaccount.WithClient(mgr.GetClient()), + serviceaccount.WithLogger(ctrl.Log.WithName("controllers").WithName("ServiceAccount")), + serviceaccount.WithProxyCA(string(proxyCA)), + serviceaccount.WithProxyURL(o.ProxyURL), + ).SetupWithManager(ctx, mgr); err != nil { + o.SetupLog.Error(err, "unable to create manager", "controller", "ServiceAccount") + return errors.Wrap(err, "unable to setup the service account controller") + } + + if err = mgr.Start(ctx); err != nil { + o.SetupLog.Error(err, "problem running manager") + return errors.Wrap(err, "unable to start the manager") + } + + return nil +} diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 0000000..e03c3ea --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,21 @@ +package cmd + +import ( + "github.com/maxgio92/capsule-addon-flux/cmd/manager" + "github.com/spf13/cobra" +) + +func New() *cobra.Command { + cmd := &cobra.Command{ + Use: commandName, + } + + cmd.AddCommand(manager.New()) + + return cmd +} + +func Execute() error { + cmd := New() + return cmd.Execute() +} diff --git a/controllers/serviceaccount/constants.go b/controllers/serviceaccount/constants.go new file mode 100644 index 0000000..5424395 --- /dev/null +++ b/controllers/serviceaccount/constants.go @@ -0,0 +1,21 @@ +package serviceaccount + +const ( + ManagerName = "capsule-addon-fluxcd" + + GlobalTenantResourceSuffix = "-kubeconfig" + + SecretNameSuffixKubeconfig = "-kubeconfig" + SecretNameSuffixToken = "-token" + SecretKeyKubeconfig = "kubeconfig" + + ServiceAccountAddonAnnotationKey = "capsule.addon.fluxcd/enabled" + ServiceAccountAddonAnnotationValue = "true" + + ServiceAccountGlobalAnnotationKey = "capsule.addon.fluxcd/kubeconfig-global" + ServiceAccountGlobalAnnotationValue = "true" + + KubeconfigClusterName = "default" + KubeconfigUserName = "default" + KubeconfigContextName = "default" +) diff --git a/controllers/serviceaccount/errors.go b/controllers/serviceaccount/errors.go new file mode 100644 index 0000000..f596a16 --- /dev/null +++ b/controllers/serviceaccount/errors.go @@ -0,0 +1,9 @@ +package serviceaccount + +import "github.com/pkg/errors" + +var ( + ErrServiceAccountTokenNotFound = errors.New("service account token not found") + ErrGetServiceAccountToken = errors.New("error getting service account token") + ErrServiceAccountTokenSecretEmpty = errors.New("the service account token secret is empty") +) diff --git a/controllers/serviceaccount/globaltenantresources.go b/controllers/serviceaccount/globaltenantresources.go new file mode 100644 index 0000000..cba4c91 --- /dev/null +++ b/controllers/serviceaccount/globaltenantresources.go @@ -0,0 +1,46 @@ +package serviceaccount + +import ( + "context" + capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" +) + +// ensureGlobalTenantResource ensures the GlobalTenantResource to distribute an object. +func (r *ServiceAccountReconciler) ensureGlobalTenantResource(ctx context.Context, name, tenantName string, object runtime.Object) error { + gtr := &capsulev1beta2.GlobalTenantResource{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Labels: map[string]string{"app.kubernetes.io/managed-by": ManagerName}, + }, + Spec: capsulev1beta2.GlobalTenantResourceSpec{ + TenantSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{"kubernetes.io/metadata.name": tenantName}, + }, + TenantResourceSpec: capsulev1beta2.TenantResourceSpec{ + Resources: []capsulev1beta2.ResourceSpec{{ + RawItems: []capsulev1beta2.RawExtension{{ + RawExtension: runtime.RawExtension{ + Object: object, + }, + }}, + }}, + }, + }, + } + + if _, err := controllerutil.CreateOrUpdate(ctx, r.Client, gtr, func() error { + if gtr.ObjectMeta.Labels == nil { + gtr.ObjectMeta.Labels = make(map[string]string, 1) + } + gtr.ObjectMeta.Labels["app.kubernetes.io/managed-by"] = ManagerName + + return nil + }); err != nil { + return err + } + + return nil +} diff --git a/controllers/serviceaccount/rolebindings.go b/controllers/serviceaccount/rolebindings.go new file mode 100644 index 0000000..3aee5c6 --- /dev/null +++ b/controllers/serviceaccount/rolebindings.go @@ -0,0 +1,113 @@ +package serviceaccount + +import ( + "context" + "fmt" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" +) + +func (r *ServiceAccountReconciler) ensureRoles(ctx context.Context, saName, saNamespace string) error { + subjects := []rbacv1.Subject{{Kind: "ServiceAccount", Name: saName, Namespace: saNamespace}} + + // Ensure the Service Account Namespace cluster-admin RoleBinding. + homeAdminRB := &rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: saName, + Namespace: saNamespace, + }, + Subjects: subjects, + RoleRef: rbacv1.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "ClusterRole", + Name: "cluster-admin", + }, + } + + if _, err := controllerutil.CreateOrUpdate(ctx, r.Client, homeAdminRB, func() error { + if homeAdminRB.Subjects == nil { + homeAdminRB.Subjects = subjects + } + + if homeAdminRB.RoleRef.Name != "cluster-admin" || + homeAdminRB.RoleRef.Kind != "ClusterRole" || + homeAdminRB.RoleRef.APIGroup != "rbac.authorization.k8s.io" { + homeAdminRB.RoleRef = rbacv1.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "ClusterRole", + Name: "cluster-admin", + } + } + + return nil + }); err != nil { + return err + } + + // Ensure the Service Account impersonator ClusterRole. + impersonatorName := fmt.Sprintf("%s-%s-impersonator", saNamespace, saName) + + impersonatorRules := []rbacv1.PolicyRule{{ + APIGroups: []string{""}, + Verbs: []string{"impersonate"}, + Resources: []string{"users"}, + ResourceNames: []string{fmt.Sprintf("system:serviceaccount:%s:%s", saNamespace, saName)}, + }} + + impersonatorCR := &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{Name: impersonatorName}, + Rules: impersonatorRules, + } + + if _, err := controllerutil.CreateOrUpdate(ctx, r.Client, impersonatorCR, func() error { + if len(impersonatorCR.Rules) == 0 { + impersonatorCR.Rules = impersonatorRules + } + if impersonatorCR.Rules[0].Verbs == nil { + impersonatorCR.Rules[0].Verbs = []string{"impersonate"} + } + if impersonatorCR.Rules[0].Resources == nil { + impersonatorCR.Rules[0].Resources = []string{"users"} + } + if impersonatorCR.Rules[0].ResourceNames == nil { + impersonatorCR.Rules[0].ResourceNames = []string{ + fmt.Sprintf("system:serviceaccount:%s:%s", saNamespace, saName), + } + } + + return nil + }); err != nil { + return err + } + + // Ensure the Service Account impersonator ClusterRoleBinding. + impersonatorRoleRef := rbacv1.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "ClusterRole", + Name: impersonatorName, + } + + impersonatorCRB := &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{Name: impersonatorName}, + Subjects: subjects, + RoleRef: impersonatorRoleRef, + } + + if _, err := controllerutil.CreateOrUpdate(ctx, r.Client, impersonatorCRB, func() error { + if impersonatorCRB.RoleRef.Kind != "ClusterRole" || + impersonatorCRB.RoleRef.APIGroup != "rbac.authorization.k8s.io" || + impersonatorCRB.RoleRef.Name != impersonatorName { + impersonatorCRB.RoleRef = impersonatorRoleRef + } + if len(impersonatorCRB.Subjects) == 0 { + impersonatorCRB.Subjects = subjects + } + + return nil + }); err != nil { + return err + } + + return nil +} diff --git a/controllers/serviceaccount/serviceaccount.go b/controllers/serviceaccount/serviceaccount.go new file mode 100644 index 0000000..7212f7e --- /dev/null +++ b/controllers/serviceaccount/serviceaccount.go @@ -0,0 +1,232 @@ +package serviceaccount + +import ( + "context" + "fmt" + + "github.com/go-logr/logr" + "github.com/pkg/errors" + capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/tools/clientcmd" + clientcmdapi "k8s.io/client-go/tools/clientcmd/api" + clientcmdlatest "k8s.io/client-go/tools/clientcmd/api/latest" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +type ServiceAccountReconciler struct { + proxyURL string + proxyCA string + + Client client.Client + Log logr.Logger +} + +type Option func(r *ServiceAccountReconciler) + +func WithClient(c client.Client) Option { + return func(r *ServiceAccountReconciler) { + r.Client = c + } +} + +func WithLogger(log logr.Logger) Option { + return func(r *ServiceAccountReconciler) { + r.Log = log + } +} + +func WithProxyCA(proxyCA string) Option { + return func(r *ServiceAccountReconciler) { + r.proxyCA = proxyCA + } +} + +func WithProxyURL(proxyURL string) Option { + return func(r *ServiceAccountReconciler) { + r.proxyURL = proxyURL + } +} + +func NewServiceAccountReconciler(opts ...Option) *ServiceAccountReconciler { + reconciler := new(ServiceAccountReconciler) + + for _, f := range opts { + f(reconciler) + } + + return reconciler +} + +func (r *ServiceAccountReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&corev1.ServiceAccount{}, r.forOption(ctx)). + Owns(&capsulev1beta2.GlobalTenantResource{}). + Complete(r) +} + +func (r *ServiceAccountReconciler) Reconcile(ctx context.Context, request ctrl.Request) (ctrl.Result, error) { + r.Log = r.Log.WithValues("Request.NamespacedName", request.NamespacedName) + + // Unmarshal ServiceAccount object. + sa := new(corev1.ServiceAccount) + if err := r.Client.Get(ctx, request.NamespacedName, sa); err != nil { + if apierrors.IsNotFound(err) { + r.Log.Info("Request object not found, could have been deleted after reconcile request") + + return reconcile.Result{}, nil + } + r.Log.Error(err, "Error reading the object") + + return ctrl.Result{}, err + } + + // Ensure the (Cluster)RoleBindings for the ServiceAccount. + if err := r.ensureRoles(ctx, sa.Name, sa.Namespace); err != nil { + return reconcile.Result{}, errors.Wrap(err, "error ensuring the role bindings for the service account") + } + + // Ensure ServiceAccount token. + if err := r.ensureSATokenSecret(ctx, sa.Name, sa.Namespace); err != nil { + return reconcile.Result{}, errors.Wrap(err, "error ensuring token of the service account") + } + + tokenSecret, err := r.getSATokenSecret(ctx, sa.Name, sa.Namespace) + if err != nil { + return reconcile.Result{}, errors.Wrap(err, "error getting token of the service account") + } + if tokenSecret.Data == nil { + r.Log.Info("ServiceAccount token data is missing. Requeueing.") + return reconcile.Result{Requeue: true}, nil + } + + // Build the kubeConfig for the ServiceAccount Tenant Owner. + config, err := r.buildKubeconfig(r.proxyURL, string(tokenSecret.Data[corev1.ServiceAccountTokenKey])) + if err != nil { + return reconcile.Result{}, errors.Wrap(err, "error building the tenant owner config") + } + + configRaw, err := clientcmd.Write(*config) + if err != nil { + return reconcile.Result{}, errors.Wrap(err, "error building the tenant owner config") + } + + secret := &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + Kind: "Secret", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s%s", sa.Name, SecretNameSuffixKubeconfig), + Namespace: sa.Namespace, + }, + Type: corev1.SecretTypeOpaque, + Data: map[string][]byte{ + SecretKeyKubeconfig: configRaw, + }, + } + if _, err = controllerutil.CreateOrUpdate(ctx, r.Client, secret, func() error { + return nil + }); err != nil { + return reconcile.Result{}, errors.Wrap(err, "error ensuring the kubeConfig secret") + } + + // If the option for distributing the kubeConfig to Tenant globally. + if sa.GetAnnotations()[ServiceAccountGlobalAnnotationKey] == ServiceAccountGlobalAnnotationValue { + // Get the Tenant owned by the ServiceAccount. + ownerName := fmt.Sprintf("system:serviceaccount:%s:%s", sa.GetNamespace(), sa.GetName()) + tenantList, err := r.listTenantsOwned(ctx, string(capsulev1beta2.ServiceAccountOwner), ownerName) + if err != nil { + return reconcile.Result{}, errors.Wrap(err, "error listing Tenants for owner") + } + if tenantList.Items == nil { + return reconcile.Result{}, errors.New("Tenant list for owner is empty") + } + + for _, tenant := range tenantList.Items { + // Ensure the GlobalTenantResource to distribute the kubeConfig Secret. + name := fmt.Sprintf("%s-%s%s", tenant.Name, sa.Name, GlobalTenantResourceSuffix) + if err = r.ensureGlobalTenantResource(ctx, name, tenant.Name, secret); err != nil { + return reconcile.Result{}, errors.Wrap(err, "error ensuring the kubeConfig globaltenantresource") + } + } + } + + r.Log.Info("ServiceAccount reconciliation completed") + + return reconcile.Result{}, nil +} + +// forOption is the option used to make reconciliation only of ServiceAccounts that both: +// - have the required addon annotation, and +// - are Tenant owners. +func (r *ServiceAccountReconciler) forOption(ctx context.Context) builder.ForOption { + return builder.WithPredicates( + predicate.And( + predicate.NewPredicateFuncs(func(object client.Object) bool { + return object.GetAnnotations()[ServiceAccountAddonAnnotationKey] == ServiceAccountAddonAnnotationValue + }), + predicate.NewPredicateFuncs(func(object client.Object) bool { + ownerName := fmt.Sprintf("system:serviceaccount:%s:%s", object.GetNamespace(), object.GetName()) + tntList, err := r.listTenantsOwned(ctx, string(capsulev1beta2.ServiceAccountOwner), ownerName) + return err == nil && tntList.Items != nil && len(tntList.Items) != 0 + }), + ), + ) +} + +// listTenantsOwned returns the first Tenant object owned by the ServiceAccount of which the name and the +// namespace are specified as arguments. +func (r *ServiceAccountReconciler) listTenantsOwned(ctx context.Context, ownerKind, ownerName string) (*capsulev1beta2.TenantList, error) { + tntList := &capsulev1beta2.TenantList{} + fields := client.MatchingFields{ + ".spec.owner.ownerkind": fmt.Sprintf("%s:%s", ownerKind, ownerName), + } + err := r.Client.List(ctx, tntList, fields) + + return tntList, err +} + +// buildKubeconfig returns a client-go/clientcmd/api.Config with a token and server URL specified as arguments. +// The server set is be the proxy configured at ServiceAccountReconciler-level. +func (r *ServiceAccountReconciler) buildKubeconfig(server, token string) (*clientcmdapi.Config, error) { + // Build the client API Config. + config := clientcmdapi.NewConfig() + config.APIVersion = clientcmdlatest.Version + config.Kind = "Config" + + // Build the client Config cluster. + cluster := clientcmdapi.NewCluster() + cluster.Server = server + + cluster.CertificateAuthorityData = []byte(r.proxyCA) + config.Clusters = map[string]*clientcmdapi.Cluster{ + "default": cluster, + } + + // Build the client Config AuthInfo. + authInfo := clientcmdapi.NewAuthInfo() + authInfo.Token = token + config.AuthInfos = map[string]*clientcmdapi.AuthInfo{ + "default": authInfo, + } + + // Build the client Config context. + contexts := make(map[string]*clientcmdapi.Context) + kctx := clientcmdapi.NewContext() + kctx.Cluster = KubeconfigClusterName + kctx.AuthInfo = KubeconfigUserName + kctx.Namespace = corev1.NamespaceDefault + contexts[KubeconfigContextName] = kctx + config.Contexts = contexts + config.CurrentContext = KubeconfigContextName + + return config, nil +} diff --git a/controllers/serviceaccount/tokens.go b/controllers/serviceaccount/tokens.go new file mode 100644 index 0000000..4702d0d --- /dev/null +++ b/controllers/serviceaccount/tokens.go @@ -0,0 +1,71 @@ +package serviceaccount + +import ( + "context" + "fmt" + "github.com/pkg/errors" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" +) + +// ensureSATokenSecret ensures that a token Secret is present for the Service Account of which the name and the namespace +// are specified as arguments. +func (r *ServiceAccountReconciler) ensureSATokenSecret(ctx context.Context, name, namespace string) error { + // If the token does not exist, create it. + if _, err := r.getSATokenSecret(ctx, name, namespace); err != nil { + if errors.Is(err, ErrServiceAccountTokenNotFound) { + tokenSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s%s", name, SecretNameSuffixToken), + Namespace: namespace, + Annotations: map[string]string{ + corev1.ServiceAccountNameKey: name, + }, + }, + Type: corev1.SecretTypeServiceAccountToken, + } + if _, err = controllerutil.CreateOrUpdate(ctx, r.Client, tokenSecret, func() error { + return nil + }); err != nil { + return err + } + + return nil + } + return err + } + + return nil +} + +// getSATokenSecret returns, if exists, the token Secret of the Service Account of which the name and the namespace +// are specified as arguments. +func (r *ServiceAccountReconciler) getSATokenSecret(ctx context.Context, saName, saNamespace string) (*corev1.Secret, error) { + saTokenList := new(corev1.SecretList) + // TODO: filter by Service Account-type and Namespace. Need index by Secret type. + if err := r.Client.List(ctx, saTokenList); err != nil { + return nil, ErrServiceAccountTokenNotFound + } + + if len(saTokenList.Items) == 0 { + return nil, ErrServiceAccountTokenNotFound + } + + var tokenSecret *corev1.Secret + for _, v := range saTokenList.Items { + v := v + switch v.Type { + case corev1.SecretTypeServiceAccountToken: + if v.Namespace == saNamespace && v.Annotations[corev1.ServiceAccountNameKey] == saName { + return &v, nil + } + } + } + if tokenSecret == nil { + return nil, ErrServiceAccountTokenNotFound + } + + return tokenSecret, nil +} diff --git a/e2e/serviceaccount_test.go b/e2e/serviceaccount_test.go new file mode 100644 index 0000000..66559de --- /dev/null +++ b/e2e/serviceaccount_test.go @@ -0,0 +1,310 @@ +//go:build e2e + +package e2e + +import ( + "context" + "fmt" + "github.com/maxgio92/capsule-addon-flux/controllers/serviceaccount" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/json" + "k8s.io/client-go/tools/clientcmd" + "time" +) + +var _ = Describe("Creating a new ServiceAccount", func() { + var ( + sa *corev1.ServiceAccount + configSecret, tokenSecret *corev1.Secret + err error + ) + + Context("in the Tenant system Namespace", Ordered, func() { + BeforeAll(func() { + Expect(adminClient.Create(context.TODO(), &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: TenantSystemNamespace, + }, + })).Should(Succeed()) + }) + + AfterAll(func() { + Eventually(func() error { + return adminClient.Delete(context.TODO(), &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: TenantSystemNamespace, + }, + }) + }).Should(Succeed()) + }) + + Context("not set as owner of the Tenant", func() { + BeforeEach(func() { + Expect(adminClient.Create(context.TODO(), &capsulev1beta2.Tenant{ + ObjectMeta: metav1.ObjectMeta{ + Name: TenantName, + }, + Spec: capsulev1beta2.TenantSpec{ + Owners: []capsulev1beta2.OwnerSpec{}, + }, + })).Should(Succeed()) + }) + BeforeEach(func() { + sa = &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: TenantOwnerSAName, + Namespace: TenantSystemNamespace, + }, + } + err = adminClient.Create(context.TODO(), sa) + Expect(err).ShouldNot(HaveOccurred()) + + configSecret = new(corev1.Secret) + err = adminClient.Get(context.TODO(), types.NamespacedName{ + Namespace: TenantSystemNamespace, + Name: fmt.Sprintf("%s%s", + TenantOwnerSAName, serviceaccount.SecretNameSuffixKubeconfig), + }, configSecret) + }) + AfterEach(func() { + Expect(adminClient.Delete(context.TODO(), &capsulev1beta2.Tenant{ + ObjectMeta: metav1.ObjectMeta{ + Name: TenantName, + }, + })).Should(Succeed()) + }) + AfterEach(func() { + Expect(adminClient.Delete(context.TODO(), sa)).Should(Succeed()) + }) + It("should do nothing", func() { + Expect(err).ShouldNot(Succeed()) + Expect(configSecret.Data).To(BeNil()) + }) + }) + Context("set as owner of the Tenant", func() { + BeforeEach(func() { + Expect(adminClient.Create(context.TODO(), &capsulev1beta2.Tenant{ + ObjectMeta: metav1.ObjectMeta{ + Name: TenantName, + }, + Spec: capsulev1beta2.TenantSpec{ + Owners: []capsulev1beta2.OwnerSpec{ + { + Kind: "ServiceAccount", + Name: fmt.Sprintf("system:serviceaccount:%s:%s", + TenantSystemNamespace, TenantOwnerSAName), + }, + }, + }, + })).Should(Succeed()) + }) + AfterEach(func() { + Expect(adminClient.Delete(context.TODO(), &capsulev1beta2.Tenant{ + ObjectMeta: metav1.ObjectMeta{ + Name: TenantName, + }, + })).Should(Succeed()) + }) + + When("does not have the annotation to enable the addon", func() { + BeforeEach(func() { + sa = &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: TenantOwnerSAName, + Namespace: TenantSystemNamespace, + }, + } + err = adminClient.Create(context.TODO(), sa) + Expect(err).ShouldNot(HaveOccurred()) + + configSecret = new(corev1.Secret) + err = adminClient.Get(context.TODO(), types.NamespacedName{ + Namespace: TenantSystemNamespace, + Name: fmt.Sprintf("%s%s", + TenantOwnerSAName, serviceaccount.SecretNameSuffixKubeconfig), + }, configSecret) + }) + AfterEach(func() { + Expect(adminClient.Delete(context.TODO(), sa)).Should(Succeed()) + }) + It("should do nothing", func() { + Expect(err).ShouldNot(Succeed()) + Expect(configSecret.Data).To(BeNil()) + }) + }) + + When("has the annotation to enable the addon", func() { + BeforeEach(func() { + sa = &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: TenantOwnerSAName, + Namespace: TenantSystemNamespace, + Annotations: map[string]string{ + serviceaccount.ServiceAccountAddonAnnotationKey: serviceaccount.ServiceAccountAddonAnnotationValue, + }, + }, + } + err = adminClient.Create(context.TODO(), sa) + Expect(err).ShouldNot(HaveOccurred()) + }) + + AfterEach(func() { + Expect(adminClient.Delete(context.TODO(), sa)).Should(Succeed()) + }) + + It("should generate the kubeConfig Secret", func() { + Eventually(func(g Gomega) { + configSecret = new(corev1.Secret) + err = adminClient.Get(context.TODO(), types.NamespacedName{ + Namespace: TenantSystemNamespace, + Name: fmt.Sprintf("%s%s", + TenantOwnerSAName, serviceaccount.SecretNameSuffixKubeconfig), + }, configSecret) + g.Expect(err).Should(Succeed()) + g.Expect(configSecret.Data).ToNot(BeNil()) + g.Expect(configSecret.Data[serviceaccount.SecretKeyKubeconfig]).ToNot(BeEmpty()) + }, 20*time.Second, 1*time.Second).Should(Succeed()) + }) + + It("should generate the Service Account token Secret", func() { + Eventually(func(g Gomega) { + tokenSecret = new(corev1.Secret) + err = adminClient.Get(context.TODO(), types.NamespacedName{ + Namespace: TenantSystemNamespace, + Name: fmt.Sprintf("%s%s", + TenantOwnerSAName, serviceaccount.SecretNameSuffixToken), + }, tokenSecret) + g.Expect(err).Should(Succeed()) + g.Expect(tokenSecret.Data).ToNot(BeNil()) + g.Expect(tokenSecret.Data[corev1.ServiceAccountTokenKey]).ToNot(BeEmpty()) + }, 20*time.Second, 1*time.Second).Should(Succeed()) + }) + + It("should generate the Tenant system Namespace RoleBinding", func() { + Eventually(func(g Gomega) { + rb := new(rbacv1.RoleBinding) + err = adminClient.Get(context.TODO(), types.NamespacedName{ + Namespace: TenantSystemNamespace, + Name: TenantOwnerSAName, + }, rb) + g.Expect(err).Should(Succeed()) + g.Expect(rb.RoleRef.Kind).To(Equal("ClusterRole")) + g.Expect(rb.RoleRef.Name).To(Equal("cluster-admin")) + + By("binding it to the Tenant Owner", func() { + g.Expect(len(rb.Subjects)).To(Equal(1)) + g.Expect(rb.Subjects[0].Kind).To(Equal("ServiceAccount")) + g.Expect(rb.Subjects[0].Name).To(Equal(TenantOwnerSAName)) + g.Expect(rb.Subjects[0].Namespace).To(Equal(TenantSystemNamespace)) + }) + }, 20*time.Second, 1*time.Second).Should(Succeed()) + }) + + It("should generate the impersonator ClusterRole", func() { + Eventually(func(g Gomega) { + cr := new(rbacv1.ClusterRole) + err = adminClient.Get(context.TODO(), types.NamespacedName{ + Name: fmt.Sprintf("%s-%s-impersonator", + TenantSystemNamespace, TenantOwnerSAName), + }, cr) + g.Expect(err).Should(Succeed()) + g.Expect(len(cr.Rules)).To(Equal(1)) + }, 20*time.Second, 1*time.Second).Should(Succeed()) + }) + + It("should generate the impersonator ClusterRoleBinding", func() { + Eventually(func(g Gomega) { + crb := new(rbacv1.ClusterRoleBinding) + err = adminClient.Get(context.TODO(), types.NamespacedName{ + Name: fmt.Sprintf("%s-%s-impersonator", + TenantSystemNamespace, TenantOwnerSAName), + }, crb) + g.Expect(err).Should(Succeed()) + g.Expect(crb.RoleRef.Kind).To(Equal("ClusterRole")) + g.Expect(crb.RoleRef.Name).To(Equal( + fmt.Sprintf("%s-%s-impersonator", TenantSystemNamespace, TenantOwnerSAName), + )) + + By("binding it to the Tenant Owner", func() { + g.Expect(len(crb.Subjects)).To(Equal(1)) + g.Expect(crb.Subjects[0].Kind).To(Equal("ServiceAccount")) + g.Expect(crb.Subjects[0].Name).To(Equal(TenantOwnerSAName)) + g.Expect(crb.Subjects[0].Namespace).To(Equal(TenantSystemNamespace)) + }) + }, 20*time.Second, 1*time.Second).Should(Succeed()) + }) + + It("should not generate GlobalTenantResource with the kubeConfig Secret", func() { + Eventually(func(g Gomega) { + gtr := new(capsulev1beta2.GlobalTenantResource) + err = adminClient.Get(context.TODO(), types.NamespacedName{ + Name: fmt.Sprintf("%s-%s%s", + TenantName, TenantOwnerSAName, serviceaccount.GlobalTenantResourceSuffix), + }, gtr) + g.Expect(err).Should(HaveOccurred()) + g.Expect(gtr.Spec.TenantSelector.MatchLabels).To(BeNil()) + g.Expect(gtr.Spec.TenantSelector.MatchExpressions).To(BeNil()) + g.Expect(gtr.Spec.Resources).To(BeNil()) + }, 20*time.Second, 1*time.Second).Should(Succeed()) + }) + }) + + When("has the annotation to enable the addon and the annotation to make the kubeconfig global", func() { + BeforeEach(func() { + sa = &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: TenantOwnerSAName, + Namespace: TenantSystemNamespace, + Annotations: map[string]string{ + serviceaccount.ServiceAccountAddonAnnotationKey: serviceaccount.ServiceAccountAddonAnnotationValue, + serviceaccount.ServiceAccountGlobalAnnotationKey: serviceaccount.ServiceAccountGlobalAnnotationValue, + }, + }, + } + err = adminClient.Create(context.TODO(), sa) + Expect(err).ShouldNot(HaveOccurred()) + }) + + AfterEach(func() { + Expect(adminClient.Delete(context.TODO(), sa)).Should(Succeed()) + }) + + It("should generate GlobalTenantResource with the kubeConfig Secret", func() { + Eventually(func(g Gomega) { + gtr := new(capsulev1beta2.GlobalTenantResource) + err = adminClient.Get(context.TODO(), types.NamespacedName{ + Name: fmt.Sprintf("%s-%s%s", + TenantName, TenantOwnerSAName, serviceaccount.GlobalTenantResourceSuffix), + }, gtr) + g.Expect(err).Should(Succeed()) + g.Expect(len(gtr.Spec.Resources)).To(Equal(1)) + g.Expect(gtr.Spec.TenantSelector.MatchLabels["kubernetes.io/metadata.name"]).To(Equal( + TenantName, + )) + + By("setting the server to Capsule Proxy", func() { + kcSecret := &corev1.Secret{} + g.Expect(len(gtr.Spec.Resources[0].RawItems)).To(Equal(1)) + g.Expect(json.Unmarshal(gtr.Spec.Resources[0].RawItems[0].Raw, kcSecret)).To(Succeed()) + g.Expect(kcSecret.Data[serviceaccount.SecretKeyKubeconfig]).ToNot(BeEmpty()) + + kc, err := clientcmd.Load(kcSecret.Data[serviceaccount.SecretKeyKubeconfig]) + g.Expect(err).Should(Succeed()) + g.Expect(kc).ToNot(BeNil()) + g.Expect(kc.Clusters[serviceaccount.KubeconfigClusterName].Server).To(Equal( + fmt.Sprintf("https://capsule-proxy.%s.svc:9001", NamespaceCapsuleProxy), + )) + g.Expect(kc.AuthInfos[serviceaccount.KubeconfigUserName].Token).ToNot(BeEmpty()) + }) + }, 20*time.Second, 1*time.Second).Should(Succeed()) + }) + }) + }) + }) +}) diff --git a/e2e/suite_test.go b/e2e/suite_test.go new file mode 100644 index 0000000..54c83c7 --- /dev/null +++ b/e2e/suite_test.go @@ -0,0 +1,163 @@ +//go:build e2e + +package e2e + +import ( + "context" + "fmt" + "os" + "testing" + "time" + + cmd "github.com/maxgio92/capsule-addon-flux/cmd/manager" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2" + "go.uber.org/zap/zapcore" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/config" + "sigs.k8s.io/controller-runtime/pkg/log/zap" +) + +const ( + k8sVersionDefault = "v1.27.3" + + KindClusterName = "capsule-addon-fluxcd-e2e" + KindNodeImageRepo = "kindest/node" + + HelmChartVersionCapsule = "0.6.0" + HelmChartVersionCapsuleProxy = "0.5.2" + HelmChartURLCapsule = "oci://ghcr.io/projectcapsule/charts/capsule" + HelmChartURLCapsuleProxy = "oci://ghcr.io/projectcapsule/charts/capsule-proxy" + NamespaceCapsule = "capsule-system" + NamespaceCapsuleProxy = "capsule-system" + + EnvKubernetesVersion = "KUBERNETES_VERSION" + EnvChartVersionCapsule = "CAPSULE_HELM_VERSION" + EnvChartVersionCapsuleProxy = "CAPSULE_PROXY_HELM_VERSION" + + TimeoutKindSeconds = 120 + TimeoutHelmInstallSeconds = 120 + + KubeConfigPath = "/tmp/kubeconfig" + + TenantName = "oil" + TenantSystemNamespace = "oil-system" + TenantOwnerSAName = "gitops-reconciler" + + CapsuleProxyCAFilePath = "/tmp/capsule-proxy-ca.crt" +) + +var ( + adminConfig *rest.Config + adminClient client.Client +) + +func TestControllers(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecs(t, "Controller Suite") +} + +var _ = BeforeSuite(func() { + k8sVersion := os.Getenv(EnvKubernetesVersion) + if k8sVersion == "" { + k8sVersion = k8sVersionDefault + } + + capsuleHelmVersion := os.Getenv(EnvChartVersionCapsule) + if capsuleHelmVersion == "" { + capsuleHelmVersion = HelmChartVersionCapsule + } + + capsuleProxyHelmVersion := os.Getenv(EnvChartVersionCapsuleProxy) + if capsuleProxyHelmVersion == "" { + capsuleProxyHelmVersion = HelmChartVersionCapsuleProxy + } + + var ( + err error + kubeConfig string + ) + + By("Ensuring a Kind cluster", func() { + kubeConfig, err = ensureKindCluster( + KindClusterName, + fmt.Sprintf("%s:%s", KindNodeImageRepo, k8sVersion), + time.Second*TimeoutKindSeconds, + KubeConfigPath, + ) + Expect(err).ShouldNot(HaveOccurred()) + Expect(kubeConfig).ToNot(BeEmpty()) + + err = os.Setenv("KUBECONFIG", KubeConfigPath) + Expect(err).ShouldNot(HaveOccurred()) + }) + + By("Ensuring Capsule is installed", func() { + err = ensureCapsule(capsuleHelmVersion, kubeConfig) + Expect(err).ShouldNot(HaveOccurred()) + }) + + By("Ensuring Capsule Proxy is installed", func() { + err = ensureCapsuleProxy(capsuleProxyHelmVersion, kubeConfig) + Expect(err).ShouldNot(HaveOccurred()) + }) + + By("Building the e2e client", func() { + adminConfig, err = config.GetConfig() + Expect(err).ToNot(HaveOccurred()) + Expect(adminConfig).ToNot(BeNil()) + + Expect(scheme.AddToScheme(scheme.Scheme)).NotTo(HaveOccurred()) + Expect(capsulev1beta2.AddToScheme(scheme.Scheme)).NotTo(HaveOccurred()) + + c, err := client.New(adminConfig, client.Options{Scheme: scheme.Scheme}) + Expect(err).ToNot(HaveOccurred()) + Expect(c).ToNot(BeNil()) + + adminClient = &e2eClient{Client: c} + }) + + By("Getting the CA of the Capsule Proxy", func() { + caSecret := new(corev1.Secret) + err = adminClient.Get(context.TODO(), types.NamespacedName{ + Namespace: "capsule-system", + Name: "capsule-proxy", + }, caSecret) + Expect(err).ToNot(HaveOccurred()) + Expect(caSecret).ToNot(BeNil()) + Expect(caSecret.Data).ToNot(BeNil()) + Expect(caSecret.Data["ca"]).ToNot(BeEmpty()) + + err = os.WriteFile(CapsuleProxyCAFilePath, caSecret.Data["ca"], 0644) + Expect(err).ToNot(HaveOccurred()) + }) + + By("Starting the manager", func() { + mo := &cmd.Options{ + ProxyURL: fmt.Sprintf("https://capsule-proxy.%s.svc:9001", NamespaceCapsuleProxy), + ProxyCAPath: CapsuleProxyCAFilePath, + SetupLog: ctrl.Log.WithName("setup"), + Zo: &zap.Options{ + EncoderConfigOptions: append([]zap.EncoderConfigOption{}, func(config *zapcore.EncoderConfig) { + config.EncodeTime = zapcore.ISO8601TimeEncoder + }), + }, + } + go func() { + err = mo.Run(nil, nil) + Expect(err).ShouldNot(HaveOccurred()) + }() + }) +}) + +var _ = AfterSuite(func() { + Expect(deleteKindCluster(KindClusterName)).ShouldNot(HaveOccurred()) + Expect(os.Remove(CapsuleProxyCAFilePath)).ShouldNot(HaveOccurred()) +}) diff --git a/e2e/utils_test.go b/e2e/utils_test.go new file mode 100644 index 0000000..efc201f --- /dev/null +++ b/e2e/utils_test.go @@ -0,0 +1,250 @@ +//go:build e2e + +package e2e + +import ( + "context" + "fmt" + "os" + "time" + + "github.com/pkg/errors" + helmaction "helm.sh/helm/v3/pkg/action" + helmloader "helm.sh/helm/v3/pkg/chart/loader" + helmcli "helm.sh/helm/v3/pkg/cli" + registry "helm.sh/helm/v3/pkg/registry" + helmrelease "helm.sh/helm/v3/pkg/release" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/client-go/discovery" + "k8s.io/client-go/discovery/cached/memory" + _ "k8s.io/client-go/plugin/pkg/client/auth" + "k8s.io/client-go/rest" + "k8s.io/client-go/restmapper" + "k8s.io/client-go/tools/clientcmd" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/kind/pkg/cluster" +) + +func ensureKindCluster(name, imageName string, wait time.Duration, kubeConfigPath ...string) (string, error) { + provider := cluster.NewProvider( + cluster.ProviderWithDocker(), + ) + + // create the cluster + if err := provider.Create( + name, + cluster.CreateWithNodeImage(imageName), + cluster.CreateWithWaitForReady(wait), + cluster.CreateWithDisplayUsage(true), + cluster.CreateWithDisplaySalutation(true), + ); err != nil { + return "", errors.Wrap(err, "failed to create cluster") + } + + kubeconfig, err := provider.KubeConfig(name, false) + if err != nil { + return "", err + } + + if len(kubeConfigPath) > 0 { + if err = provider.ExportKubeConfig(name, kubeConfigPath[0], false); err != nil { + return "", err + } + } + + return kubeconfig, nil +} + +func deleteKindCluster(name string) error { + provider := cluster.NewProvider( + cluster.ProviderWithDocker(), + ) + if err := provider.Delete(name, ""); err != nil { + return err + } + + return nil +} + +func ensureCapsule(version, kubeConfig string) error { + if _, err := helmInstall( + "capsule", + NamespaceCapsule, + HelmChartURLCapsule, + version, + nil, + kubeConfig); err != nil { + return errors.Wrap(err, "error installing capsule-proxy") + } + return nil +} + +func ensureCapsuleProxy(version, kubeConfig string) error { + if _, err := helmInstall( + "capsule-proxy", + NamespaceCapsuleProxy, + HelmChartURLCapsuleProxy, + version, + nil, + kubeConfig); err != nil { + return errors.Wrap(err, "error installing capsule-proxy") + } + return nil +} + +func helmInstall(releaseName, releaseNamespace, chart, version string, values map[string]interface{}, kubeConfig string) (*helmrelease.Release, error) { + restClientGetter := newRESTClientGetter("", kubeConfig) + + actionConfig := new(helmaction.Configuration) + if err := actionConfig.Init( + restClientGetter, + releaseNamespace, + os.Getenv("HELM_DRIVER"), + func(format string, v ...interface{}) { + fmt.Sprintf(format, v) + }); err != nil { + return nil, err + } + + registryClient, err := registry.NewClient() + if err != nil { + return nil, err + } + actionConfig.RegistryClient = registryClient + restClientGetter.Namespace = releaseNamespace + + client := helmaction.NewInstall(actionConfig) + client.Namespace = releaseNamespace + client.ReleaseName = releaseName + client.ChartPathOptions.Version = version + client.CreateNamespace = true + client.Wait = true + client.WaitForJobs = true + client.Timeout = TimeoutHelmInstallSeconds * time.Second + + cp, err := client.ChartPathOptions.LocateChart(chart, helmcli.New()) + if err != nil { + return nil, err + } + + // Check chart dependencies to make sure all are present in /charts + chartRequested, err := helmloader.Load(cp) + if err != nil { + return nil, err + } + + rel, err := client.Run(chartRequested, values) + if err != nil { + return nil, err + } + + return rel, nil +} + +type SimpleRESTClientGetter struct { + Namespace string + KubeConfig string +} + +func newRESTClientGetter(namespace, kubeConfig string) *SimpleRESTClientGetter { + return &SimpleRESTClientGetter{ + Namespace: namespace, + KubeConfig: kubeConfig, + } +} + +func (c *SimpleRESTClientGetter) ToRESTConfig() (*rest.Config, error) { + config, err := clientcmd.RESTConfigFromKubeConfig([]byte(c.KubeConfig)) + if err != nil { + return nil, err + } + return config, nil +} + +func (c *SimpleRESTClientGetter) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) { + config, err := c.ToRESTConfig() + if err != nil { + return nil, err + } + + // The more groups you have, the more discovery requests you need to make. + // given 25 groups (our groups + a few custom conf) with one-ish version each, discovery needs to make 50 requests + // double it just so we don't end up here again for a while. This config is only used for discovery. + config.Burst = 100 + + discoveryClient, _ := discovery.NewDiscoveryClientForConfig(config) + return memory.NewMemCacheClient(discoveryClient), nil +} + +func (c *SimpleRESTClientGetter) ToRESTMapper() (meta.RESTMapper, error) { + discoveryClient, err := c.ToDiscoveryClient() + if err != nil { + return nil, err + } + + mapper := restmapper.NewDeferredDiscoveryRESTMapper(discoveryClient) + expander := restmapper.NewShortcutExpander(mapper, discoveryClient) + return expander, nil +} + +func (c *SimpleRESTClientGetter) ToRawKubeConfigLoader() clientcmd.ClientConfig { + loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() + // Use the standard defaults for this client command. + // DEPRECATED: remove and replace with something more accurate. + loadingRules.DefaultClientConfig = &clientcmd.DefaultClientConfig + + overrides := &clientcmd.ConfigOverrides{ClusterDefaults: clientcmd.ClusterDefaults} + overrides.Context.Namespace = c.Namespace + + return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, overrides) +} + +type e2eClient struct { + client.Client +} + +func (e *e2eClient) sleep() { + time.Sleep(500 * time.Millisecond) +} + +func (e *e2eClient) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { + defer e.sleep() + + return e.Client.Get(ctx, key, obj, opts...) +} + +func (e *e2eClient) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error { + defer e.sleep() + + return e.Client.List(ctx, list, opts...) +} + +func (e *e2eClient) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) error { + defer e.sleep() + + return e.Client.Create(ctx, obj, opts...) +} + +func (e *e2eClient) Delete(ctx context.Context, obj client.Object, opts ...client.DeleteOption) error { + defer e.sleep() + + return e.Client.Delete(ctx, obj, opts...) +} + +func (e *e2eClient) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { + defer e.sleep() + + return e.Client.Update(ctx, obj, opts...) +} + +func (e *e2eClient) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error { + defer e.sleep() + + return e.Client.Patch(ctx, obj, patch, opts...) +} + +func (e *e2eClient) DeleteAllOf(ctx context.Context, obj client.Object, opts ...client.DeleteAllOfOption) error { + defer e.sleep() + + return e.Client.DeleteAllOf(ctx, obj, opts...) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..7bc3508 --- /dev/null +++ b/go.mod @@ -0,0 +1,158 @@ +module github.com/maxgio92/capsule-addon-flux + +go 1.19 + +require ( + github.com/go-logr/logr v1.3.0 + github.com/onsi/ginkgo/v2 v2.13.1 + github.com/onsi/gomega v1.30.0 + github.com/pkg/errors v0.9.1 + github.com/projectcapsule/capsule v0.4.2 + github.com/spf13/cobra v1.7.0 + go.uber.org/zap v1.26.0 + helm.sh/helm/v3 v3.13.3 + k8s.io/api v0.28.4 + k8s.io/apimachinery v0.28.4 + k8s.io/client-go v0.28.4 + sigs.k8s.io/controller-runtime v0.16.3 + sigs.k8s.io/kind v0.20.0 +) + +require ( + github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect + github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/BurntSushi/toml v1.3.2 // indirect + github.com/MakeNowJust/heredoc v1.0.0 // indirect + github.com/Masterminds/goutils v1.1.1 // indirect + github.com/Masterminds/semver/v3 v3.2.1 // indirect + github.com/Masterminds/sprig/v3 v3.2.3 // indirect + github.com/Masterminds/squirrel v1.5.4 // indirect + github.com/Microsoft/hcsshim v0.11.0 // indirect + github.com/alessio/shellescape v1.4.1 // indirect + github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/chai2010/gettext-go v1.0.2 // indirect + github.com/containerd/containerd v1.7.6 // indirect + github.com/cyphar/filepath-securejoin v0.2.4 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/docker/cli v24.0.6+incompatible // indirect + github.com/docker/distribution v2.8.2+incompatible // indirect + github.com/docker/docker v24.0.7+incompatible // indirect + github.com/docker/docker-credential-helpers v0.7.0 // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-metrics v0.0.1 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/evanphx/json-patch v5.6.0+incompatible // indirect + github.com/evanphx/json-patch/v5 v5.7.0 // indirect + github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect + github.com/fatih/color v1.13.0 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/go-errors/errors v1.4.2 // indirect + github.com/go-gorp/gorp/v3 v3.1.0 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-logr/zapr v1.2.4 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.3 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/gobwas/glob v0.2.3 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/btree v1.0.1 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect + github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2 // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect + github.com/google/uuid v1.3.1 // indirect + github.com/gorilla/mux v1.8.0 // indirect + github.com/gosuri/uitable v0.0.4 // indirect + github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/huandu/xstrings v1.4.0 // indirect + github.com/imdario/mergo v0.3.13 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jmoiron/sqlx v1.3.5 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.16.0 // indirect + github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect + github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect + github.com/lib/pq v1.10.9 // 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/mattn/go-runewidth v0.0.9 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/go-wordwrap v1.0.1 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/moby/locker v1.0.1 // indirect + github.com/moby/spdystream v0.2.0 // indirect + github.com/moby/term v0.5.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/morikuni/aec v1.0.0 // 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-rc5 // indirect + github.com/pelletier/go-toml v1.9.5 // indirect + github.com/peterbourgon/diskv v2.0.1+incompatible // indirect + github.com/prometheus/client_golang v1.17.0 // indirect + github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect + github.com/prometheus/common v0.44.0 // indirect + github.com/prometheus/procfs v0.11.1 // indirect + github.com/rubenv/sql-migrate v1.5.2 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/shopspring/decimal v1.3.1 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/spf13/cast v1.5.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xeipuuv/gojsonschema v1.2.0 // indirect + github.com/xlab/treeprint v1.2.0 // indirect + go.opentelemetry.io/otel v1.14.0 // indirect + go.opentelemetry.io/otel/trace v1.14.0 // indirect + go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/crypto v0.14.0 // indirect + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/oauth2 v0.13.0 // indirect + golang.org/x/sync v0.5.0 // indirect + golang.org/x/sys v0.14.0 // indirect + golang.org/x/term v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + golang.org/x/time v0.3.0 // indirect + golang.org/x/tools v0.14.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect + google.golang.org/grpc v1.56.3 // indirect + google.golang.org/protobuf v1.31.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/apiextensions-apiserver v0.28.4 // indirect + k8s.io/apiserver v0.28.4 // indirect + k8s.io/cli-runtime v0.28.4 // indirect + k8s.io/component-base v0.28.4 // indirect + k8s.io/klog/v2 v2.100.1 // indirect + k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect + k8s.io/kubectl v0.28.4 // indirect + k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect + oras.land/oras-go v1.2.4 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 // indirect + sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..0a675e4 --- /dev/null +++ b/go.sum @@ -0,0 +1,588 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= +github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= +github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= +github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= +github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= +github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= +github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= +github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/hcsshim v0.11.0 h1:7EFNIY4igHEXUdj1zXgAyU3fLc7QfOKHbkldRVTBdiM= +github.com/Microsoft/hcsshim v0.11.0/go.mod h1:OEthFdQv/AD2RAdzR6Mm1N1KPCztGKDurW1Z8b8VGMM= +github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= +github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY= +github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= +github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng= +github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ= +github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +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/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= +github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= +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/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= +github.com/containerd/containerd v1.7.6 h1:oNAVsnhPoy4BTPQivLgTzI9Oleml9l/+eYIDYXRCYo8= +github.com/containerd/containerd v1.7.6/go.mod h1:SY6lrkkuJT40BVNO37tlYTSnKJnP5AXBc0fhx0q+TJ4= +github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= +github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= +github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aBfCb7iqHmDEIp6fBvC/hQUddQfg+3qdYjwzaiP9Hnc= +github.com/docker/cli v24.0.6+incompatible h1:fF+XCQCgJjjQNIMjzaSmiKJSCcfcXb3TWTcc7GAneOY= +github.com/docker/cli v24.0.6+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.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= +github.com/docker/docker v24.0.7+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/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= +github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= +github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= +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/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= +github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/evanphx/json-patch/v5 v5.7.0 h1:nJqP7uwL84RJInrohHfW0Fx3awjbm8qZeFv0nW9SYGc= +github.com/evanphx/json-patch/v5 v5.7.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= +github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= +github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI= +github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= +github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-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 v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +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-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo= +github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +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 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/gobuffalo/logger v1.0.6 h1:nnZNpxYo0zx+Aj9RfMPBm+x9zAU2OayFh/xrAWi34HU= +github.com/gobuffalo/packd v1.0.1 h1:U2wXfRr4E9DH8IdsDLlRFwTZTK7hLfq9qT/QHXGVe/0= +github.com/gobuffalo/packr/v2 v2.8.3 h1:xE1yzvnO56cUC0sTpKR3DIbxZgB54AftTFMhB2XEWlY= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/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.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.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +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/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k= +github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +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.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2 h1:SJ+NtwL6QaZ21U+IrK7d0gGgpjGGvd2kz+FzTHVzdqI= +github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2/go.mod h1:Tv1PlzqC9t8wNnpPdctvtSUOPUUg4SHeE6vR1Ir2hmg= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= +github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= +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/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= +github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= +github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= +github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= +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.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= +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/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= +github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/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.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/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/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= +github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= +github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= +github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI= +github.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY= +github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +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.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.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/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/miekg/dns v1.1.25 h1:dFwPR6SfLtrSwgDcIq2bcU/gVutB4sNApq2HBdqcakg= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= +github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-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/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= +github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/onsi/ginkgo/v2 v2.13.1 h1:LNGfMbR2OVGBfXjvRZIZ2YCTQdGKtPLvuI1rMCCj3OU= +github.com/onsi/ginkgo/v2 v2.13.1/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM= +github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= +github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= +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-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= +github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +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/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= +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/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= +github.com/projectcapsule/capsule v0.4.2 h1:8UzZxRmrWoeXW0idQwQFlwEnxkPcWUyi1lZXbm186Og= +github.com/projectcapsule/capsule v0.4.2/go.mod h1:uKMqO1wInZjP/prMW/G88LsLC2eYeSKyRTMffmYkjpU= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +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.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.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= +github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= +github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rubenv/sql-migrate v1.5.2 h1:bMDqOnrJVV/6JQgQ/MxOpU+AdO8uzYYA/TxFUBzFtS0= +github.com/rubenv/sql-migrate v1.5.2/go.mod h1:H38GW8Vqf8F0Su5XignRyaRcbXbJunSWxs+kmzlg0Is= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= +github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= +github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= +github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= +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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/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.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +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= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI= +github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE= +github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM= +go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU= +go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M= +go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8= +go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY= +go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +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-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= +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-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-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-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +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-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +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.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +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-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-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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +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-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/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.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.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.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +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-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-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +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.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= +golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= +gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +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.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 h1:0nDDozoAU19Qb2HwhXadU8OcsiO/09cnTqhUtq2MEOM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc= +google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= +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.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +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.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/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.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= +helm.sh/helm/v3 v3.13.3 h1:0zPEdGqHcubehJHP9emCtzRmu8oYsJFRrlVF3TFj8xY= +helm.sh/helm/v3 v3.13.3/go.mod h1:3OKO33yI3p4YEXtTITN2+4oScsHeQe71KuzhlZ+aPfg= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +k8s.io/api v0.28.4 h1:8ZBrLjwosLl/NYgv1P7EQLqoO8MGQApnbgH8tu3BMzY= +k8s.io/api v0.28.4/go.mod h1:axWTGrY88s/5YE+JSt4uUi6NMM+gur1en2REMR7IRj0= +k8s.io/apiextensions-apiserver v0.28.4 h1:AZpKY/7wQ8n+ZYDtNHbAJBb+N4AXXJvyZx6ww6yAJvU= +k8s.io/apiextensions-apiserver v0.28.4/go.mod h1:pgQIZ1U8eJSMQcENew/0ShUTlePcSGFq6dxSxf2mwPM= +k8s.io/apimachinery v0.28.4 h1:zOSJe1mc+GxuMnFzD4Z/U1wst50X28ZNsn5bhgIIao8= +k8s.io/apimachinery v0.28.4/go.mod h1:wI37ncBvfAoswfq626yPTe6Bz1c22L7uaJ8dho83mgg= +k8s.io/apiserver v0.28.4 h1:BJXlaQbAU/RXYX2lRz+E1oPe3G3TKlozMMCZWu5GMgg= +k8s.io/apiserver v0.28.4/go.mod h1:Idq71oXugKZoVGUUL2wgBCTHbUR+FYTWa4rq9j4n23w= +k8s.io/cli-runtime v0.28.4 h1:IW3aqSNFXiGDllJF4KVYM90YX4cXPGxuCxCVqCD8X+Q= +k8s.io/cli-runtime v0.28.4/go.mod h1:MLGRB7LWTIYyYR3d/DOgtUC8ihsAPA3P8K8FDNIqJ0k= +k8s.io/client-go v0.28.4 h1:Np5ocjlZcTrkyRJ3+T3PkXDpe4UpatQxj85+xjaD2wY= +k8s.io/client-go v0.28.4/go.mod h1:0VDZFpgoZfelyP5Wqu0/r/TRYcLYuJ2U1KEeoaPa1N4= +k8s.io/component-base v0.28.4 h1:c/iQLWPdUgI90O+T9TeECg8o7N3YJTiuz2sKxILYcYo= +k8s.io/component-base v0.28.4/go.mod h1:m9hR0uvqXDybiGL2nf/3Lf0MerAfQXzkfWhUY58JUbU= +k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= +k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ= +k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM= +k8s.io/kubectl v0.28.4 h1:gWpUXW/T7aFne+rchYeHkyB8eVDl5UZce8G4X//kjUQ= +k8s.io/kubectl v0.28.4/go.mod h1:CKOccVx3l+3MmDbkXtIUtibq93nN2hkDR99XDCn7c/c= +k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk= +k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +oras.land/oras-go v1.2.4 h1:djpBY2/2Cs1PV87GSJlxv4voajVOMZxqqtq9AB8YNvY= +oras.land/oras-go v1.2.4/go.mod h1:DYcGfb3YF1nKjcezfX2SNlDAeQFKSXmf+qrFmrh4324= +sigs.k8s.io/controller-runtime v0.16.3 h1:2TuvuokmfXvDUamSx1SuAOO3eTyye+47mJCigwG62c4= +sigs.k8s.io/controller-runtime v0.16.3/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0= +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/kind v0.20.0 h1:f0sc3v9mQbGnjBUaqSFST1dwIuiikKVGgoTwpoP33a8= +sigs.k8s.io/kind v0.20.0/go.mod h1:aBlbxg08cauDgZ612shr017/rZwqd7AS563FvpWKPVs= +sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 h1:XX3Ajgzov2RKUdc5jW3t5jwY7Bo7dcRm+tFxT+NfgY0= +sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3/go.mod h1:9n16EZKMhXBNSiUC5kSdFQJkdH3zbxS/JoO619G1VAY= +sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 h1:W6cLQc5pnqM7vh3b7HvGNfXrJ/xL6BDMS0v1V/HHg5U= +sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3/go.mod h1:JWP1Fj0VWGHyw3YUPjXSQnRnrwezrZSrApfX5S0nIag= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= +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/main.go b/main.go new file mode 100644 index 0000000..4efe83b --- /dev/null +++ b/main.go @@ -0,0 +1,15 @@ +package main + +import ( + "fmt" + "os" + + "github.com/maxgio92/capsule-addon-flux/cmd" +) + +func main() { + if err := cmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) + } +} diff --git a/pkg/indexer/indexer.go b/pkg/indexer/indexer.go new file mode 100644 index 0000000..133dc7c --- /dev/null +++ b/pkg/indexer/indexer.go @@ -0,0 +1,25 @@ +package indexer + +import ( + "context" + "fmt" + + "github.com/go-logr/logr" + "github.com/projectcapsule/capsule/pkg/indexer/tenant" + "github.com/projectcapsule/capsule/pkg/utils" + "sigs.k8s.io/controller-runtime/pkg/manager" +) + +func AddToManager(ctx context.Context, log logr.Logger, mgr manager.Manager) error { + indexer := tenant.OwnerReference{} + + if err := mgr.GetFieldIndexer().IndexField(ctx, indexer.Object(), indexer.Field(), indexer.Func()); err != nil { + if utils.IsUnsupportedAPI(err) { + log.Info(fmt.Sprintf("skipping setup of Indexer %T for object %T", indexer, indexer.Object()), "error", err.Error()) + } + + return err + } + + return nil +}