Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Slowroll:Build:1
helmify
helmify-0.4.16.obscpio
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File helmify-0.4.16.obscpio of Package helmify
07070100000000000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000001700000000helmify-0.4.16/.github07070100000001000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000002100000000helmify-0.4.16/.github/workflows07070100000002000081A4000000000000000000000001673B898300000F8E000000000000000000000000000000000000002800000000helmify-0.4.16/.github/workflows/ci.ymlname: CI on: push: branches: [ main ] pull_request: branches: [ main ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 # GO tests - name: Set up Go uses: actions/setup-go@v4 with: go-version: '1.21' cache: false - name: Fmt run: | # Run gofmt in "diff" mode to check for unformatted code UNFORMATTED_FILES=$(gofmt -l .) # Check if any files are unformatted if [[ -n "$UNFORMATTED_FILES" ]]; then echo "::error::The following Go files are not formatted correctly:" echo "$UNFORMATTED_FILES" # List unformatted files in the log echo "::error::Please format your Go code by running \`go fmt ./...\` and commit the changes." exit 1 # Fail the check else echo "All Go files are properly formatted." fi - name: Vet run: go vet ./... - name: Test run: go test ./... - name: golangci-lint uses: golangci/golangci-lint-action@v3 with: version: v1.54 # Generate example charts - name: Generate example charts run: | cat test_data/sample-app.yaml | go run ./cmd/helmify examples/app cat test_data/k8s-operator-kustomize.output | go run ./cmd/helmify examples/operator - name: Check that chart examples were commited run: | if [[ -n "$(git status --porcelain)" ]]; then # Capture the list of uncommitted files UNCOMMITTED_FILES=$(git status --porcelain) echo "::error::Chart examples generation step has uncommitted changes: $UNCOMMITTED_FILES Please run following commands and commit the results: - \`cat test_data/sample-app.yaml | go run ./cmd/helmify examples/app\` - \`cat test_data/k8s-operator-kustomize.output | go run ./cmd/helmify examples/operator\`" exit 1 else echo "Chart examples generation check passed. No uncommitted changes." fi # Dry-run generated charts in cluster - name: Install k8s cluster uses: helm/kind-action@v1.4.0 - name: Install certs run: kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.1.1/cert-manager.yaml - name: Generate operator ci chart run: cat test_data/k8s-operator-ci.yaml | go run ./cmd/helmify examples/operator-ci - name: Fill operator ci secrets run: sed -i 's/""/"abc"/' ./examples/operator-ci/values.yaml - name: Dry-run operator in k8s cluster run: helm template ./examples/operator-ci -n operator-ns --create-namespace | kubectl apply --dry-run=server -f - - name: Generate app chart run: cat test_data/sample-app.yaml | go run ./cmd/helmify examples/app - name: Fill app secrets run: sed -i 's/""/"abc"/' ./examples/app/values.yaml - name: Dry-run app in k8s cluster run: helm template ./examples/app -n app-ns --create-namespace | kubectl apply --dry-run=server -f - # Validate charts with Kubeconform - name: Install Kubeconform run: go install github.com/yannh/kubeconform/cmd/kubeconform@v0.6.1 - name: Validate app run: helm template ./examples/app -n app-ns --create-namespace | kubeconform -schema-location 'https://raw.githubusercontent.com/kubernetes/kubernetes/master/api/openapi-spec/v3/apis__apiextensions.k8s.io__v1_openapi.json' -strict - name: Generate operator example chart run: cat test_data/k8s-operator-kustomize.output | go run ./cmd/helmify examples/operator - name: Fill operator example secrets run: sed -i 's/""/"abc"/' ./examples/operator/values.yaml - name: Validate example operator run: helm template ./examples/operator -n operator-ns --create-namespace | kubeconform -schema-location 'https://raw.githubusercontent.com/kubernetes/kubernetes/master/api/openapi-spec/v3/apis__apiextensions.k8s.io__v1_openapi.json' -strict 07070100000003000081A4000000000000000000000001673B8983000001BF000000000000000000000000000000000000002D00000000helmify-0.4.16/.github/workflows/release.ymlname: Release Go Binaries on: push: tags: - '*' jobs: goreleaser: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-go@v3 with: go-version: '1.21' - name: Run GoReleaser uses: goreleaser/goreleaser-action@v2 with: version: v1.26.2 args: release --rm-dist env: GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} 07070100000004000081A4000000000000000000000001673B8983000001A6000000000000000000000000000000000000001A00000000helmify-0.4.16/.gitignore # Binaries for programs and plugins *.exe *.exe~ *.dll *.so *.dylib bin testbin/* # 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 *~ # VSCode devcontainer/codespace .devcontainer/ dist/ 07070100000005000081A4000000000000000000000001673B8983000005B5000000000000000000000000000000000000001E00000000helmify-0.4.16/.golangci.yamlrun: tests: false skip-dirs: - internal go: 1.21 fix: true linters-settings: maligned: suggest-new: true staticcheck: go: "1.21" checks: [ "all", "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022","-SA6005","-SA1019" ] gocritic: disabled-checks: - captLocal - commentFormatting revive: rules: - name: var-naming disabled: true nolintlint: require-explanation: true linters: disable-all: true enable: - asasalint # check for pass []any as any in variadic func(...any) - asciicheck # simple linter to check that your code does not contain non-ASCII identifiers - errchkjson # checks types passed to the json encoding functions - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13 - exportloopref # checks for pointers to enclosing loop variables - gocritic # provides diagnostics that check for bugs, performance and style issues - revive # fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint - nolintlint # requiresw to write notes why linter is disabled - errcheck - staticcheck - typecheck - unused - govet - ineffassign - gosimple - durationcheck - errchkjson - errorlint - makezero - exportloopref - errchkjson - prealloc - deadcode 07070100000006000081A4000000000000000000000001673B8983000004C1000000000000000000000000000000000000001F00000000helmify-0.4.16/.goreleaser.ymlproject_name: helmify before: hooks: - go mod tidy builds: - main: ./cmd/helmify env: - CGO_ENABLED=0 goos: - linux - windows - darwin ignore: - goos: darwin goarch: 386 - goos: darwin goarch: arm - goos: windows goarch: arm - goos: windows goarch: arm64 archives: - name_template: >- {{ .ProjectName }}_ {{- title .Os }}_ {{- if eq .Arch "amd64" }}x86_64 {{- else if eq .Arch "386" }}i386 {{- else }}{{ .Arch }}{{ end }} format_overrides: - goos: windows format: zip files: - none* checksum: name_template: 'checksums.txt' brews: - tap: owner: arttor name: homebrew-tap commit_author: name: arttor email: torubarov-a-a@yandex.ru commit_msg_template: "Brew formula update for {{ .ProjectName }} version {{ .Tag }}" folder: Formula homepage: "https://github.com/arttor/helmify" description: "Creates Helm chart from Kubernetes yaml." license: "MIT" test: | system "#{bin}/helmify --version" install: | bin.install "helmify" release: github: owner: arttor name: helmify07070100000007000081A4000000000000000000000001673B898300000427000000000000000000000000000000000000001700000000helmify-0.4.16/LICENSEMIT License Copyright (c) 2021 arttor Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 07070100000008000081A4000000000000000000000001673B898300002C89000000000000000000000000000000000000001900000000helmify-0.4.16/README.md# Helmify [![CI](https://github.com/arttor/helmify/actions/workflows/ci.yml/badge.svg)](https://github.com/arttor/helmify/actions/workflows/ci.yml) ![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/arttor/helmify) ![GitHub](https://img.shields.io/github/license/arttor/helmify) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/arttor/helmify) [![Go Report Card](https://goreportcard.com/badge/github.com/arttor/helmify)](https://goreportcard.com/report/github.com/arttor/helmify) [![GoDoc](https://godoc.org/github.com/arttor/helmify?status.svg)](https://pkg.go.dev/github.com/arttor/helmify?tab=doc) ![GitHub total downloads](https://img.shields.io/github/downloads/arttor/helmify/total) CLI that creates [Helm](https://github.com/helm/helm) charts from kubernetes manifests. Helmify reads a list of [supported k8s objects](#status) from stdin and converts it to a helm chart. Designed to generate charts for [k8s operators](#integrate-to-your-operator-sdkkubebuilder-project) but not limited to. See [examples](https://github.com/arttor/helmify/tree/main/examples) of charts generated by helmify. Supports `Helm >=v3.6.0` Submit issue if some features missing for your use-case. ## Usage 1) As pipe: ```shell cat my-app.yaml | helmify mychart ``` Will create 'mychart' directory with Helm chart from yaml file with k8s objects. ```shell awk 'FNR==1 && NR!=1 {print "---"}{print}' /<my_directory>/*.yaml | helmify mychart ``` Will create 'mychart' directory with Helm chart from all yaml files in `<my_directory> `directory. 2) From filesystem: ```shell helmify -f /my_directory/my-app.yaml mychart ``` Will create 'mychart' directory with Helm chart from `my_directory/my-app.yaml`. ```shell helmify -f /my_directory mychart ``` Will create 'mychart' directory with Helm chart from all yaml files in `<my_directory> `directory. ```shell helmify -f /my_directory -r mychart ``` Will create 'mychart' directory with Helm chart from all yaml files in `<my_directory> `directory recursively. ```shell helmify -f ./first_dir -f ./second_dir/my_deployment.yaml -f ./third_dir mychart ``` Will create 'mychart' directory with Helm chart from multiple directories and files. 3) From [kustomize](https://kustomize.io/) output: ```shell kustomize build <kustomize_dir> | helmify mychart ``` Will create 'mychart' directory with Helm chart from kustomize output. ### Integrate to your Operator-SDK/Kubebuilder project 1. Open `Makefile` in your operator project generated by [Operator-SDK](https://github.com/operator-framework/operator-sdk) or [Kubebuilder](https://github.com/kubernetes-sigs/kubebuilder). 2. Add these lines to `Makefile`: - With operator-sdk version < v1.23.0 ```makefile HELMIFY = $(shell pwd)/bin/helmify helmify: $(call go-get-tool,$(HELMIFY),github.com/arttor/helmify/cmd/helmify@v0.3.7) helm: manifests kustomize helmify $(KUSTOMIZE) build config/default | $(HELMIFY) ``` - With operator-sdk version >= v1.23.0 ```makefile HELMIFY ?= $(LOCALBIN)/helmify .PHONY: helmify helmify: $(HELMIFY) ## Download helmify locally if necessary. $(HELMIFY): $(LOCALBIN) test -s $(LOCALBIN)/helmify || GOBIN=$(LOCALBIN) go install github.com/arttor/helmify/cmd/helmify@latest helm: manifests kustomize helmify $(KUSTOMIZE) build config/default | $(HELMIFY) ``` 3. Run `make helm` in project root. It will generate helm chart with name 'chart' in 'chart' directory. ## Install With [Homebrew](https://brew.sh/) (for MacOS or Linux): `brew install arttor/tap/helmify` Or download suitable for your system binary from [the Releases page](https://github.com/arttor/helmify/releases/latest). Unpack the helmify binary and add it to your PATH and you are good to go! ## Available options Helmify takes a chart name for an argument. Usage: ```helmify [flags] CHART_NAME``` - `CHART_NAME` is optional. Default is 'chart'. Can be a directory, e.g. 'deploy/charts/mychart'. | flag | description | sample | |---------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------| | -h -help | Prints help | `helmify -h` | | -f | File source for k8s manifests (directory or file), multiple sources supported | `helmify -f ./test_data` | | -r | Scan file directory recursively. Used only if -f provided | `helmify -f ./test_data -r` | | -v | Enable verbose output. Prints WARN and INFO. | `helmify -v` | | -vv | Enable very verbose output. Also prints DEBUG. | `helmify -vv` | | -version | Print helmify version. | `helmify -version` | | -crd-dir | Place crds in their own folder per Helm 3 [docs](https://helm.sh/docs/chart_best_practices/custom_resource_definitions/#method-1-let-helm-do-it-for-you). Caveat: CRDs templating is not supported by Helm. | `helmify -crd-dir` | | -image-pull-secrets | Allows the user to use existing secrets as imagePullSecrets | `helmify -image-pull-secrets` | | -original-name | Use the object's original name instead of adding the chart's release name as the common prefix. | `helmify -original-name` | | -cert-manager-as-subchart | Allows the user to install cert-manager as a subchart | `helmify -cert-manager-as-subchart` | | -cert-manager-version | Allows the user to specify cert-manager subchart version. Only useful with cert-manager-as-subchart. (default "v1.12.2") | `helmify -cert-manager-version=v1.12.2` | | -cert-manager-install-crd | Allows the user to install cert-manager CRD as part of the cert-manager subchart.(default "true") | `helmify -cert-manager-install-crd` | | -preserve-ns | Allows users to use the object's original namespace instead of adding all the resources to a common namespace. (default "false") | `helmify -preserve-ns` | | -add-webhook-option | Adds an option to enable/disable webhook installation | `helmify -add-webhook-option`| ## Status Supported k8s resources: - Deployment, DaemonSet, StatefulSet - Job, CronJob - Service, Ingress - PersistentVolumeClaim - RBAC (ServiceAccount, (cluster-)role, (cluster-)roleBinding) - configs (ConfigMap, Secret) - webhooks (cert, issuer, ValidatingWebhookConfiguration) - custom resource definitions (CRD) ### Known issues - Helmify will not overwrite `Chart.yaml` file if presented. Done on purpose. - Helmify will not delete existing template files, only overwrite. - Helmify overwrites templates and values files on every run. This means that all your manual changes in helm template files will be lost on the next run. - if switching between the using the `-crd-dir` flag it is better to delete and regenerate the from scratch to ensure crds are not accidentally spliced/formatted into the same chart. Bear in mind you will want to update your `Chart.yaml` thereafter. ## Develop To support a new type of k8s object template: 1. Implement `helmify.Processor` interface. Place implementation in `pkg/processor`. The package contains examples for most k8s objects. 2. Register your processor in the `pkg/app/app.go` 3. Add relevant input sample to `test_data/kustomize.output`. ### Run Clone repo and execute command: ```shell cat test_data/k8s-operator-kustomize.output | go run ./cmd/helmify mychart ``` Will generate `mychart` Helm chart form file `test_data/k8s-operator-kustomize.output` representing typical operator [kustomize](https://github.com/kubernetes-sigs/kustomize) output. ### Test For manual testing, run program with debug output: ```shell cat test_data/k8s-operator-kustomize.output | go run ./cmd/helmify -vv mychart ``` Then inspect logs and generated chart in `./mychart` directory. To execute tests, run: ```shell go test ./... ``` Beside unit-tests, project contains e2e test `pkg/app/app_e2e_test.go`. It's a go test, which uses `test_data/*` to generate a chart in temporary directory. Then runs `helm lint --strict` to check if generated chart is valid. ## Contribute Following rules will help changes to be accepted faster: - For more than one-line bugfixes consider creating an issue with bug description or feature request - For feature request try to think about and cover following topics (when applicable): - Motivation: why feature is needed? Which problem does it solve? What is current workaround? - Backward-compatibility: existing users expect that after upgrading helmify version their existing generated charts wont be changed without consent. - For bugfix PR consider adding example to [/test_data](./test_data/) source yamls reproducing bug. ### Contribution flow Check list before submitting PR: 1. Run `go fmt ./...` 2. Run tests `go test ./...` 3. Update chart examples: ```shell cat test_data/sample-app.yaml | go run ./cmd/helmify examples/app ``` ```shell cat test_data/k8s-operator-kustomize.output | go run ./cmd/helmify examples/operator ``` 4. In case of long commit history (more than 3) squash local commits into one 07070100000009000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000001300000000helmify-0.4.16/cmd0707010000000A000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000001B00000000helmify-0.4.16/cmd/helmify0707010000000B000081A4000000000000000000000001673B898300001104000000000000000000000000000000000000002400000000helmify-0.4.16/cmd/helmify/flags.gopackage main import ( "flag" "fmt" "os" "path/filepath" "strings" "github.com/arttor/helmify/pkg/config" ) const helpText = `Helmify parses kubernetes resources from std.in and converts it to a Helm chart. Example 1: 'kustomize build <kustomize_dir> | helmify mychart' - will create 'mychart' directory with Helm chart from kustomize output. Example 2: 'cat my-app.yaml | helmify mychart' - will create 'mychart' directory with Helm chart from yaml file. Example 3: 'helmify -f ./test_data/dir mychart' - will scan directory ./test_data/dir for files with k8s manifests and create 'mychart' directory with Helm chart. Example 4: 'helmify -f ./test_data/dir -r mychart' - will scan directory ./test_data/dir recursively and create 'mychart' directory with Helm chart. Example 5: 'helmify -f ./test_data/dir -f ./test_data/sample-app.yaml -f ./test_data/dir/another_dir mychart' - will scan provided multiple files and directories and create 'mychart' directory with Helm chart. Example 6: 'awk 'FNR==1 && NR!=1 {print "---"}{print}' /my_directory/*.yaml | helmify mychart' - will create 'mychart' directory with Helm chart from all yaml files in my_directory directory. Usage: helmify [flags] CHART_NAME - CHART_NAME is optional. Default is 'chart'. Can be a directory, e.g. 'deploy/charts/mychart'. Flags: ` type arrayFlags []string func (i *arrayFlags) String() string { if i == nil || len(*i) == 0 { return "" } return strings.Join(*i, ", ") } func (i *arrayFlags) Set(value string) error { *i = append(*i, value) return nil } // ReadFlags command-line flags into app config. func ReadFlags() config.Config { files := arrayFlags{} result := config.Config{} var h, help, version, crd, preservens bool flag.BoolVar(&h, "h", false, "Print help. Example: helmify -h") flag.BoolVar(&help, "help", false, "Print help. Example: helmify -help") flag.BoolVar(&version, "version", false, "Print helmify version. Example: helmify -version") flag.BoolVar(&result.Verbose, "v", false, "Enable verbose output (print WARN & INFO). Example: helmify -v") flag.BoolVar(&result.VeryVerbose, "vv", false, "Enable very verbose output. Same as verbose but with DEBUG. Example: helmify -vv") flag.BoolVar(&crd, "crd-dir", false, "Enable crd install into 'crds' directory.\nWarning: CRDs placed in 'crds' directory will not be templated by Helm.\nSee https://helm.sh/docs/chart_best_practices/custom_resource_definitions/#some-caveats-and-explanations\nExample: helmify -crd-dir") flag.BoolVar(&result.ImagePullSecrets, "image-pull-secrets", false, "Allows the user to use existing secrets as imagePullSecrets in values.yaml") flag.BoolVar(&result.GenerateDefaults, "generate-defaults", false, "Allows the user to add empty placeholders for typical customization options in values.yaml. Currently covers: topology constraints, node selectors, tolerances") flag.BoolVar(&result.CertManagerAsSubchart, "cert-manager-as-subchart", false, "Allows the user to add cert-manager as a subchart") flag.StringVar(&result.CertManagerVersion, "cert-manager-version", "v1.12.2", "Allows the user to specify cert-manager subchart version. Only useful with cert-manager-as-subchart.") flag.BoolVar(&result.CertManagerInstallCRD, "cert-manager-install-crd", true, "Allows the user to install cert-manager CRD. Only useful with cert-manager-as-subchart.") flag.BoolVar(&result.FilesRecursively, "r", false, "Scan dirs from -f option recursively") flag.BoolVar(&result.OriginalName, "original-name", false, "Use the object's original name instead of adding the chart's release name as the common prefix.") flag.Var(&files, "f", "File or directory containing k8s manifests") flag.BoolVar(&preservens, "preserve-ns", false, "Use the object's original namespace instead of adding all the resources to a common namespace") flag.BoolVar(&result.AddWebhookOption, "add-webhook-option", false, "Allows the user to add webhook option in values.yaml") flag.Parse() if h || help { fmt.Print(helpText) flag.PrintDefaults() os.Exit(0) } if version { printVersion() os.Exit(0) } name := flag.Arg(0) if name != "" { result.ChartName = filepath.Base(name) result.ChartDir = filepath.Dir(name) } if crd { result.Crd = crd } if preservens { result.PreserveNs = true } result.Files = files return result } 0707010000000C000081A4000000000000000000000001673B8983000001F0000000000000000000000000000000000000002300000000helmify-0.4.16/cmd/helmify/main.gopackage main import ( "os" "github.com/arttor/helmify/pkg/app" "github.com/sirupsen/logrus" ) func main() { conf := ReadFlags() stat, err := os.Stdin.Stat() if err != nil { logrus.WithError(err).Error("stdin error") os.Exit(1) } if len(conf.Files) == 0 && (stat.Mode()&os.ModeCharDevice) != 0 { logrus.Error("no data piped in stdin") os.Exit(1) } if err = app.Start(os.Stdin, conf); err != nil { logrus.WithError(err).Error("helmify finished with error") os.Exit(1) } } 0707010000000D000081A4000000000000000000000001673B898300000154000000000000000000000000000000000000002600000000helmify-0.4.16/cmd/helmify/version.gopackage main import ( "fmt" ) // these information will be collected when build, by `-ldflags "-X main.version=0.1"`. var ( version = "development" date = "not set" commit = "not set" ) func printVersion() { fmt.Printf("Version: %s\n", version) fmt.Printf("Build Time: %s\n", date) fmt.Printf("Git Commit: %s\n", commit) } 0707010000000E000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000001800000000helmify-0.4.16/examples0707010000000F000081A4000000000000000000000001673B898300000303000000000000000000000000000000000000002200000000helmify-0.4.16/examples/README.md# Examples `app` - helm chart generated by helmify from [test_data/sample-app.yaml](https://github.com/arttor/helmify/blob/main/test_data/sample-app.yaml). Represents typical k8s app with Deployment, Service, ConfigMap, Secret. Generated with: `cat test_data/sample-app.yaml | go run ./cmd/helmify examples/app` `operator` - helm chart generated by helmify from [test_data/k8s-operator-kustomize.output](https://github.com/arttor/helmify/blob/main/test_data/k8s-operator-kustomize.output). Represents typical k8s operator build with [Operator-SDK](https://github.com/operator-framework/operator-sdk) or [Kubebuilder](https://github.com/kubernetes-sigs/kubebuilder). Generated with: `cat test_data/k8s-operator-kustomize.output | go run ./cmd/helmify examples/operator`07070100000010000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000001C00000000helmify-0.4.16/examples/app07070100000011000081A4000000000000000000000001673B89830000015D000000000000000000000000000000000000002800000000helmify-0.4.16/examples/app/.helmignore# Patterns to ignore when building packages. # This supports shell glob matching, relative path matching, and # negation (prefixed with !). Only one pattern per line. .DS_Store # Common VCS dirs .git/ .gitignore .bzr/ .bzrignore .hg/ .hgignore .svn/ # Common backup files *.swp *.bak *.tmp *.orig *~ # Various IDEs .project .idea/ *.tmproj .vscode/ 07070100000012000081A4000000000000000000000001673B89830000046F000000000000000000000000000000000000002700000000helmify-0.4.16/examples/app/Chart.yamlapiVersion: v2 name: app description: A Helm chart for Kubernetes # A chart can be either an 'application' or a 'library' chart. # # Application charts are a collection of templates that can be packaged into versioned archives # to be deployed. # # Library charts provide useful utilities or functions for the chart developer. They're included as # a dependency of application charts to inject those utilities and functions into the rendering # pipeline. Library charts do not define any templates and therefore cannot be deployed. type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) version: 0.1.0 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. appVersion: "0.1.0" 07070100000013000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000002600000000helmify-0.4.16/examples/app/templates07070100000014000081A4000000000000000000000001673B8983000006CE000000000000000000000000000000000000003300000000helmify-0.4.16/examples/app/templates/_helpers.tpl{{/* Expand the name of the chart. */}} {{- define "app.name" -}} {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} {{- end }} {{/* Create a default fully qualified app name. We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). If release name contains chart name it will be used as a full name. */}} {{- define "app.fullname" -}} {{- if .Values.fullnameOverride }} {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} {{- else }} {{- $name := default .Chart.Name .Values.nameOverride }} {{- if contains $name .Release.Name }} {{- .Release.Name | trunc 63 | trimSuffix "-" }} {{- else }} {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} {{- end }} {{- end }} {{- end }} {{/* Create chart name and version as used by the chart label. */}} {{- define "app.chart" -}} {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} {{- end }} {{/* Common labels */}} {{- define "app.labels" -}} helm.sh/chart: {{ include "app.chart" . }} {{ include "app.selectorLabels" . }} {{- if .Chart.AppVersion }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} app.kubernetes.io/managed-by: {{ .Release.Service }} {{- end }} {{/* Selector labels */}} {{- define "app.selectorLabels" -}} app.kubernetes.io/name: {{ include "app.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} {{/* Create the name of the service account to use */}} {{- define "app.serviceAccountName" -}} {{- if .Values.serviceAccount.create }} {{- default (include "app.fullname" .) .Values.serviceAccount.name }} {{- else }} {{- default "default" .Values.serviceAccount.name }} {{- end }} {{- end }} 07070100000015000081A4000000000000000000000001673B89830000028B000000000000000000000000000000000000003500000000helmify-0.4.16/examples/app/templates/batch-job.yamlapiVersion: batch/v1 kind: Job metadata: name: {{ include "app.fullname" . }}-batch-job labels: {{- include "app.labels" . | nindent 4 }} spec: backoffLimit: {{ .Values.batchJob.backoffLimit }} template: spec: containers: - command: - perl - -Mbignum=bpi - -wle - print bpi(2000) env: - name: KUBERNETES_CLUSTER_DOMAIN value: {{ quote .Values.kubernetesClusterDomain }} image: {{ .Values.batchJob.pi.image.repository }}:{{ .Values.batchJob.pi.image.tag | default .Chart.AppVersion }} name: pi resources: {} restartPolicy: Never07070100000016000081A4000000000000000000000001673B89830000033D000000000000000000000000000000000000003400000000helmify-0.4.16/examples/app/templates/cron-job.yamlapiVersion: batch/v1 kind: CronJob metadata: name: {{ include "app.fullname" . }}-cron-job labels: {{- include "app.labels" . | nindent 4 }} spec: jobTemplate: spec: template: spec: containers: - command: - /bin/sh - -c - date; echo Hello from the Kubernetes cluster env: - name: KUBERNETES_CLUSTER_DOMAIN value: {{ quote .Values.kubernetesClusterDomain }} image: {{ .Values.cronJob.hello.image.repository }}:{{ .Values.cronJob.hello.image.tag | default .Chart.AppVersion }} imagePullPolicy: {{ .Values.cronJob.hello.imagePullPolicy }} name: hello resources: {} restartPolicy: OnFailure schedule: {{ .Values.cronJob.schedule | quote }}07070100000017000081A4000000000000000000000001673B8983000005B5000000000000000000000000000000000000003500000000helmify-0.4.16/examples/app/templates/daemonset.yamlapiVersion: apps/v1 kind: DaemonSet metadata: name: {{ include "app.fullname" . }}-fluentd-elasticsearch labels: k8s-app: fluentd-logging {{- include "app.labels" . | nindent 4 }} spec: selector: matchLabels: name: fluentd-elasticsearch {{- include "app.selectorLabels" . | nindent 6 }} template: metadata: labels: name: fluentd-elasticsearch {{- include "app.selectorLabels" . | nindent 8 }} spec: containers: - env: - name: KUBERNETES_CLUSTER_DOMAIN value: {{ quote .Values.kubernetesClusterDomain }} image: {{ .Values.fluentdElasticsearch.fluentdElasticsearch.image.repository }}:{{ .Values.fluentdElasticsearch.fluentdElasticsearch.image.tag | default .Chart.AppVersion }} name: fluentd-elasticsearch resources: {{- toYaml .Values.fluentdElasticsearch.fluentdElasticsearch.resources | nindent 10 }} volumeMounts: - mountPath: /var/log name: varlog - mountPath: /var/lib/docker/containers name: varlibdockercontainers readOnly: true terminationGracePeriodSeconds: 30 tolerations: - effect: NoSchedule key: node-role.kubernetes.io/master operator: Exists volumes: - hostPath: path: /var/log name: varlog - hostPath: path: /var/lib/docker/containers name: varlibdockercontainers07070100000018000081A4000000000000000000000001673B898300000EEE000000000000000000000000000000000000003600000000helmify-0.4.16/examples/app/templates/deployment.yamlapiVersion: apps/v1 kind: Deployment metadata: name: {{ include "app.fullname" . }}-myapp labels: app: myapp {{- include "app.labels" . | nindent 4 }} spec: replicas: {{ .Values.myapp.replicas }} revisionHistoryLimit: {{ .Values.myapp.revisionHistoryLimit }} selector: matchLabels: app: myapp {{- include "app.selectorLabels" . | nindent 6 }} template: metadata: labels: app: myapp {{- include "app.selectorLabels" . | nindent 8 }} spec: containers: - args: {{- toYaml .Values.myapp.app.args | nindent 8 }} command: - /manager env: - name: VAR1 valueFrom: secretKeyRef: key: VAR1 name: {{ include "app.fullname" . }}-my-secret-vars - name: VAR2 valueFrom: secretKeyRef: key: VAR2 name: {{ include "app.fullname" . }}-my-secret-vars - name: APP_NAME valueFrom: fieldRef: fieldPath: metadata.labels['app.kubernetes.io/name'] - name: INSTANCE_NAME valueFrom: fieldRef: fieldPath: metadata.labels['app.kubernetes.io/instance'] - name: KUBERNETES_CLUSTER_DOMAIN value: {{ quote .Values.kubernetesClusterDomain }} image: {{ .Values.myapp.app.image.repository }}:{{ .Values.myapp.app.image.tag | default .Chart.AppVersion }} livenessProbe: httpGet: path: /healthz port: 8081 initialDelaySeconds: 15 periodSeconds: 20 name: app readinessProbe: httpGet: path: /readyz port: 8081 initialDelaySeconds: 5 periodSeconds: 10 resources: {{- toYaml .Values.myapp.app.resources | nindent 10 }} securityContext: {{- toYaml .Values.myapp.app.containerSecurityContext | nindent 10 }} volumeMounts: - mountPath: /my_config.properties name: manager-config subPath: my_config.properties - mountPath: /my.ca name: secret-volume - mountPath: /etc/props name: props - mountPath: /usr/share/nginx/html name: sample-pv-storage - args: {{- toYaml .Values.myapp.proxySidecar.args | nindent 8 }} env: - name: KUBERNETES_CLUSTER_DOMAIN value: {{ quote .Values.kubernetesClusterDomain }} image: {{ .Values.myapp.proxySidecar.image.repository }}:{{ .Values.myapp.proxySidecar.image.tag | default .Chart.AppVersion }} name: proxy-sidecar ports: - containerPort: 8443 name: https resources: {} initContainers: - command: - /bin/sh - -c - echo 'Initializing container...' env: - name: KUBERNETES_CLUSTER_DOMAIN value: {{ quote .Values.kubernetesClusterDomain }} image: {{ .Values.myapp.initContainer.image.repository }}:{{ .Values.myapp.initContainer.image.tag | default .Chart.AppVersion }} name: init-container resources: {} nodeSelector: {{- toYaml .Values.myapp.nodeSelector | nindent 8 }} securityContext: {{- toYaml .Values.myapp.podSecurityContext | nindent 8 }} terminationGracePeriodSeconds: 10 volumes: - configMap: name: {{ include "app.fullname" . }}-my-config name: manager-config - configMap: name: {{ include "app.fullname" . }}-my-config-props name: props - name: secret-volume secret: secretName: {{ include "app.fullname" . }}-my-secret-ca - name: sample-pv-storage persistentVolumeClaim: claimName: {{ include "app.fullname" . }}-my-sample-pv-claim07070100000019000081A4000000000000000000000001673B89830000018B000000000000000000000000000000000000003B00000000helmify-0.4.16/examples/app/templates/my-config-props.yamlapiVersion: v1 kind: ConfigMap metadata: name: {{ include "app.fullname" . }}-my-config-props labels: {{- include "app.labels" . | nindent 4 }} data: my.prop1: {{ .Values.myConfigProps.myProp1 | quote }} my.prop2: {{ .Values.myConfigProps.myProp2 | quote }} my.prop3: {{ .Values.myConfigProps.myProp3 | quote }} myval.yaml: {{ .Values.myConfigProps.myvalYaml | toYaml | indent 1 }}0707010000001A000081A4000000000000000000000001673B8983000001D5000000000000000000000000000000000000003500000000helmify-0.4.16/examples/app/templates/my-config.yamlapiVersion: v1 kind: ConfigMap metadata: name: {{ include "app.fullname" . }}-my-config labels: {{- include "app.labels" . | nindent 4 }} immutable: true data: dummyconfigmapkey: {{ .Values.myConfig.dummyconfigmapkey | quote }} my_config.properties: | health.healthProbeBindAddress={{ .Values.myConfig.myConfigProperties.health.healthProbeBindAddress | quote }} metrics.bindAddress={{ .Values.myConfig.myConfigProperties.metrics.bindAddress | quote }}0707010000001B000081A4000000000000000000000001673B8983000001CB000000000000000000000000000000000000003E00000000helmify-0.4.16/examples/app/templates/my-sample-pv-claim.yamlapiVersion: v1 kind: PersistentVolumeClaim metadata: name: {{ include "app.fullname" . }}-my-sample-pv-claim labels: {{- include "app.labels" . | nindent 4 }} spec: accessModes: - ReadWriteOnce resources: limits: storage: {{ .Values.pvc.mySamplePvClaim.storageLimit | quote }} requests: storage: {{ .Values.pvc.mySamplePvClaim.storageRequest | quote }} storageClassName: {{ .Values.pvc.mySamplePvClaim.storageClass | quote }}0707010000001C000081A4000000000000000000000001673B898300000108000000000000000000000000000000000000003800000000helmify-0.4.16/examples/app/templates/my-secret-ca.yamlapiVersion: v1 kind: Secret metadata: name: {{ include "app.fullname" . }}-my-secret-ca labels: {{- include "app.labels" . | nindent 4 }} data: ca.crt: {{ required "mySecretCa.caCrt is required" .Values.mySecretCa.caCrt | b64enc | quote }} type: opaque0707010000001D000081A4000000000000000000000001673B8983000002A0000000000000000000000000000000000000003A00000000helmify-0.4.16/examples/app/templates/my-secret-vars.yamlapiVersion: v1 kind: Secret metadata: name: {{ include "app.fullname" . }}-my-secret-vars labels: {{- include "app.labels" . | nindent 4 }} data: ELASTIC_FOOBAR_HUNTER123_MEOWTOWN_VERIFY: {{ required "mySecretVars.elasticFoobarHunter123MeowtownVerify is required" .Values.mySecretVars.elasticFoobarHunter123MeowtownVerify | b64enc | quote }} VAR1: {{ required "mySecretVars.var1 is required" .Values.mySecretVars.var1 | b64enc | quote }} VAR2: {{ required "mySecretVars.var2 is required" .Values.mySecretVars.var2 | b64enc | quote }} stringData: str: {{ required "mySecretVars.str is required" .Values.mySecretVars.str | quote }} type: opaque0707010000001E000081A4000000000000000000000001673B8983000001CD000000000000000000000000000000000000003900000000helmify-0.4.16/examples/app/templates/myapp-ingress.yamlapiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: {{ include "app.fullname" . }}-myapp-ingress labels: {{- include "app.labels" . | nindent 4 }} annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: rules: - http: paths: - backend: service: name: '{{ include "app.fullname" . }}-myapp-service' port: number: 8443 path: /testpath pathType: Prefix0707010000001F000081A4000000000000000000000001673B8983000001D2000000000000000000000000000000000000003C00000000helmify-0.4.16/examples/app/templates/myapp-lb-service.yamlapiVersion: v1 kind: Service metadata: name: {{ include "app.fullname" . }}-myapp-lb-service labels: app: myapp {{- include "app.labels" . | nindent 4 }} spec: type: {{ .Values.myappLbService.type }} selector: app: myapp {{- include "app.selectorLabels" . | nindent 4 }} ports: {{- .Values.myappLbService.ports | toYaml | nindent 2 }} loadBalancerSourceRanges: {{- .Values.myappLbService.loadBalancerSourceRanges | toYaml | nindent 2 }}07070100000020000081A4000000000000000000000001673B898300000185000000000000000000000000000000000000003500000000helmify-0.4.16/examples/app/templates/myapp-pdb.yamlapiVersion: policy/v1 kind: PodDisruptionBudget metadata: name: {{ include "app.fullname" . }}-myapp-pdb labels: app: nginx {{- include "app.labels" . | nindent 4 }} spec: minAvailable: {{ .Values.myappPdb.minAvailable }} maxUnavailable: {{ .Values.myappPdb.maxUnavailable }} selector: matchLabels: app: nginx {{- include "app.selectorLabels" . | nindent 6 }}07070100000021000081A4000000000000000000000001673B898300000161000000000000000000000000000000000000003900000000helmify-0.4.16/examples/app/templates/myapp-service.yamlapiVersion: v1 kind: Service metadata: name: {{ include "app.fullname" . }}-myapp-service labels: app: myapp {{- include "app.labels" . | nindent 4 }} spec: type: {{ .Values.myappService.type }} selector: app: myapp {{- include "app.selectorLabels" . | nindent 4 }} ports: {{- .Values.myappService.ports | toYaml | nindent 2 }}07070100000022000081A4000000000000000000000001673B89830000014B000000000000000000000000000000000000003100000000helmify-0.4.16/examples/app/templates/nginx.yamlapiVersion: v1 kind: Service metadata: name: {{ include "app.fullname" . }}-nginx labels: app: nginx {{- include "app.labels" . | nindent 4 }} spec: type: {{ .Values.nginx.type }} selector: app: nginx {{- include "app.selectorLabels" . | nindent 4 }} ports: {{- .Values.nginx.ports | toYaml | nindent 2 }}07070100000023000081A4000000000000000000000001673B89830000040D000000000000000000000000000000000000003700000000helmify-0.4.16/examples/app/templates/statefulset.yamlapiVersion: apps/v1 kind: StatefulSet metadata: name: {{ include "app.fullname" . }}-web labels: {{- include "app.labels" . | nindent 4 }} spec: replicas: {{ .Values.web.replicas }} selector: matchLabels: app: nginx serviceName: {{ include "app.fullname" . }}-nginx template: metadata: labels: app: nginx spec: containers: - env: - name: KUBERNETES_CLUSTER_DOMAIN value: {{ quote .Values.kubernetesClusterDomain }} image: {{ .Values.web.nginx.image.repository }}:{{ .Values.web.nginx.image.tag | default .Chart.AppVersion }} name: nginx ports: - containerPort: 80 name: web resources: {} volumeMounts: - mountPath: /usr/share/nginx/html name: www updateStrategy: {} volumeClaimTemplates: - metadata: creationTimestamp: null name: www spec: accessModes: - ReadWriteOnce resources: {{ .Values.web.volumeClaims.www | toYaml | nindent 8 }}07070100000024000081A4000000000000000000000001673B8983000009DF000000000000000000000000000000000000002800000000helmify-0.4.16/examples/app/values.yamlbatchJob: backoffLimit: 4 pi: image: repository: perl tag: 5.34.0 cronJob: hello: image: repository: busybox tag: "1.28" imagePullPolicy: IfNotPresent schedule: '* * * * *' fluentdElasticsearch: fluentdElasticsearch: image: repository: quay.io/fluentd_elasticsearch/fluentd tag: v2.5.2 resources: limits: memory: 200Mi requests: cpu: 100m memory: 200Mi kubernetesClusterDomain: cluster.local myConfig: dummyconfigmapkey: dummyconfigmapvalue myConfigProperties: health: healthProbeBindAddress: "8081" metrics: bindAddress: 127.0.0.1:8080 myConfigProps: myProp1: "1" myProp2: val 1 myProp3: "true" myvalYaml: |- apiVersion: clickhouse.altinity.com/v1 kind: ClickHouseInstallationTemplate metadata: name: default-oneperhost-pod-template spec: templates: podTemplates: - name: default-oneperhost-pod-template distribution: "OnePerHost" mySecretCa: caCrt: "" mySecretVars: elasticFoobarHunter123MeowtownVerify: "" str: "" var1: "" var2: "" myapp: app: args: - --health-probe-bind-address=:8081 - --metrics-bind-address=127.0.0.1:8080 - --leader-elect containerSecurityContext: allowPrivilegeEscalation: false image: repository: controller tag: latest resources: limits: cpu: 100m memory: 30Mi requests: cpu: 100m memory: 20Mi initContainer: image: repository: bash tag: latest nodeSelector: region: east type: user-node podSecurityContext: fsGroup: 20000 runAsNonRoot: true runAsUser: 65532 proxySidecar: args: - --secure-listen-address=0.0.0.0:8443 - --v=10 image: repository: gcr.io/kubebuilder/kube-rbac-proxy tag: v0.8.0 replicas: 3 revisionHistoryLimit: 5 myappLbService: loadBalancerSourceRanges: - 10.0.0.0/8 ports: - name: https port: 8443 targetPort: https type: LoadBalancer myappPdb: minAvailable: 2 myappService: ports: - name: https port: 8443 targetPort: https type: ClusterIP nginx: ports: - name: web port: 80 targetPort: 0 type: ClusterIP pvc: mySamplePvClaim: storageClass: manual storageLimit: 5Gi storageRequest: 3Gi web: nginx: image: repository: registry.k8s.io/nginx-slim tag: "0.8" replicas: 2 volumeClaims: www: requests: storage: 1Gi 07070100000025000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000002100000000helmify-0.4.16/examples/operator07070100000026000081A4000000000000000000000001673B89830000015D000000000000000000000000000000000000002D00000000helmify-0.4.16/examples/operator/.helmignore# Patterns to ignore when building packages. # This supports shell glob matching, relative path matching, and # negation (prefixed with !). Only one pattern per line. .DS_Store # Common VCS dirs .git/ .gitignore .bzr/ .bzrignore .hg/ .hgignore .svn/ # Common backup files *.swp *.bak *.tmp *.orig *~ # Various IDEs .project .idea/ *.tmproj .vscode/ 07070100000027000081A4000000000000000000000001673B898300000474000000000000000000000000000000000000002C00000000helmify-0.4.16/examples/operator/Chart.yamlapiVersion: v2 name: operator description: A Helm chart for Kubernetes # A chart can be either an 'application' or a 'library' chart. # # Application charts are a collection of templates that can be packaged into versioned archives # to be deployed. # # Library charts provide useful utilities or functions for the chart developer. They're included as # a dependency of application charts to inject those utilities and functions into the rendering # pipeline. Library charts do not define any templates and therefore cannot be deployed. type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) version: 0.1.0 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. appVersion: "0.1.0" 07070100000028000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000002B00000000helmify-0.4.16/examples/operator/templates07070100000029000081A4000000000000000000000001673B898300000700000000000000000000000000000000000000003800000000helmify-0.4.16/examples/operator/templates/_helpers.tpl{{/* Expand the name of the chart. */}} {{- define "operator.name" -}} {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} {{- end }} {{/* Create a default fully qualified app name. We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). If release name contains chart name it will be used as a full name. */}} {{- define "operator.fullname" -}} {{- if .Values.fullnameOverride }} {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} {{- else }} {{- $name := default .Chart.Name .Values.nameOverride }} {{- if contains $name .Release.Name }} {{- .Release.Name | trunc 63 | trimSuffix "-" }} {{- else }} {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} {{- end }} {{- end }} {{- end }} {{/* Create chart name and version as used by the chart label. */}} {{- define "operator.chart" -}} {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} {{- end }} {{/* Common labels */}} {{- define "operator.labels" -}} helm.sh/chart: {{ include "operator.chart" . }} {{ include "operator.selectorLabels" . }} {{- if .Chart.AppVersion }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} app.kubernetes.io/managed-by: {{ .Release.Service }} {{- end }} {{/* Selector labels */}} {{- define "operator.selectorLabels" -}} app.kubernetes.io/name: {{ include "operator.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} {{/* Create the name of the service account to use */}} {{- define "operator.serviceAccountName" -}} {{- if .Values.serviceAccount.create }} {{- default (include "operator.fullname" .) .Values.serviceAccount.name }} {{- else }} {{- default "default" .Values.serviceAccount.name }} {{- end }} {{- end }} 0707010000002A000081A4000000000000000000000001673B89830000278B000000000000000000000000000000000000003F00000000helmify-0.4.16/examples/operator/templates/cephvolume-crd.yamlapiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: cephvolumes.test.example.com annotations: cert-manager.io/inject-ca-from: '{{ .Release.Namespace }}/{{ include "operator.fullname" . }}-serving-cert' example-annotation: xyz labels: example-label: my-app {{- include "operator.labels" . | nindent 4 }} spec: group: test.example.com names: kind: CephVolume listKind: CephVolumeList plural: cephvolumes singular: cephvolume scope: Namespaced versions: - additionalPrinterColumns: - description: Ceph RBD pool name jsonPath: .spec.pool name: Pool type: string - description: Storage type jsonPath: .status.type name: Type type: string - description: Volume size jsonPath: .spec.size name: Size type: string - description: Max number of volume I/O operations per second jsonPath: .status.limits.iops name: IOPS type: string - description: true if volume contains latest type,size spec from Ceph jsonPath: .status.conditions[?(@.type=="Provided")].status name: Provided type: string - description: true if volume IOPS limits calculated. False indicates error - check reason for details jsonPath: .status.conditions[?(@.type=="Calculated")].status name: Calculated type: string - description: true if volume IOPS limits applied to volume. False indicates error - check reason for details jsonPath: .status.conditions[?(@.type=="Limited")].status name: Limited type: string - description: latest resource generation jsonPath: .metadata.generation name: gen type: string - description: latest observed generation of Limited condition jsonPath: .status.conditions[?(@.type=="Limited")].observedGeneration name: Lim-gen type: string name: v1alpha1 schema: openAPIV3Schema: description: CephVolume represents Ceph RBD volume properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: description: CephVolumeSpec defines the desired state of CephVolume properties: pool: description: Pool - volume pool name type: string size: anyOf: - type: integer - type: string description: Size - volume size pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true type: object status: description: CephVolumeStatus defines the observed state of CephVolume properties: conditions: description: 'Conditions represent the latest available observations of an object''s state Known .status.conditions.type are: "Provided". "Calculated", "Limited"' items: description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type \ Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" properties: lastTransitionTime: description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object type: array limits: description: Limits represent calculated IOPS limits properties: iops: description: IOPS - desired limit of IO operations per second. See Ceph rbd_qos_iops_limit property. format: int64 minimum: 0 type: integer iopsBurst: description: IOPSBurst - desired burst limit of IO operations. See Ceph rbd_qos_iops_burst property. format: int64 minimum: 0 type: integer readIOPS: description: ReadIOPS - desired limit of read operations per second. See Ceph rbd_qos_read_iops_limit property. format: int64 minimum: 0 type: integer readIOPSBurst: description: ReadIOPSBurst - desired burst limit of read operations. See Ceph rbd_qos_read_iops_burst property. format: int64 minimum: 0 type: integer writeIOPS: description: WriteIOPS - desired limit of write operations per second. See Ceph rbd_qos_write_iops_limit property format: int64 minimum: 0 type: integer writeIOPSBurst: description: WriteIOPSBurst - desired burst limit of write operations. See Ceph rbd_qos_write_iops_burst property. format: int64 minimum: 0 type: integer type: object type: description: Type - volume storage type. See StorageType CRD. type: string required: - conditions type: object type: object served: true storage: true subresources: status: {} status: acceptedNames: kind: "" plural: "" conditions: [] storedVersions: []0707010000002B000081A4000000000000000000000001673B8983000000D5000000000000000000000000000000000000003F00000000helmify-0.4.16/examples/operator/templates/configmap-vars.yamlapiVersion: v1 kind: ConfigMap metadata: name: {{ include "operator.fullname" . }}-configmap-vars labels: {{- include "operator.labels" . | nindent 4 }} data: VAR4: {{ .Values.configmapVars.var4 | quote }}0707010000002C000081A4000000000000000000000001673B898300000F49000000000000000000000000000000000000003B00000000helmify-0.4.16/examples/operator/templates/deployment.yamlapiVersion: apps/v1 kind: Deployment metadata: name: {{ include "operator.fullname" . }}-controller-manager labels: control-plane: controller-manager {{- include "operator.labels" . | nindent 4 }} spec: replicas: {{ .Values.controllerManager.replicas }} selector: matchLabels: control-plane: controller-manager {{- include "operator.selectorLabels" . | nindent 6 }} template: metadata: labels: control-plane: controller-manager {{- include "operator.selectorLabels" . | nindent 8 }} spec: containers: - args: {{- toYaml .Values.controllerManager.kubeRbacProxy.args | nindent 8 }} env: - name: KUBERNETES_CLUSTER_DOMAIN value: {{ quote .Values.kubernetesClusterDomain }} image: {{ .Values.controllerManager.kubeRbacProxy.image.repository }}:{{ .Values.controllerManager.kubeRbacProxy.image.tag | default .Chart.AppVersion }} name: kube-rbac-proxy ports: - containerPort: 8443 name: https resources: {} - args: {{- toYaml .Values.controllerManager.manager.args | nindent 8 }} command: - /manager env: - name: VAR1 valueFrom: secretKeyRef: key: VAR1 name: {{ include "operator.fullname" . }}-secret-vars - name: VAR2 value: {{ quote .Values.controllerManager.manager.env.var2 }} - name: VAR3_MY_ENV value: {{ quote .Values.controllerManager.manager.env.var3MyEnv }} - name: VAR4 valueFrom: configMapKeyRef: key: VAR4 name: {{ include "operator.fullname" . }}-configmap-vars - name: VAR5 valueFrom: fieldRef: fieldPath: metadata.namespace - name: VAR6 valueFrom: resourceFieldRef: divisor: "0" resource: limits.cpu - name: KUBERNETES_CLUSTER_DOMAIN value: {{ quote .Values.kubernetesClusterDomain }} image: {{ .Values.controllerManager.manager.image.repository }}:{{ .Values.controllerManager.manager.image.tag | default .Chart.AppVersion }} imagePullPolicy: {{ .Values.controllerManager.manager.imagePullPolicy }} livenessProbe: httpGet: path: /healthz port: 8081 initialDelaySeconds: 15 periodSeconds: 20 name: manager readinessProbe: httpGet: path: /readyz port: 8081 initialDelaySeconds: 5 periodSeconds: 10 resources: {{- toYaml .Values.controllerManager.manager.resources | nindent 10 }} securityContext: {{- toYaml .Values.controllerManager.manager.containerSecurityContext | nindent 10 }} volumeMounts: - mountPath: /controller_manager_config.yaml name: manager-config subPath: controller_manager_config.yaml - mountPath: /my.ca name: secret-volume imagePullSecrets: - name: {{ include "operator.fullname" . }}-secret-registry-credentials nodeSelector: {{- toYaml .Values.controllerManager.nodeSelector | nindent 8 }} securityContext: {{- toYaml .Values.controllerManager.podSecurityContext | nindent 8 }} serviceAccountName: {{ include "operator.fullname" . }}-controller-manager terminationGracePeriodSeconds: 10 topologySpreadConstraints: - matchLabelKeys: - app - pod-template-hash maxSkew: 1 topologyKey: kubernetes.io/hostname whenUnsatisfiable: DoNotSchedule volumes: - configMap: name: {{ include "operator.fullname" . }}-manager-config name: manager-config - name: secret-volume secret: secretName: {{ include "operator.fullname" . }}-secret-ca0707010000002D000081A4000000000000000000000001673B8983000003EA000000000000000000000000000000000000004500000000helmify-0.4.16/examples/operator/templates/leader-election-rbac.yamlapiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: {{ include "operator.fullname" . }}-leader-election-role labels: {{- include "operator.labels" . | nindent 4 }} rules: - apiGroups: - "" resources: - configmaps verbs: - get - list - watch - create - update - patch - delete - apiGroups: - coordination.k8s.io resources: - leases verbs: - get - list - watch - create - update - patch - delete - apiGroups: - "" resources: - events verbs: - create - patch --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: {{ include "operator.fullname" . }}-leader-election-rolebinding labels: {{- include "operator.labels" . | nindent 4 }} roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: '{{ include "operator.fullname" . }}-leader-election-role' subjects: - kind: ServiceAccount name: '{{ include "operator.fullname" . }}-controller-manager' namespace: '{{ .Release.Namespace }}'0707010000002E000081A4000000000000000000000001673B898300000167000000000000000000000000000000000000004800000000helmify-0.4.16/examples/operator/templates/manager-aggregated-rbac.yamlapiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: {{ include "operator.fullname" . }}-manager-aggregated-role labels: {{- include "operator.labels" . | nindent 4 }} aggregationRule: clusterRoleSelectors: - matchExpressions: - key: app.kubernetes.io/instance operator: In values: - my-operator rules: []0707010000002F000081A4000000000000000000000001673B898300000161000000000000000000000000000000000000003F00000000helmify-0.4.16/examples/operator/templates/manager-config.yamlapiVersion: v1 kind: ConfigMap metadata: name: {{ include "operator.fullname" . }}-manager-config labels: {{- include "operator.labels" . | nindent 4 }} data: controller_manager_config.yaml: {{ .Values.managerConfig.controllerManagerConfigYaml | toYaml | indent 1 }} dummyconfigmapkey: {{ .Values.managerConfig.dummyconfigmapkey | quote }}07070100000030000081A4000000000000000000000001673B8983000006A5000000000000000000000000000000000000003D00000000helmify-0.4.16/examples/operator/templates/manager-rbac.yamlapiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: {{ include "operator.fullname" . }}-manager-role labels: {{- include "operator.labels" . | nindent 4 }} rules: - apiGroups: - "" resources: - pods verbs: - get - list - apiGroups: - "" resources: - pods/exec verbs: - create - get - apiGroups: - test.example.com resources: - cephvolumes verbs: - create - delete - get - list - patch - update - watch - apiGroups: - test.example.com resources: - cephvolumes/finalizers verbs: - update - apiGroups: - test.example.com resources: - cephvolumes/status verbs: - get - patch - update - apiGroups: - test.example.com resources: - manifestcephvolumes verbs: - create - delete - get - list - patch - update - watch - apiGroups: - test.example.com resources: - manifestcephvolumes/finalizers verbs: - update - apiGroups: - test.example.com resources: - manifestcephvolumes/status verbs: - get - patch - update - apiGroups: - test.example.com resources: - storagetypes verbs: - get - list - watch - apiGroups: - test.example.com resources: - storagetypes/status verbs: - get --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: {{ include "operator.fullname" . }}-manager-rolebinding labels: {{- include "operator.labels" . | nindent 4 }} roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: '{{ include "operator.fullname" . }}-manager-role' subjects: - kind: ServiceAccount name: '{{ include "operator.fullname" . }}-controller-manager' namespace: '{{ .Release.Namespace }}'07070100000031000081A4000000000000000000000001673B898300000C8A000000000000000000000000000000000000004700000000helmify-0.4.16/examples/operator/templates/manifestcephvolume-crd.yamlapiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: manifestcephvolumes.test.example.com annotations: cert-manager.io/inject-ca-from: '{{ .Release.Namespace }}/{{ include "operator.fullname" . }}-serving-cert' labels: {{- include "operator.labels" . | nindent 4 }} spec: conversion: strategy: Webhook webhook: clientConfig: service: name: '{{ include "operator.fullname" . }}-webhook-service' namespace: '{{ .Release.Namespace }}' path: /convert conversionReviewVersions: - v1 group: test.example.com names: kind: ManifestCephVolume listKind: ManifestCephVolumeList plural: manifestcephvolumes singular: manifestcephvolume scope: Namespaced versions: - additionalPrinterColumns: - description: Ceph RBD pool name jsonPath: .spec.poolName name: PoolName type: string - description: Sync interval in seconds jsonPath: .spec.interval name: Interval type: string - description: Last update time jsonPath: .status.lastUpdate name: LastUpdate type: string name: v1alpha1 schema: openAPIV3Schema: description: ManifestCephVolume monitors given ceph pool and manifests containing volumes as CephVolume CR properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: description: ManifestCephVolumeSpec defines the desired state of ManifestCephVolume properties: interval: description: Interval - Ceph pool polling interval format: int32 minimum: 60 type: integer poolName: description: PoolName name of Ceph RBD pool to get volumes type: string required: - interval type: object status: description: ManifestCephVolumeStatus defines the observed state of ManifestCephVolume properties: lastUpdate: description: LastUpdate - time of last successful volumes update format: date-time type: string type: object type: object served: true storage: true subresources: status: {} status: acceptedNames: kind: "" plural: "" conditions: [] storedVersions: []07070100000032000081A4000000000000000000000001673B8983000000F2000000000000000000000000000000000000004400000000helmify-0.4.16/examples/operator/templates/metrics-reader-rbac.yamlapiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: {{ include "operator.fullname" . }}-metrics-reader labels: {{- include "operator.labels" . | nindent 4 }} rules: - nonResourceURLs: - /metrics verbs: - get07070100000033000081A4000000000000000000000001673B8983000001B7000000000000000000000000000000000000004000000000helmify-0.4.16/examples/operator/templates/metrics-service.yamlapiVersion: v1 kind: Service metadata: name: {{ include "operator.fullname" . }}-controller-manager-metrics-service labels: control-plane: controller-manager {{- include "operator.labels" . | nindent 4 }} spec: type: {{ .Values.metricsService.type }} selector: control-plane: controller-manager {{- include "operator.selectorLabels" . | nindent 4 }} ports: {{- .Values.metricsService.ports | toYaml | nindent 2 }}07070100000034000081A4000000000000000000000001673B89830000031A000000000000000000000000000000000000004F00000000helmify-0.4.16/examples/operator/templates/mutating-webhook-configuration.yamlapiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration metadata: name: {{ include "operator.fullname" . }}-mutating-webhook-configuration annotations: cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/{{ include "operator.fullname" . }}-serving-cert labels: {{- include "operator.labels" . | nindent 4 }} webhooks: - admissionReviewVersions: - v1 clientConfig: service: name: '{{ include "operator.fullname" . }}-webhook-service' namespace: '{{ .Release.Namespace }}' path: /mutate-ceph-example-com-v1-mycluster failurePolicy: Fail name: mmycluster.kb.io rules: - apiGroups: - test.example.com apiVersions: - v1 operations: - CREATE - UPDATE resources: - myclusters sideEffects: None07070100000035000081A4000000000000000000000001673B898300000345000000000000000000000000000000000000003B00000000helmify-0.4.16/examples/operator/templates/proxy-rbac.yamlapiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: {{ include "operator.fullname" . }}-proxy-role labels: {{- include "operator.labels" . | nindent 4 }} rules: - apiGroups: - authentication.k8s.io resources: - tokenreviews verbs: - create - apiGroups: - authorization.k8s.io resources: - subjectaccessreviews verbs: - create --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: {{ include "operator.fullname" . }}-proxy-rolebinding labels: {{- include "operator.labels" . | nindent 4 }} roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: '{{ include "operator.fullname" . }}-proxy-role' subjects: - kind: ServiceAccount name: '{{ include "operator.fullname" . }}-controller-manager' namespace: '{{ .Release.Namespace }}'07070100000036000081A4000000000000000000000001673B898300000166000000000000000000000000000000000000003800000000helmify-0.4.16/examples/operator/templates/pvc-lim.yamlapiVersion: v1 kind: PersistentVolumeClaim metadata: name: {{ include "operator.fullname" . }}-pvc-lim labels: {{- include "operator.labels" . | nindent 4 }} spec: accessModes: - ReadWriteOnce resources: requests: storage: {{ .Values.pvc.pvcLim.storageRequest | quote }} storageClassName: {{ .Values.pvc.pvcLim.storageClass | quote }}07070100000037000081A4000000000000000000000001673B89830000010B000000000000000000000000000000000000003A00000000helmify-0.4.16/examples/operator/templates/secret-ca.yamlapiVersion: v1 kind: Secret metadata: name: {{ include "operator.fullname" . }}-secret-ca labels: {{- include "operator.labels" . | nindent 4 }} data: ca.crt: {{ required "secretCa.caCrt is required" .Values.secretCa.caCrt | b64enc | quote }} type: opaque07070100000038000081A4000000000000000000000001673B898300000178000000000000000000000000000000000000004C00000000helmify-0.4.16/examples/operator/templates/secret-registry-credentials.yamlapiVersion: v1 kind: Secret metadata: name: {{ include "operator.fullname" . }}-secret-registry-credentials labels: {{- include "operator.labels" . | nindent 4 }} data: .dockerconfigjson: {{ required "secretRegistryCredentials.dockerconfigjson is required" .Values.secretRegistryCredentials.dockerconfigjson | b64enc | quote }} type: kubernetes.io/dockerconfigjson07070100000039000081A4000000000000000000000001673B89830000016F000000000000000000000000000000000000003C00000000helmify-0.4.16/examples/operator/templates/secret-vars.yamlapiVersion: v1 kind: Secret metadata: name: {{ include "operator.fullname" . }}-secret-vars labels: {{- include "operator.labels" . | nindent 4 }} data: VAR1: {{ required "secretVars.var1 is required" .Values.secretVars.var1 | b64enc | quote }} VAR2: {{ required "secretVars.var2 is required" .Values.secretVars.var2 | b64enc | quote }} type: opaque0707010000003A000081A4000000000000000000000001673B8983000000C5000000000000000000000000000000000000004200000000helmify-0.4.16/examples/operator/templates/selfsigned-issuer.yamlapiVersion: cert-manager.io/v1 kind: Issuer metadata: name: {{ include "operator.fullname" . }}-selfsigned-issuer labels: {{- include "operator.labels" . | nindent 4 }} spec: selfSigned: {}0707010000003B000081A4000000000000000000000001673B898300000109000000000000000000000000000000000000003F00000000helmify-0.4.16/examples/operator/templates/serviceaccount.yamlapiVersion: v1 kind: ServiceAccount metadata: name: {{ include "operator.fullname" . }}-controller-manager labels: {{- include "operator.labels" . | nindent 4 }} annotations: {{- toYaml .Values.controllerManager.serviceAccount.annotations | nindent 4 }}0707010000003C000081A4000000000000000000000001673B89830000021A000000000000000000000000000000000000003D00000000helmify-0.4.16/examples/operator/templates/serving-cert.yamlapiVersion: cert-manager.io/v1 kind: Certificate metadata: name: {{ include "operator.fullname" . }}-serving-cert labels: {{- include "operator.labels" . | nindent 4 }} spec: dnsNames: - '{{ include "operator.fullname" . }}-webhook-service.{{ .Release.Namespace }}.svc' - '{{ include "operator.fullname" . }}-webhook-service.{{ .Release.Namespace }}.svc.{{ .Values.kubernetesClusterDomain }}' issuerRef: kind: Issuer name: '{{ include "operator.fullname" . }}-selfsigned-issuer' secretName: webhook-server-cert0707010000003D000081A4000000000000000000000001673B89830000032F000000000000000000000000000000000000005100000000helmify-0.4.16/examples/operator/templates/validating-webhook-configuration.yamlapiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: name: {{ include "operator.fullname" . }}-validating-webhook-configuration annotations: cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/{{ include "operator.fullname" . }}-serving-cert labels: {{- include "operator.labels" . | nindent 4 }} webhooks: - admissionReviewVersions: - v1 - v1beta1 clientConfig: service: name: '{{ include "operator.fullname" . }}-webhook-service' namespace: '{{ .Release.Namespace }}' path: /validate-ceph-example-com-v1alpha1-volume failurePolicy: Fail name: vvolume.kb.io rules: - apiGroups: - test.example.com apiVersions: - v1alpha1 operations: - CREATE - UPDATE resources: - volumes sideEffects: None0707010000003E000081A4000000000000000000000001673B89830000017E000000000000000000000000000000000000004000000000helmify-0.4.16/examples/operator/templates/webhook-service.yamlapiVersion: v1 kind: Service metadata: name: {{ include "operator.fullname" . }}-webhook-service labels: {{- include "operator.labels" . | nindent 4 }} spec: type: {{ .Values.webhookService.type }} selector: control-plane: controller-manager {{- include "operator.selectorLabels" . | nindent 4 }} ports: {{- .Values.webhookService.ports | toYaml | nindent 2 }}0707010000003F000081A4000000000000000000000001673B8983000007CB000000000000000000000000000000000000002D00000000helmify-0.4.16/examples/operator/values.yamlconfigmapVars: var4: value for var4 controllerManager: kubeRbacProxy: args: - --secure-listen-address=0.0.0.0:8443 - --upstream=http://127.0.0.1:8080/ - --logtostderr=true - --v=10 image: repository: gcr.io/kubebuilder/kube-rbac-proxy tag: v0.8.0 manager: args: - --health-probe-bind-address=:8081 - --metrics-bind-address=127.0.0.1:8080 - --leader-elect containerSecurityContext: allowPrivilegeEscalation: false capabilities: drop: - ALL privileged: false readOnlyRootFilesystem: true runAsNonRoot: true runAsUser: 65532 seccompProfile: type: RuntimeDefault env: var2: ciao var3MyEnv: ciao image: repository: controller tag: latest imagePullPolicy: Always resources: limits: cpu: 100m memory: 30Mi requests: cpu: 100m memory: 20Mi nodeSelector: region: east type: user-node podSecurityContext: runAsNonRoot: true replicas: 1 serviceAccount: annotations: k8s.acme.org/some-meta-data: ACME Inc. kubernetesClusterDomain: cluster.local managerConfig: controllerManagerConfigYaml: |- apiVersion: controller-runtime.sigs.k8s.io/v1alpha1 kind: ControllerManagerConfig health: healthProbeBindAddress: :8081 metrics: bindAddress: 127.0.0.1:8080 webhook: port: 9443 leaderElection: leaderElect: true resourceName: 3a2e09e9.example.com rook: namespace: rook-ceph toolboxPodLabel: rook-ceph-tools dummyconfigmapkey: dummyconfigmapvalue metricsService: ports: - name: https port: 8443 targetPort: https type: ClusterIP pvc: pvcLim: storageClass: cust1-mypool-lim storageRequest: 2Gi secretCa: caCrt: "" secretRegistryCredentials: dockerconfigjson: "" secretVars: var1: "" var2: "" webhookService: ports: - port: 443 targetPort: 9443 type: ClusterIP 07070100000040000081A4000000000000000000000001673B898300001982000000000000000000000000000000000000001600000000helmify-0.4.16/go.modmodule github.com/arttor/helmify go 1.21 require ( dario.cat/mergo v1.0.0 github.com/iancoleman/strcase v0.2.0 github.com/sirupsen/logrus v1.9.0 github.com/stretchr/testify v1.8.1 helm.sh/helm/v3 v3.11.2 k8s.io/api v0.26.2 k8s.io/apiextensions-apiserver v0.26.2 k8s.io/apimachinery v0.26.2 sigs.k8s.io/yaml v1.3.0 ) require ( github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/BurntSushi/toml v1.2.1 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.2.0 // indirect github.com/Masterminds/sprig/v3 v3.2.3 // indirect github.com/Masterminds/squirrel v1.5.3 // 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.6.15 // indirect github.com/cyphar/filepath-securejoin v0.2.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/docker/cli v20.10.21+incompatible // indirect github.com/docker/distribution v2.8.1+incompatible // indirect github.com/docker/docker v20.10.21+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.4.0 // indirect github.com/emicklei/go-restful/v3 v3.9.0 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect github.com/fatih/color v1.13.0 // indirect github.com/go-errors/errors v1.0.1 // indirect github.com/go-gorp/gorp/v3 v3.0.5 // indirect github.com/go-logr/logr v1.2.3 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.1 // indirect github.com/go-openapi/swag v0.22.3 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/btree v1.0.1 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.3.0 // 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/huandu/xstrings v1.4.0 // indirect github.com/imdario/mergo v0.3.16 // 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.11.13 // 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.7 // 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.0 // 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.0.0-20221205130635-1aeaba878587 // 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-rc2 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.16.0 // indirect github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/procfs v0.10.1 // indirect github.com/rubenv/sql-migrate v1.3.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/spf13/cobra v1.7.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xlab/treeprint v1.1.0 // indirect go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect golang.org/x/crypto v0.13.0 // indirect golang.org/x/net v0.15.0 // indirect golang.org/x/oauth2 v0.8.0 // indirect golang.org/x/sync v0.2.0 // indirect golang.org/x/sys v0.12.0 // indirect golang.org/x/term v0.12.0 // indirect golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.3.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.54.0 // indirect google.golang.org/protobuf v1.30.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/apiserver v0.26.2 // indirect k8s.io/cli-runtime v0.26.0 // indirect k8s.io/client-go v0.26.2 // indirect k8s.io/component-base v0.26.2 // indirect k8s.io/klog/v2 v2.90.1 // indirect k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect k8s.io/kubectl v0.26.0 // indirect k8s.io/utils v0.0.0-20230313181309-38a27ef9d749 // indirect oras.land/oras-go v1.2.2 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/kustomize/api v0.12.1 // indirect sigs.k8s.io/kustomize/kyaml v0.13.9 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect ) 07070100000041000081A4000000000000000000000001673B898300019CD2000000000000000000000000000000000000001600000000helmify-0.4.16/go.sumcloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 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.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= 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.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/sprig/v3 v3.2.1/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= github.com/Masterminds/squirrel v1.5.3 h1:YPpoceAcxuzIljlr5iWpNKaql7hLeG1KLSrhvdHpkZc= github.com/Masterminds/squirrel v1.5.3/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/hcsshim v0.9.6 h1:VwnDOgLeoi2du6dAznfmspNqTiwczvjv4K7NxuY9jsY= github.com/Microsoft/hcsshim v0.9.6/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/a8m/expect v1.0.0/go.mod h1:4IwSCMumY49ScypDnjNbYEjgVeqy1/U2cEs3Lat96eA= 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/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= 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/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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng= github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ= github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 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/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/containerd/cgroups v1.0.4 h1:jN/mbWBEaz+T1pi5OFtnkQ+8qnmEbAr1Oo1FRm5B0dA= github.com/containerd/cgroups v1.0.4/go.mod h1:nLNQtsF7Sl2HxNebu77i1R0oDlhiTG+kO4JTrUzo6IA= github.com/containerd/containerd v1.6.15 h1:4wWexxzLNHNE46aIETc6ge4TofO550v+BlLoANrbses= github.com/containerd/containerd v1.6.15/go.mod h1:U2NnBPIhzJDm59xF7xB2MMHnKtggpZ+phKg8o2TKj2c= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/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/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= github.com/cyphar/filepath-securejoin v0.2.3/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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aBfCb7iqHmDEIp6fBvC/hQUddQfg+3qdYjwzaiP9Hnc= github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2/go.mod h1:WHNsWjnIn2V1LYOrME7e8KxSeKunYHsxEm4am0BUtcI= github.com/docker/cli v20.10.21+incompatible h1:qVkgyYUnOLQ98LtXBrwd/duVqPT2X4SHndOuGsfwyhU= github.com/docker/cli v20.10.21+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v20.10.21+incompatible h1:UTLdBmHk3bEY+w8qeO5KttOhy6OmXWsl/FEet9Uswog= github.com/docker/docker v20.10.21+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-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= 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.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 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/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.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= 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/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gorp/gorp/v3 v3.0.5 h1:PUjzYdYu3HBOh8LE+UUmRG2P0IRDak9XMeGNvaeq4Ow= github.com/go-gorp/gorp/v3 v3.0.5/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-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 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.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8= github.com/go-openapi/jsonreference v0.20.1/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/gobuffalo/logger v1.0.6 h1:nnZNpxYo0zx+Aj9RfMPBm+x9zAU2OayFh/xrAWi34HU= github.com/gobuffalo/logger v1.0.6/go.mod h1:J31TBEHR1QLV2683OXTAItYIg8pv2JMHnF/quuAbMjs= github.com/gobuffalo/packd v1.0.1 h1:U2wXfRr4E9DH8IdsDLlRFwTZTK7hLfq9qT/QHXGVe/0= github.com/gobuffalo/packd v1.0.1/go.mod h1:PP2POP3p3RXGz7Jh6eYEf93S7vA2za6xM7QT85L4+VY= github.com/gobuffalo/packr/v2 v2.8.3 h1:xE1yzvnO56cUC0sTpKR3DIbxZgB54AftTFMhB2XEWlY= github.com/gobuffalo/packr/v2 v2.8.3/go.mod h1:0SahksCVcx4IMnigTjiFuyldmTrdTctXsOdiU5KwbKc= 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/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godror/godror v0.24.2/go.mod h1:wZv/9vPiUib6tkoDl+AZ/QLf5YZgMravZ7jxH2eQWAE= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 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/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k= github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/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/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 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.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= 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.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 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/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.3/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/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.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/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 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.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 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.11.13 h1:eSvu8Tmq6j2psUJqJrLcWH6K3w5Dwc+qipbaA6eVEN4= github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kortschak/utter v1.0.1/go.mod h1:vSmSjbyrlKjjsL71193LmzBOKgwePk9DH6uFaWHIInc= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 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.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= 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/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc= github.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY= github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2aSZ0mcI= github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 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.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= 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-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI= 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/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= 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.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.5/go.mod h1:v8+iFts2sPIKUV1ltktPXMCC8fumSKFItNcD2cLtRR4= 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-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 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.5.0 h1:2Ks8/r6lopsxWi9m58nlwjaeSzUX9iiL1vj5qB/9ObI= github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-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/nelsam/hel/v2 v2.3.2/go.mod h1:1ZTGfU2PFTOd5mx22i5O0Lc2GY933lQ2wb/ggy+rL3w= github.com/nelsam/hel/v2 v2.3.3/go.mod h1:1ZTGfU2PFTOd5mx22i5O0Lc2GY933lQ2wb/ggy+rL3w= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs= github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo= github.com/onsi/gomega v1.23.0 h1:/oxKu9c2HVap+F3PfKort2Hw5DEU+HGlW8n+tguWsys= github.com/onsi/gomega v1.23.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= 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-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.3/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/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= 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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/poy/onpar v0.0.0-20200406201722-06f95a1c68e8/go.mod h1:nSbFQvMj97ZyhFRSJYtut+msi4sOY6zJDGCdSc+/rZU= github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= 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.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.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.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rubenv/sql-migrate v1.3.1 h1:Vx+n4Du8X8VTYuXbhNxdEUoh6wiJERA0GlWocR5FrbA= github.com/rubenv/sql-migrate v1.3.1/go.mod h1:YzG/Vh82CwyhTFXy+Mf5ahAiiEOpAlHurg+23VEzcsk= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= 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/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 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 v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= 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/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= 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.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 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 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/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/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/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.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc= go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/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-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-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-20181023162649-9b4f9f5ad519/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-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 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.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/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-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/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-20210809222454-d867a43fc93e/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-20221013171732-95e765b1cc43/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.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.6.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.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200313205530-4303120df7d8/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-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= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= 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.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.30.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-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/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-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.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.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= helm.sh/helm/v3 v3.11.2 h1:P3cLaFxfoxaGLGJVnoPrhf1j86LC5EDINSpYSpMUkkA= helm.sh/helm/v3 v3.11.2/go.mod h1:Hw+09mfpDiRRKAgAIZlFkPSeOkvv7Acl5McBvQyNPVw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.26.2 h1:dM3cinp3PGB6asOySalOZxEG4CZ0IAdJsrYZXE/ovGQ= k8s.io/api v0.26.2/go.mod h1:1kjMQsFE+QHPfskEcVNgL3+Hp88B80uj0QtSOlj8itU= k8s.io/apiextensions-apiserver v0.26.2 h1:/yTG2B9jGY2Q70iGskMf41qTLhL9XeNN2KhI0uDgwko= k8s.io/apiextensions-apiserver v0.26.2/go.mod h1:Y7UPgch8nph8mGCuVk0SK83LnS8Esf3n6fUBgew8SH8= k8s.io/apimachinery v0.26.2 h1:da1u3D5wfR5u2RpLhE/ZtZS2P7QvDgLZTi9wrNZl/tQ= k8s.io/apimachinery v0.26.2/go.mod h1:ats7nN1LExKHvJ9TmwootT00Yz05MuYqPXEXaVeOy5I= k8s.io/apiserver v0.26.2 h1:Pk8lmX4G14hYqJd1poHGC08G03nIHVqdJMR0SD3IH3o= k8s.io/apiserver v0.26.2/go.mod h1:GHcozwXgXsPuOJ28EnQ/jXEM9QeG6HT22YxSNmpYNh8= k8s.io/cli-runtime v0.26.0 h1:aQHa1SyUhpqxAw1fY21x2z2OS5RLtMJOCj7tN4oq8mw= k8s.io/cli-runtime v0.26.0/go.mod h1:o+4KmwHzO/UK0wepE1qpRk6l3o60/txUZ1fEXWGIKTY= k8s.io/client-go v0.26.2 h1:s1WkVujHX3kTp4Zn4yGNFK+dlDXy1bAAkIl+cFAiuYI= k8s.io/client-go v0.26.2/go.mod h1:u5EjOuSyBa09yqqyY7m3abZeovO/7D/WehVVlZ2qcqU= k8s.io/component-base v0.26.2 h1:IfWgCGUDzrD6wLLgXEstJKYZKAFS2kO+rBRi0p3LqcI= k8s.io/component-base v0.26.2/go.mod h1:DxbuIe9M3IZPRxPIzhch2m1eT7uFrSBJUBuVCQEBivs= k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= k8s.io/kubectl v0.26.0 h1:xmrzoKR9CyNdzxBmXV7jW9Ln8WMrwRK6hGbbf69o4T0= k8s.io/kubectl v0.26.0/go.mod h1:eInP0b+U9XUJWSYeU9XZnTA+cVYuWyl3iYPGtru0qhQ= k8s.io/utils v0.0.0-20230313181309-38a27ef9d749 h1:xMMXJlJbsU8w3V5N2FLDQ8YgU8s1EoULdbQBcAeNJkY= k8s.io/utils v0.0.0-20230313181309-38a27ef9d749/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= oras.land/oras-go v1.2.2 h1:0E9tOHUfrNH7TCDk5KU0jVBEzCqbfdyuVfGmJ7ZeRPE= oras.land/oras-go v1.2.2/go.mod h1:Apa81sKoZPpP7CDciE006tSZ0x3Q3+dOoBcMZ/aNxvw= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM= sigs.k8s.io/kustomize/api v0.12.1/go.mod h1:y3JUhimkZkR6sbLNwfJHxvo1TCLwuwm14sCYnkH6S1s= sigs.k8s.io/kustomize/kyaml v0.13.9 h1:Qz53EAaFFANyNgyOEJbT/yoIHygK40/ZcvU3rgry2Tk= sigs.k8s.io/kustomize/kyaml v0.13.9/go.mod h1:QsRbD0/KcU+wdk0/L0fIp2KLnohkVzs6fQ85/nOXac4= 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 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= 07070100000042000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000001800000000helmify-0.4.16/internal07070100000043000081A4000000000000000000000001673B8983000002C5000000000000000000000000000000000000002600000000helmify-0.4.16/internal/test_utils.gopackage internal import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/serializer/yaml" ) const ( nsYaml = `apiVersion: v1 kind: Namespace metadata: labels: control-plane: controller-manager name: my-operator-system` TestNsName = "my-operator-system" ) // TestNs k8s namespace object example. var TestNs = GenerateObj(nsYaml) // GenerateObj generates unstructured form yaml string. func GenerateObj(objYaml string) *unstructured.Unstructured { obj := unstructured.Unstructured{} dec := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme) _, _, err := dec.Decode([]byte(objYaml), nil, &obj) if err != nil { panic(err) } return &obj } 07070100000044000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000001300000000helmify-0.4.16/pkg07070100000045000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000001700000000helmify-0.4.16/pkg/app07070100000046000081A4000000000000000000000001673B898300000A0F000000000000000000000000000000000000001E00000000helmify-0.4.16/pkg/app/app.gopackage app import ( "context" "io" "os" "os/signal" "syscall" "github.com/arttor/helmify/pkg/file" "github.com/arttor/helmify/pkg/processor/job" "github.com/arttor/helmify/pkg/processor/poddisruptionbudget" "github.com/arttor/helmify/pkg/processor/statefulset" "github.com/sirupsen/logrus" "github.com/arttor/helmify/pkg/config" "github.com/arttor/helmify/pkg/decoder" "github.com/arttor/helmify/pkg/helm" "github.com/arttor/helmify/pkg/processor" "github.com/arttor/helmify/pkg/processor/configmap" "github.com/arttor/helmify/pkg/processor/crd" "github.com/arttor/helmify/pkg/processor/daemonset" "github.com/arttor/helmify/pkg/processor/deployment" "github.com/arttor/helmify/pkg/processor/rbac" "github.com/arttor/helmify/pkg/processor/secret" "github.com/arttor/helmify/pkg/processor/service" "github.com/arttor/helmify/pkg/processor/storage" "github.com/arttor/helmify/pkg/processor/webhook" ) // Start - application entrypoint for processing input to a Helm chart. func Start(stdin io.Reader, config config.Config) error { err := config.Validate() if err != nil { return err } setLogLevel(config) ctx, cancelFunc := context.WithCancel(context.Background()) defer cancelFunc() done := make(chan os.Signal, 1) signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) go func() { <-done logrus.Debug("Received termination, signaling shutdown") cancelFunc() }() appCtx := New(config, helm.NewOutput()) appCtx = appCtx.WithProcessors( configmap.New(), crd.New(), daemonset.New(), deployment.New(), statefulset.New(), storage.New(), service.New(), service.NewIngress(), rbac.ClusterRoleBinding(), rbac.Role(), rbac.RoleBinding(), rbac.ServiceAccount(), secret.New(), webhook.Issuer(), webhook.Certificate(), webhook.ValidatingWebhook(), webhook.MutatingWebhook(), job.NewCron(), job.NewJob(), poddisruptionbudget.New(), ).WithDefaultProcessor(processor.Default()) if len(config.Files) != 0 { file.Walk(config.Files, config.FilesRecursively, func(filename string, fileReader io.Reader) { objects := decoder.Decode(ctx.Done(), fileReader) for obj := range objects { appCtx.Add(obj, filename) } }) } else { objects := decoder.Decode(ctx.Done(), stdin) for obj := range objects { appCtx.Add(obj, "") } } return appCtx.CreateHelm(ctx.Done()) } func setLogLevel(config config.Config) { logrus.SetLevel(logrus.ErrorLevel) if config.Verbose { logrus.SetLevel(logrus.InfoLevel) } if config.VeryVerbose { logrus.SetLevel(logrus.DebugLevel) } } 07070100000047000081A4000000000000000000000001673B898300000527000000000000000000000000000000000000002700000000helmify-0.4.16/pkg/app/app_e2e_test.gopackage app import ( "bufio" "os" "testing" "github.com/arttor/helmify/pkg/config" "github.com/stretchr/testify/assert" "helm.sh/helm/v3/pkg/action" ) const ( operatorChartName = "test-operator" appChartName = "test-app" ) func TestOperator(t *testing.T) { file, err := os.Open("../../test_data/k8s-operator-kustomize.output") assert.NoError(t, err) objects := bufio.NewReader(file) err = Start(objects, config.Config{ChartName: operatorChartName}) assert.NoError(t, err) t.Cleanup(func() { err = os.RemoveAll(operatorChartName) assert.NoError(t, err) }) helmLint := action.NewLint() helmLint.Strict = true helmLint.Namespace = "test-ns" result := helmLint.Run([]string{operatorChartName}, nil) for _, err = range result.Errors { assert.NoError(t, err) } } func TestApp(t *testing.T) { file, err := os.Open("../../test_data/sample-app.yaml") assert.NoError(t, err) objects := bufio.NewReader(file) err = Start(objects, config.Config{ChartName: appChartName}) assert.NoError(t, err) t.Cleanup(func() { err = os.RemoveAll(appChartName) assert.NoError(t, err) }) helmLint := action.NewLint() helmLint.Strict = true helmLint.Namespace = "test-ns" result := helmLint.Run([]string{appChartName}, nil) for _, err = range result.Errors { assert.NoError(t, err) } } 07070100000048000081A4000000000000000000000001673B898300000C59000000000000000000000000000000000000002200000000helmify-0.4.16/pkg/app/context.gopackage app import ( "github.com/arttor/helmify/pkg/config" "github.com/arttor/helmify/pkg/helmify" "github.com/arttor/helmify/pkg/metadata" "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) // appContext helm processing context. Stores processed objects. type appContext struct { processors []helmify.Processor defaultProcessor helmify.Processor output helmify.Output config config.Config appMeta *metadata.Service objects []*unstructured.Unstructured fileNames []string } // New returns context with config set. func New(config config.Config, output helmify.Output) *appContext { return &appContext{ config: config, appMeta: metadata.New(config), output: output, } } // WithProcessors add processors to the context and returns it. func (c *appContext) WithProcessors(processors ...helmify.Processor) *appContext { c.processors = append(c.processors, processors...) return c } // WithDefaultProcessor add defaultProcessor for unknown resources to the context and returns it. func (c *appContext) WithDefaultProcessor(processor helmify.Processor) *appContext { c.defaultProcessor = processor return c } // Add k8s object to app context. func (c *appContext) Add(obj *unstructured.Unstructured, filename string) { // we need to add all objects before start processing only to define app metadata. c.appMeta.Load(obj) c.objects = append(c.objects, obj) c.fileNames = append(c.fileNames, filename) } // CreateHelm creates helm chart from context k8s objects. func (c *appContext) CreateHelm(stop <-chan struct{}) error { logrus.WithFields(logrus.Fields{ "ChartName": c.appMeta.ChartName(), "Namespace": c.appMeta.Namespace(), }).Info("creating a chart") var templates []helmify.Template var filenames []string for i, obj := range c.objects { template, err := c.process(obj) if err != nil { return err } if template != nil { templates = append(templates, template) filename := template.Filename() if c.fileNames[i] != "" { filename = c.fileNames[i] } filenames = append(filenames, filename) } select { case <-stop: return nil default: } } return c.output.Create(c.config.ChartDir, c.config.ChartName, c.config.Crd, c.config.CertManagerAsSubchart, c.config.CertManagerVersion, c.config.CertManagerInstallCRD, templates, filenames) } func (c *appContext) process(obj *unstructured.Unstructured) (helmify.Template, error) { for _, p := range c.processors { if processed, result, err := p.Process(c.appMeta, obj); processed { if err != nil { return nil, err } logrus.WithFields(logrus.Fields{ "ApiVersion": obj.GetAPIVersion(), "Kind": obj.GetKind(), "Name": obj.GetName(), }).Debug("processed") return result, nil } } if c.defaultProcessor == nil { logrus.WithFields(logrus.Fields{ "ApiVersion": obj.GetAPIVersion(), "Kind": obj.GetKind(), "Name": obj.GetName(), }).Warn("Skipping: no suitable processor for resource.") return nil, nil } _, t, err := c.defaultProcessor.Process(c.appMeta, obj) return t, err } 07070100000049000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000001B00000000helmify-0.4.16/pkg/cluster0707010000004A000081A4000000000000000000000001673B898300000094000000000000000000000000000000000000002500000000helmify-0.4.16/pkg/cluster/domain.gopackage cluster const ( DefaultDomain = "cluster.local" DomainKey = "kubernetesClusterDomain" DomainEnv = "KUBERNETES_CLUSTER_DOMAIN" ) 0707010000004B000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000001A00000000helmify-0.4.16/pkg/config0707010000004C000081A4000000000000000000000001673B8983000007F8000000000000000000000000000000000000002400000000helmify-0.4.16/pkg/config/config.gopackage config import ( "fmt" "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/util/validation" ) // defaultChartName - default name for a helm chart directory. const defaultChartName = "chart" // Config for Helmify application. type Config struct { // ChartName name of the Helm chart and its base directory where Chart.yaml is located. ChartName string // ChartDir - optional path to chart dir. Full chart path will be: ChartDir/ChartName/Chart.yaml. ChartDir string // Verbose set true to see WARN and INFO logs. Verbose bool // VeryVerbose set true to see WARN, INFO, and DEBUG logs. VeryVerbose bool // crd-dir set true to enable crd folder. Crd bool // ImagePullSecrets flag ImagePullSecrets bool // GenerateDefaults enables the generation of empty values placeholders for common customization options of helm chart // current generated values: tolerances, node selectors, topology constraints GenerateDefaults bool // CertManagerAsSubchart enables the generation of a subchart for cert-manager CertManagerAsSubchart bool // CertManagerVersion sets cert-manager version in dependency CertManagerVersion string // CertManagerVersion enables installation of cert-manager CRD CertManagerInstallCRD bool // Files - directories or files with k8s manifests Files []string // FilesRecursively read Files recursively FilesRecursively bool // OriginalName retains Kubernetes resource's original name OriginalName bool // PreserveNs retains the namespaces on the Kubernetes manifests PreserveNs bool // AddWebhookOption enables the generation of a webhook option in values.yamlß AddWebhookOption bool } func (c *Config) Validate() error { if c.ChartName == "" { logrus.Infof("Chart name is not set. Using default name '%s", defaultChartName) c.ChartName = defaultChartName } err := validation.IsDNS1123Subdomain(c.ChartName) if err != nil { for _, e := range err { logrus.Errorf("Invalid chart name %s", e) } return fmt.Errorf("invalid chart name %s", c.ChartName) } return nil } 0707010000004D000081A4000000000000000000000001673B898300000535000000000000000000000000000000000000002900000000helmify-0.4.16/pkg/config/config_test.gopackage config import ( "testing" "github.com/stretchr/testify/assert" ) func TestConfig_Validate(t *testing.T) { type fields struct { ChartName string Verbose bool VeryVerbose bool } tests := []struct { name string fields fields wantErr bool }{ {name: "valid", fields: fields{ChartName: ""}, wantErr: false}, {name: "valid", fields: fields{ChartName: "my.chart123"}, wantErr: false}, {name: "valid", fields: fields{ChartName: "my-chart123"}, wantErr: false}, {name: "invalid", fields: fields{ChartName: "my_chart123"}, wantErr: true}, {name: "invalid", fields: fields{ChartName: "my char123t"}, wantErr: true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &Config{ ChartName: tt.fields.ChartName, Verbose: tt.fields.Verbose, VeryVerbose: tt.fields.VeryVerbose, } if err := c.Validate(); (err != nil) != tt.wantErr { t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr) } }) } t.Run("chart name not set", func(t *testing.T) { c := &Config{} err := c.Validate() assert.NoError(t, err) assert.Equal(t, defaultChartName, c.ChartName) }) t.Run("chart name set", func(t *testing.T) { c := &Config{ChartName: "test"} err := c.Validate() assert.NoError(t, err) assert.Equal(t, "test", c.ChartName) }) } 0707010000004E000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000001B00000000helmify-0.4.16/pkg/decoder0707010000004F000081A4000000000000000000000001673B898300000782000000000000000000000000000000000000002600000000helmify-0.4.16/pkg/decoder/decoder.gopackage decoder import ( "errors" "io" "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/serializer/yaml" yamlutil "k8s.io/apimachinery/pkg/util/yaml" ) const ( yamlDecoderBufferSize = 100 decoderResultChannelBufferSize = 1 ) // Decode - reads bytes stream of k8s yaml manifests and decodes it to k8s unstructured objects. // Non-blocking function. Sends results into buffered channel. Closes channel on io.EOF. func Decode(stop <-chan struct{}, reader io.Reader) <-chan *unstructured.Unstructured { decoder := yamlutil.NewYAMLOrJSONDecoder(reader, yamlDecoderBufferSize) res := make(chan *unstructured.Unstructured, decoderResultChannelBufferSize) go func() { defer close(res) logrus.Debug("Start processing...") for { select { case <-stop: logrus.Debug("Exiting: received stop signal") return default: } var rawObj runtime.RawExtension err := decoder.Decode(&rawObj) if errors.Is(err, io.EOF) { logrus.Debug("EOF received. Finishing input objects decoding.") return } if err != nil { logrus.WithError(err).Error("unable to decode yaml from input") continue } obj, _, err := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme).Decode(rawObj.Raw, nil, nil) if err != nil { logrus.WithError(err).Error("unable to decode yaml") continue } unstructuredMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj) if err != nil { logrus.WithError(err).Error("unable to map yaml to k8s unstructured") continue } object := &unstructured.Unstructured{Object: unstructuredMap} logrus.WithFields(logrus.Fields{ "ApiVersion": object.GetAPIVersion(), "Kind": object.GetKind(), "Name": object.GetName(), }).Debug("decoded") res <- object } }() return res } 07070100000050000081A4000000000000000000000001673B8983000006BC000000000000000000000000000000000000002B00000000helmify-0.4.16/pkg/decoder/decoder_test.gopackage decoder import ( "strings" "testing" "github.com/stretchr/testify/assert" ) const ( validObjects2 = `apiVersion: v1 kind: Service metadata: name: my-operator-webhook-service namespace: my-operator-system spec: ports: - port: 443 targetPort: 9443 selector: control-plane: controller-manager --- apiVersion: v1 kind: Namespace metadata: labels: control-plane: controller-manager name: my-operator-system ` validObjects2withInvalid = `ajrcmq84xpru038um9q8 wqprux934ur8wcnqwp8urxqwrxuqweruncw --- apiVersion: v1 kind: Service metadata: name: my-operator-webhook-service namespace: my-operator-system spec: ports: - port: 443 targetPort: 9443 selector: control-plane: controller-manager --- --- --- 8umx9284ru 82q983y49q q 3408tuqw8e q 49tuqw[fa iwfaowoewihfe4hf --- apiVersion: v1 kind: Namespace metadata: labels: control-plane: controller-manager name: my-operator-system --- apiVersion: v1 metadata: labels: ` validObjects0 = `--- --- --- ` ) func TestDecodeOk(t *testing.T) { reader := strings.NewReader(validObjects2) stop := make(chan struct{}) objects := Decode(stop, reader) i := 0 for range objects { i++ } assert.Equal(t, 2, i, "decoded two objects") } func TestDecodeEmptyObj(t *testing.T) { reader := strings.NewReader(validObjects0) stop := make(chan struct{}) objects := Decode(stop, reader) i := 0 for range objects { i++ } assert.Equal(t, 0, i, "decoded none objects") } func TestDecodeInvalidObj(t *testing.T) { reader := strings.NewReader(validObjects2withInvalid) stop := make(chan struct{}) objects := Decode(stop, reader) i := 0 for range objects { i++ } assert.Equal(t, 2, i, "decoded 2 valid objects") } 07070100000051000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000001800000000helmify-0.4.16/pkg/file07070100000052000081A4000000000000000000000001673B89830000079A000000000000000000000000000000000000002200000000helmify-0.4.16/pkg/file/reader.gopackage file import ( "github.com/sirupsen/logrus" "io" "io/fs" "os" "path/filepath" ) func Walk(paths []string, recursively bool, walkFunc func(filename string, r io.Reader)) { for _, path := range paths { info, err := os.Stat(path) if err != nil { logrus.Warnf("no such file or directory %q: %v", path, err) continue } // handle single file file: if !info.IsDir() { file, err := os.Open(path) if err != nil { logrus.Warnf("unable to open file %q: %v", file.Name(), err) continue } walkFunc(info.Name(), file) err = file.Close() if err != nil { logrus.Warnf("unable to close file %q: %v", file.Name(), err) } continue } // handle directory non-recursively: if !recursively { dir, err := os.Open(path) if err != nil { logrus.Warnf("unable to open directory %q: %v", dir.Name(), err) continue } files, err := dir.ReadDir(0) if err != nil { logrus.Warnf("unable to read directory %q: %v", dir.Name(), err) continue } for _, f := range files { if f.IsDir() { continue } file, err := os.Open(filepath.Join(path, f.Name())) if err != nil { logrus.Warnf("unable to open file %q: %v", file.Name(), err) continue } walkFunc(f.Name(), file) err = file.Close() if err != nil { logrus.Warnf("unable to close file %q: %v", file.Name(), err) } continue } continue } // handle directory recursively: err = filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error { if err != nil { return err } if d.IsDir() { return nil } file, err := os.Open(path) if err != nil { return err } walkFunc(d.Name(), file) err = file.Close() if err != nil { logrus.Warnf("unable to close file %q: %v", file.Name(), err) } return nil }) if err != nil { logrus.Warnf("unable to open %q: %v", info.Name(), err) continue } } } 07070100000053000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000001A00000000helmify-0.4.16/pkg/format07070100000054000081A4000000000000000000000001673B898300000265000000000000000000000000000000000000002800000000helmify-0.4.16/pkg/format/fix_quotes.gopackage format import ( "strings" ) // FixUnterminatedQuotes check for Unterminated Quotes in helm templated strings // See https://github.com/arttor/helmify/issues/12 func FixUnterminatedQuotes(in string) string { sb := strings.Builder{} hasUntermQuotes := false lines := strings.Split(in, "\n") for i, line := range lines { if hasUntermQuotes { line = " " + strings.TrimSpace(line) hasUntermQuotes = false } else { hasUntermQuotes = strings.Count(line, "\"")%2 != 0 } sb.WriteString(line) if !hasUntermQuotes && i != len(lines)-1 { sb.WriteString("\n") } } return sb.String() } 07070100000055000081A4000000000000000000000001673B898300000703000000000000000000000000000000000000002D00000000helmify-0.4.16/pkg/format/fix_quotes_test.gopackage format import "testing" func TestFixUnterminatedQuotes(t *testing.T) { tests := []struct { name string in string want string }{ { name: "remove line break for unterminated quotes", in: `apiVersion: v1 kind: Secret metadata: name: {{ include "app.fullname" . }}-my-secret-vars labels: {{- include "app.labels" . | nindent 4 }} data: ELASTIC_FOOBAR_HUNTER123_MEOWTOWN_VERIFY: {{ required "mySecretVars.elasticFoobarHunter123MeowtownVerify is required" .Values.mySecretVars.elasticFoobarHunter123MeowtownVerify | b64enc | quote }} VAR1: {{ required "mySecretVars.var1 is required" .Values.mySecretVars.var1 | b64enc | quote }} VAR2: {{ required "mySecretVars.var2 is required" .Values.mySecretVars.var2 | b64enc | quote }} stringData: str: {{ required "mySecretVars.str is required" .Values.mySecretVars.str | quote }} type: opaque`, want: `apiVersion: v1 kind: Secret metadata: name: {{ include "app.fullname" . }}-my-secret-vars labels: {{- include "app.labels" . | nindent 4 }} data: ELASTIC_FOOBAR_HUNTER123_MEOWTOWN_VERIFY: {{ required "mySecretVars.elasticFoobarHunter123MeowtownVerify is required" .Values.mySecretVars.elasticFoobarHunter123MeowtownVerify | b64enc | quote }} VAR1: {{ required "mySecretVars.var1 is required" .Values.mySecretVars.var1 | b64enc | quote }} VAR2: {{ required "mySecretVars.var2 is required" .Values.mySecretVars.var2 | b64enc | quote }} stringData: str: {{ required "mySecretVars.str is required" .Values.mySecretVars.str | quote }} type: opaque`, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := FixUnterminatedQuotes(tt.in); got != tt.want { t.Errorf("FixUnterminatedQuotes() = %v, want %v", got, tt.want) } }) } } 07070100000056000081A4000000000000000000000001673B8983000000C4000000000000000000000000000000000000003200000000helmify-0.4.16/pkg/format/trailing_whitespaces.gopackage format import "regexp" var removeWhitespace = regexp.MustCompile(`(\s+)(\n|$)`) func RemoveTrailingWhitespaces(in string) string { return removeWhitespace.ReplaceAllString(in, "$2") } 07070100000057000081A4000000000000000000000001673B89830000027A000000000000000000000000000000000000003700000000helmify-0.4.16/pkg/format/trailing_whitespaces_test.gopackage format import "testing" func TestRemoveTrailingWhitespaces(t *testing.T) { tests := []struct { name string in string want string }{ { name: "", in: `abc `, want: `abc`, }, { name: "", in: `abc edf`, want: `abc edf`, }, { name: "", in: `abc edf `, want: `abc edf`, }, { name: "", in: `abc . edf .`, want: `abc . edf .`, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := RemoveTrailingWhitespaces(tt.in); got != tt.want { t.Errorf("RemoveTrailingWhitespaces() = %v, want %v", got, tt.want) } }) } } 07070100000058000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000001800000000helmify-0.4.16/pkg/helm07070100000059000081A4000000000000000000000001673B898300000F15000000000000000000000000000000000000002100000000helmify-0.4.16/pkg/helm/chart.gopackage helm import ( "fmt" "os" "path/filepath" "strings" "github.com/arttor/helmify/pkg/cluster" "github.com/arttor/helmify/pkg/helmify" "github.com/sirupsen/logrus" "sigs.k8s.io/yaml" ) // NewOutput creates interface to dump processed input to filesystem in Helm chart format. func NewOutput() helmify.Output { return &output{} } type output struct{} // Create a helm chart in the current directory: // chartName/ // // ├── .helmignore # Contains patterns to ignore when packaging Helm charts. // ├── Chart.yaml # Information about your chart // ├── values.yaml # The default values for your templates // └── templates/ # The template files // └── _helpers.tp # Helm default template partials // // Overwrites existing values.yaml and templates in templates dir on every run. func (o output) Create(chartDir, chartName string, crd bool, certManagerAsSubchart bool, certManagerVersion string, certManagerInstallCRD bool, templates []helmify.Template, filenames []string) error { err := initChartDir(chartDir, chartName, crd, certManagerAsSubchart, certManagerVersion) if err != nil { return err } // group templates into files files := map[string][]helmify.Template{} values := helmify.Values{} values[cluster.DomainKey] = cluster.DefaultDomain for i, template := range templates { file := files[filenames[i]] file = append(file, template) files[filenames[i]] = file err = values.Merge(template.Values()) if err != nil { return err } } cDir := filepath.Join(chartDir, chartName) for filename, tpls := range files { err = overwriteTemplateFile(filename, cDir, crd, tpls) if err != nil { return err } } err = overwriteValuesFile(cDir, values, certManagerAsSubchart, certManagerInstallCRD) if err != nil { return err } return nil } func overwriteTemplateFile(filename, chartDir string, crd bool, templates []helmify.Template) error { // pull in crd-dir setting and siphon crds into folder var subdir string if strings.Contains(filename, "crd") && crd { subdir = "crds" // create "crds" if not exists if _, err := os.Stat(filepath.Join(chartDir, "crds")); os.IsNotExist(err) { err = os.MkdirAll(filepath.Join(chartDir, "crds"), 0750) if err != nil { return fmt.Errorf("%w: unable create crds dir", err) } } } else { subdir = "templates" } file := filepath.Join(chartDir, subdir, filename) f, err := os.OpenFile(file, os.O_APPEND|os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) if err != nil { return fmt.Errorf("%w: unable to open %s", err, file) } defer f.Close() for i, t := range templates { logrus.WithField("file", file).Debug("writing a template into") err = t.Write(f) if err != nil { return fmt.Errorf("%w: unable to write into %s", err, file) } if i != len(templates)-1 { _, err = f.Write([]byte("\n---\n")) if err != nil { return fmt.Errorf("%w: unable to write into %s", err, file) } } } logrus.WithField("file", file).Info("overwritten") return nil } func overwriteValuesFile(chartDir string, values helmify.Values, certManagerAsSubchart bool, certManagerInstallCRD bool) error { if certManagerAsSubchart { _, err := values.Add(certManagerInstallCRD, "certmanager", "installCRDs") if err != nil { return fmt.Errorf("%w: unable to add cert-manager.installCRDs", err) } _, err = values.Add(true, "certmanager", "enabled") if err != nil { return fmt.Errorf("%w: unable to add cert-manager.enabled", err) } } res, err := yaml.Marshal(values) if err != nil { return fmt.Errorf("%w: unable to write marshal values.yaml", err) } file := filepath.Join(chartDir, "values.yaml") err = os.WriteFile(file, res, 0600) if err != nil { return fmt.Errorf("%w: unable to write values.yaml", err) } logrus.WithField("file", file).Info("overwritten") return nil } 0707010000005A000081A4000000000000000000000001673B898300000060000000000000000000000000000000000000001F00000000helmify-0.4.16/pkg/helm/doc.go// Package helm contains code for writing templates to a filesystem as Helm chart. package helm 0707010000005B000081A4000000000000000000000001673B89830000178B000000000000000000000000000000000000002000000000helmify-0.4.16/pkg/helm/init.gopackage helm import ( "fmt" "os" "path/filepath" "regexp" "strings" "github.com/sirupsen/logrus" ) const helmIgnore = `# Patterns to ignore when building packages. # This supports shell glob matching, relative path matching, and # negation (prefixed with !). Only one pattern per line. .DS_Store # Common VCS dirs .git/ .gitignore .bzr/ .bzrignore .hg/ .hgignore .svn/ # Common backup files *.swp *.bak *.tmp *.orig *~ # Various IDEs .project .idea/ *.tmproj .vscode/ ` const defaultHelpers = `{{/* Expand the name of the chart. */}} {{- define "<CHARTNAME>.name" -}} {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} {{- end }} {{/* Create a default fully qualified app name. We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). If release name contains chart name it will be used as a full name. */}} {{- define "<CHARTNAME>.fullname" -}} {{- if .Values.fullnameOverride }} {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} {{- else }} {{- $name := default .Chart.Name .Values.nameOverride }} {{- if contains $name .Release.Name }} {{- .Release.Name | trunc 63 | trimSuffix "-" }} {{- else }} {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} {{- end }} {{- end }} {{- end }} {{/* Create chart name and version as used by the chart label. */}} {{- define "<CHARTNAME>.chart" -}} {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} {{- end }} {{/* Common labels */}} {{- define "<CHARTNAME>.labels" -}} helm.sh/chart: {{ include "<CHARTNAME>.chart" . }} {{ include "<CHARTNAME>.selectorLabels" . }} {{- if .Chart.AppVersion }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} app.kubernetes.io/managed-by: {{ .Release.Service }} {{- end }} {{/* Selector labels */}} {{- define "<CHARTNAME>.selectorLabels" -}} app.kubernetes.io/name: {{ include "<CHARTNAME>.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} {{/* Create the name of the service account to use */}} {{- define "<CHARTNAME>.serviceAccountName" -}} {{- if .Values.serviceAccount.create }} {{- default (include "<CHARTNAME>.fullname" .) .Values.serviceAccount.name }} {{- else }} {{- default "default" .Values.serviceAccount.name }} {{- end }} {{- end }} ` const defaultChartfile = `apiVersion: v2 name: %s description: A Helm chart for Kubernetes # A chart can be either an 'application' or a 'library' chart. # # Application charts are a collection of templates that can be packaged into versioned archives # to be deployed. # # Library charts provide useful utilities or functions for the chart developer. They're included as # a dependency of application charts to inject those utilities and functions into the rendering # pipeline. Library charts do not define any templates and therefore cannot be deployed. type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) version: 0.1.0 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. appVersion: "0.1.0" ` const certManagerDependencies = ` dependencies: - name: cert-manager repository: https://charts.jetstack.io condition: certmanager.enabled alias: certmanager version: %q ` var chartName = regexp.MustCompile("^[a-zA-Z0-9._-]+$") const maxChartNameLength = 250 // initChartDir - creates Helm chart structure in chartName directory if not presented. func initChartDir(chartDir, chartName string, crd bool, certManagerAsSubchart bool, certManagerVersion string) error { if err := validateChartName(chartName); err != nil { return err } cDir := filepath.Join(chartDir, chartName) _, err := os.Stat(filepath.Join(cDir, "Chart.yaml")) if os.IsNotExist(err) { return createCommonFiles(chartDir, chartName, crd, certManagerAsSubchart, certManagerVersion) } logrus.Info("Skip creating Chart skeleton: Chart.yaml already exists.") return err } func validateChartName(name string) error { if name == "" || len(name) > maxChartNameLength { return fmt.Errorf("chart name must be between 1 and %d characters", maxChartNameLength) } if !chartName.MatchString(name) { return fmt.Errorf("chart name must match the regular expression %q", chartName.String()) } return nil } func createCommonFiles(chartDir, chartName string, crd bool, certManagerAsSubchart bool, certManagerVersion string) error { cDir := filepath.Join(chartDir, chartName) err := os.MkdirAll(filepath.Join(cDir, "templates"), 0750) if err != nil { return fmt.Errorf("%w: unable create chart/templates dir", err) } if crd { err = os.MkdirAll(filepath.Join(cDir, "crds"), 0750) if err != nil { return fmt.Errorf("%w: unable create crds dir", err) } } createFile := func(content []byte, path ...string) { if err != nil { return } file := filepath.Join(path...) err = os.WriteFile(file, content, 0640) if err == nil { logrus.WithField("file", file).Info("created") } } createFile(chartYAML(chartName, certManagerAsSubchart, certManagerVersion), cDir, "Chart.yaml") createFile([]byte(helmIgnore), cDir, ".helmignore") createFile(helpersYAML(chartName), cDir, "templates", "_helpers.tpl") return err } func chartYAML(appName string, certManagerAsSubchart bool, certManagerVersion string) []byte { chartFile := defaultChartfile if certManagerAsSubchart { chartFile += fmt.Sprintf(certManagerDependencies, certManagerVersion) } return []byte(fmt.Sprintf(chartFile, appName)) } func helpersYAML(chartName string) []byte { return []byte(strings.ReplaceAll(defaultHelpers, "<CHARTNAME>", chartName)) } 0707010000005C000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000001B00000000helmify-0.4.16/pkg/helmify0707010000005D000081A4000000000000000000000001673B89830000074C000000000000000000000000000000000000002400000000helmify-0.4.16/pkg/helmify/model.gopackage helmify import ( "io" "github.com/arttor/helmify/pkg/config" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) // Processor - converts k8s object to helm template. // Implement this interface and register it to a context to support a new k8s resource conversion. type Processor interface { // Process - converts k8s object to Helm template. // return false if not able to process given object type. Process(appMeta AppMetadata, unstructured *unstructured.Unstructured) (bool, Template, error) } // Template - represents Helm template in 'templates' directory. type Template interface { // Filename - returns template filename Filename() string // Values - returns set of values used in template Values() Values // Write - writes helm template into given writer Write(writer io.Writer) error } // Output - converts Template into helm chart on disk. type Output interface { Create(chartName, chartDir string, Crd bool, certManagerAsSubchart bool, certManagerVersion string, certManagerInstallCRD bool, templates []Template, filenames []string) error } // AppMetadata handle common information about K8s objects in the chart. type AppMetadata interface { // Namespace returns app namespace. Namespace() string // ChartName returns chart name ChartName() string // TemplatedName converts object name to templated Helm name. // Example: "my-app-service1" -> "{{ include "chart.fullname" . }}-service1" // "my-app-secret" -> "{{ include "chart.fullname" . }}-secret" // etc... TemplatedName(objName string) string // TemplatedString converts a string to templated string with chart name. TemplatedString(str string) string // TrimName trims common prefix from object name if exists. // We trim common prefix because helm already using release for this purpose. TrimName(objName string) string Config() config.Config } 0707010000005E000081A4000000000000000000000001673B898300000BE8000000000000000000000000000000000000002500000000helmify-0.4.16/pkg/helmify/values.gopackage helmify import ( "dario.cat/mergo" "fmt" "strconv" "strings" "github.com/iancoleman/strcase" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) // Values - represents helm template values.yaml. type Values map[string]interface{} // Merge given values with current instance. func (v *Values) Merge(values Values) error { if err := mergo.Merge(v, values, mergo.WithAppendSlice); err != nil { return fmt.Errorf("%w: unable to merge helm values", err) } return nil } // Add - adds given value to values and returns its helm template representation {{ .Values.<valueName> }} func (v *Values) Add(value interface{}, name ...string) (string, error) { name = toCamelCase(name) switch val := value.(type) { case int: value = int64(val) case int8: value = int64(val) case int16: value = int64(val) case int32: value = int64(val) } err := unstructured.SetNestedField(*v, value, name...) if err != nil { return "", fmt.Errorf("%w: unable to set value: %v", err, name) } _, isString := value.(string) if isString { return "{{ .Values." + strings.Join(name, ".") + " | quote }}", nil } _, isSlice := value.([]interface{}) if isSlice { spaces := strconv.Itoa(len(name) * 2) return "{{ toYaml .Values." + strings.Join(name, ".") + " | nindent " + spaces + " }}", nil } return "{{ .Values." + strings.Join(name, ".") + " }}", nil } // AddYaml - adds given value to values and returns its helm template representation as Yaml {{ .Values.<valueName> | toYaml | indent i }} // indent <= 0 will be omitted. func (v *Values) AddYaml(value interface{}, indent int, newLine bool, name ...string) (string, error) { name = toCamelCase(name) err := unstructured.SetNestedField(*v, value, name...) if err != nil { return "", fmt.Errorf("%w: unable to set value: %v", err, name) } if indent > 0 { if newLine { return "{{ .Values." + strings.Join(name, ".") + fmt.Sprintf(" | toYaml | nindent %d }}", indent), nil } return "{{ .Values." + strings.Join(name, ".") + fmt.Sprintf(" | toYaml | indent %d }}", indent), nil } return "{{ .Values." + strings.Join(name, ".") + " | toYaml }}", nil } // AddSecret - adds empty value to values and returns its helm template representation {{ required "<valueName>" .Values.<valueName> }}. // Set toBase64=true for Secret data to be base64 encoded and set false for Secret stringData. func (v *Values) AddSecret(toBase64 bool, name ...string) (string, error) { name = toCamelCase(name) nameStr := strings.Join(name, ".") err := unstructured.SetNestedField(*v, "", name...) if err != nil { return "", fmt.Errorf("%w: unable to set value: %v", err, nameStr) } res := fmt.Sprintf(`{{ required "%[1]s is required" .Values.%[1]s`, nameStr) if toBase64 { res += " | b64enc" } return res + " | quote }}", err } func toCamelCase(name []string) []string { for i, n := range name { camelCase := strcase.ToLowerCamel(n) if n == strings.ToUpper(n) { camelCase = strcase.ToLowerCamel(strings.ToLower(n)) } name[i] = camelCase } return name } 0707010000005F000081A4000000000000000000000001673B898300000952000000000000000000000000000000000000002A00000000helmify-0.4.16/pkg/helmify/values_test.gopackage helmify import ( "testing" "github.com/stretchr/testify/assert" ) func TestValues_Add(t *testing.T) { t.Run("quote func added for string values", func(t *testing.T) { testVal := Values{} res, err := testVal.Add("abc", "a", "b") assert.NoError(t, err) assert.Contains(t, res, "quote") }) t.Run("quote func not added for not string values", func(t *testing.T) { testVal := Values{} res, err := testVal.Add(int64(1), "a", "b") assert.NoError(t, err) assert.NotContains(t, res, "quote") res, err = testVal.Add(true, "a", "b") assert.NoError(t, err) assert.NotContains(t, res, "quote") res, err = testVal.Add(420.69, "a", "b") assert.NoError(t, err) assert.NotContains(t, res, "quote") }) t.Run("name path is dot formatted", func(t *testing.T) { testVal := Values{} res, err := testVal.Add(int64(1), "a", "b") assert.NoError(t, err) assert.Contains(t, res, " .Values.a.b ") }) t.Run("snake names camel cased", func(t *testing.T) { testVal := Values{} snake := "my_name" camel := "myName" res, err := testVal.Add(420.69, snake) assert.NoError(t, err) assert.NotContains(t, res, snake) assert.Contains(t, res, camel) }) t.Run("upper snake names camel cased", func(t *testing.T) { testVal := Values{} upSnake := "MY_NAME" camel := "myName" res, err := testVal.Add(420.69, upSnake) assert.NoError(t, err) assert.NotContains(t, res, upSnake) assert.Contains(t, res, camel) }) t.Run("kebab names camel cased", func(t *testing.T) { testVal := Values{} kebab := "my-name" camel := "myName" res, err := testVal.Add(420.69, kebab) assert.NoError(t, err) assert.NotContains(t, res, kebab) assert.Contains(t, res, camel) }) t.Run("dot names camel cased", func(t *testing.T) { testVal := Values{} dot := "my.name" camel := "myName" res, err := testVal.Add(420.69, dot) assert.NoError(t, err) assert.NotContains(t, res, dot) assert.Contains(t, res, camel) }) } func TestValues_AddSecret(t *testing.T) { t.Run("add base64 enc secret", func(t *testing.T) { testVal := Values{} res, err := testVal.AddSecret(true, "a", "b") assert.NoError(t, err) assert.Contains(t, res, "b64enc") }) t.Run("add not encoded secret", func(t *testing.T) { testVal := Values{} res, err := testVal.AddSecret(false, "a", "b") assert.NoError(t, err) assert.NotContains(t, res, "b64enc") }) } 07070100000060000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000001C00000000helmify-0.4.16/pkg/metadata07070100000061000081A4000000000000000000000001673B898300000CF6000000000000000000000000000000000000002800000000helmify-0.4.16/pkg/metadata/metadata.gopackage metadata import ( "fmt" "github.com/arttor/helmify/pkg/config" "strings" "github.com/arttor/helmify/pkg/helmify" "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" ) const nameTeml = `{{ include "%s.fullname" . }}-%s` var nsGVK = schema.GroupVersionKind{ Group: "", Version: "v1", Kind: "Namespace", } var crdGVK = schema.GroupVersionKind{ Group: "apiextensions.k8s.io", Version: "v1", Kind: "CustomResourceDefinition", } func New(conf config.Config) *Service { return &Service{names: make(map[string]struct{}), conf: conf} } type Service struct { commonPrefix string namespace string names map[string]struct{} conf config.Config } func (a *Service) Config() config.Config { return a.conf } // TrimName - tries to trim app common prefix for object name if detected. // If no common prefix - returns name as it is. // It is better to trim common prefix because Helm also adds release name as common prefix. func (a *Service) TrimName(objName string) string { trimmed := strings.TrimPrefix(objName, a.commonPrefix) trimmed = strings.TrimLeft(trimmed, "-./_ ") if trimmed == "" { return objName } return trimmed } var _ helmify.AppMetadata = &Service{} // Load processed objects one-by-one before actual processing to define app namespace, name common prefix and // other app meta information. func (a *Service) Load(obj *unstructured.Unstructured) { a.names[obj.GetName()] = struct{}{} a.commonPrefix = detectCommonPrefix(obj, a.commonPrefix) objNs := extractAppNamespace(obj) if objNs == "" { return } if a.namespace != "" && a.namespace != objNs { logrus.Warnf("Two different namespaces for app detected: %s and %s. Resulted char will have single namespace.", objNs, a.namespace) } a.namespace = objNs } // Namespace returns detected app namespace. func (a *Service) Namespace() string { return a.namespace } // ChartName returns ChartName. func (a *Service) ChartName() string { return a.conf.ChartName } // TemplatedName - converts object name to its Helm templated representation. // Adds chart fullname prefix from _helpers.tpl func (a *Service) TemplatedName(name string) string { if a.conf.OriginalName { return name } _, contains := a.names[name] if !contains { // template only app objects return name } name = a.TrimName(name) return fmt.Sprintf(nameTeml, a.conf.ChartName, name) } func (a *Service) TemplatedString(str string) string { name := a.TrimName(str) return fmt.Sprintf(nameTeml, a.conf.ChartName, name) } func extractAppNamespace(obj *unstructured.Unstructured) string { if obj.GroupVersionKind() == nsGVK { return obj.GetName() } return obj.GetNamespace() } func detectCommonPrefix(obj *unstructured.Unstructured, prevName string) string { if obj.GroupVersionKind() == crdGVK || obj.GroupVersionKind() == nsGVK { return prevName } if prevName == "" { return obj.GetName() } return commonPrefix(obj.GetName(), prevName) } func commonPrefix(one, two string) string { runes1 := []rune(one) runes2 := []rune(two) min := len(runes1) if min > len(runes2) { min = len(runes2) } for i := 0; i < min; i++ { if runes1[i] != runes2[i] { return string(runes1[:i]) } } return string(runes1[:min]) } 07070100000062000081A4000000000000000000000001673B898300000CD8000000000000000000000000000000000000002D00000000helmify-0.4.16/pkg/metadata/metadata_test.gopackage metadata import ( "fmt" "github.com/arttor/helmify/pkg/config" "testing" "github.com/arttor/helmify/internal" "github.com/stretchr/testify/assert" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) const res = `apiVersion: v1 kind: Secret metadata: name: %s namespace: %s` func Test_commonPrefix(t *testing.T) { type args struct { left, right string } tests := []struct { name string args args want string }{ { name: "left is a prefix of right", args: args{left: "test", right: "testimony"}, want: "test", }, { name: "common prefix", args: args{left: "testimony", right: "testicle"}, want: "testi", }, { name: "no common", args: args{left: "testimony", right: "abc"}, want: "", }, { name: "right is empty", args: args{left: "testimony", right: ""}, want: "", }, { name: "left is empty", args: args{left: "", right: "abc"}, want: "", }, { name: "both are empty", args: args{left: "", right: ""}, want: "", }, { name: "unicode", args: args{left: "багет", right: "багаж"}, want: "баг", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := commonPrefix(tt.args.left, tt.args.right); got != tt.want { t.Errorf("commonPrefix() = %v, want %v", got, tt.want) } }) } } func Test_Service(t *testing.T) { t.Run("load ns from object", func(t *testing.T) { obj := createRes("name", "ns") testSvc := New(config.Config{}) testSvc.Load(obj) assert.Equal(t, "ns", testSvc.Namespace()) testSvc.Load(internal.TestNs) assert.Equal(t, internal.TestNsName, testSvc.Namespace()) }) t.Run("get chart name", func(t *testing.T) { testSvc := New(config.Config{ChartName: "name"}) assert.Equal(t, "name", testSvc.ChartName()) }) t.Run("trim common prefix abc", func(t *testing.T) { testSvc := New(config.Config{}) testSvc.Load(createRes("abc-name1", "ns")) testSvc.Load(createRes("abc-name2", "ns")) testSvc.Load(createRes("abc-service", "ns")) assert.Equal(t, "name1", testSvc.TrimName("abc-name1")) assert.Equal(t, "name2", testSvc.TrimName("abc-name2")) assert.Equal(t, "service", testSvc.TrimName("abc-service")) }) t.Run("trim common prefix: no common", func(t *testing.T) { testSvc := New(config.Config{}) testSvc.Load(createRes("name1", "ns")) testSvc.Load(createRes("abc", "ns")) testSvc.Load(createRes("service", "ns")) assert.Equal(t, "name1", testSvc.TrimName("name1")) assert.Equal(t, "abc", testSvc.TrimName("abc")) assert.Equal(t, "service", testSvc.TrimName("service")) }) t.Run("template name", func(t *testing.T) { testSvc := New(config.Config{ChartName: "chart-name"}) testSvc.Load(createRes("abc", "ns")) templated := testSvc.TemplatedName("abc") assert.Equal(t, `{{ include "chart-name.fullname" . }}-abc`, templated) }) t.Run("template name: not process unknown name", func(t *testing.T) { testSvc := New(config.Config{ChartName: "chart-name"}) testSvc.Load(createRes("abc", "ns")) assert.Equal(t, "qwe", testSvc.TemplatedName("qwe")) assert.NotEqual(t, "abc", testSvc.TemplatedName("abc")) }) } func createRes(name, ns string) *unstructured.Unstructured { objYaml := fmt.Sprintf(res, name, ns) return internal.GenerateObj(objYaml) } 07070100000063000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000001D00000000helmify-0.4.16/pkg/processor07070100000064000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000002700000000helmify-0.4.16/pkg/processor/configmap07070100000065000081A4000000000000000000000001673B898300001233000000000000000000000000000000000000003400000000helmify-0.4.16/pkg/processor/configmap/configmap.gopackage configmap import ( "fmt" "github.com/arttor/helmify/pkg/format" "io" "strings" "text/template" "github.com/arttor/helmify/pkg/processor" "github.com/arttor/helmify/pkg/helmify" yamlformat "github.com/arttor/helmify/pkg/yaml" "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" ) var configMapTempl, _ = template.New("configMap").Parse( `{{ .Meta }} {{- if .Immutable }} {{ .Immutable }} {{- end }} {{- if .BinaryData }} {{ .BinaryData }} {{- end }} {{- if .Data }} {{ .Data }} {{- end }}`) var configMapGVC = schema.GroupVersionKind{ Group: "", Version: "v1", Kind: "ConfigMap", } // New creates processor for k8s ConfigMap resource. func New() helmify.Processor { return &configMap{} } type configMap struct{} // Process k8s ConfigMap object into template. Returns false if not capable of processing given resource type. func (d configMap) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstructured) (bool, helmify.Template, error) { if obj.GroupVersionKind() != configMapGVC { return false, nil, nil } var meta, immutable, binaryData, data string meta, err := processor.ProcessObjMeta(appMeta, obj) if err != nil { return true, nil, err } if field, exists, _ := unstructured.NestedBool(obj.Object, "immutable"); exists { immutable, err = yamlformat.Marshal(map[string]interface{}{"immutable": field}, 0) if err != nil { return true, nil, err } } if field, exists, _ := unstructured.NestedStringMap(obj.Object, "binaryData"); exists { binaryData, err = yamlformat.Marshal(map[string]interface{}{"binaryData": field}, 0) if err != nil { return true, nil, err } } name := appMeta.TrimName(obj.GetName()) var values helmify.Values if field, exists, _ := unstructured.NestedStringMap(obj.Object, "data"); exists { field, values = parseMapData(field, name) data, err = yamlformat.Marshal(map[string]interface{}{"data": field}, 0) if err != nil { return true, nil, err } data = strings.ReplaceAll(data, "'", "") } return true, &result{ name: name + ".yaml", data: struct { Meta string Immutable string BinaryData string Data string }{Meta: meta, Immutable: immutable, BinaryData: binaryData, Data: data}, values: values, }, nil } func parseMapData(data map[string]string, configName string) (map[string]string, helmify.Values) { values := helmify.Values{} for key, value := range data { valuesNamePath := []string{configName, key} if strings.HasSuffix(key, ".properties") { // handle properties templated, err := parseProperties(value, valuesNamePath, values) if err != nil { logrus.WithError(err).Errorf("unable to process configmap data: %v", valuesNamePath) continue } data[key] = templated continue } if strings.Contains(value, "\n") { value = format.RemoveTrailingWhitespaces(value) templatedVal, err := values.AddYaml(value, 1, false, valuesNamePath...) if err != nil { logrus.WithError(err).Errorf("unable to process multiline configmap data: %v", valuesNamePath) continue } data[key] = templatedVal continue } // handle plain string templatedVal, err := values.Add(value, valuesNamePath...) if err != nil { logrus.WithError(err).Errorf("unable to process configmap data: %v", valuesNamePath) continue } data[key] = templatedVal } return data, values } // func parseProperties(properties string, path []string, values helmify.Values) (string, error) { func parseProperties(properties interface{}, path []string, values helmify.Values) (string, error) { var res strings.Builder for _, line := range strings.Split(strings.TrimSuffix(properties.(string), "\n"), "\n") { prop := strings.Split(line, "=") if len(prop) != 2 { return "", fmt.Errorf("wrong property format in %v: %s", path, line) } propName, propVal := prop[0], prop[1] propNamePath := strings.Split(propName, ".") templatedVal, err := values.Add(propVal, append(path, propNamePath...)...) if err != nil { return "", err } _, err = res.WriteString(propName + "=" + templatedVal + "\n") if err != nil { return "", fmt.Errorf("%w: unable to write to string builder", err) } } return res.String(), nil } type result struct { name string data struct { Meta string Immutable string BinaryData string Data string } values helmify.Values } func (r *result) Filename() string { return r.name } func (r *result) Values() helmify.Values { return r.values } func (r *result) Write(writer io.Writer) error { return configMapTempl.Execute(writer, r.data) } 07070100000066000081A4000000000000000000000001673B8983000003F5000000000000000000000000000000000000003900000000helmify-0.4.16/pkg/processor/configmap/configmap_test.gopackage configmap import ( "testing" "github.com/arttor/helmify/pkg/metadata" "github.com/arttor/helmify/internal" "github.com/stretchr/testify/assert" ) const ( strConfigmap = `apiVersion: v1 kind: ConfigMap metadata: name: my-operator-manager-config namespace: my-operator-system data: dummyconfigmapkey: dummyconfigmapvalue controller_manager_config.yaml: | apiVersion: controller-runtime.sigs.k8s.io/v1alpha1 kind: ControllerManagerConfig health: healthProbeBindAddress: :8081` ) func Test_configMap_Process(t *testing.T) { var testInstance configMap t.Run("processed", func(t *testing.T) { obj := internal.GenerateObj(strConfigmap) processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, true, processed) }) t.Run("skipped", func(t *testing.T) { obj := internal.TestNs processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, false, processed) }) } 07070100000067000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000002100000000helmify-0.4.16/pkg/processor/crd07070100000068000081A4000000000000000000000001673B898300001097000000000000000000000000000000000000002800000000helmify-0.4.16/pkg/processor/crd/crd.gopackage crd import ( "bytes" "fmt" "github.com/sirupsen/logrus" "io" "strings" v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "sigs.k8s.io/yaml" "github.com/arttor/helmify/pkg/helmify" yamlformat "github.com/arttor/helmify/pkg/yaml" ) const crdTeml = `apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: %[1]s %[3]s labels: %[4]s {{- include "%[2]s.labels" . | nindent 4 }} spec: %[5]s status: acceptedNames: kind: "" plural: "" conditions: [] storedVersions: []` var crdGVC = schema.GroupVersionKind{ Group: "apiextensions.k8s.io", Version: "v1", Kind: "CustomResourceDefinition", } // New creates processor for k8s CustomResourceDefinition resource. func New() helmify.Processor { return &crd{} } type crd struct{} // Process k8s CustomResourceDefinition object into template. Returns false if not capable of processing given resource type. func (c crd) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstructured) (bool, helmify.Template, error) { if obj.GroupVersionKind() != crdGVC { return false, nil, nil } name, ok, err := unstructured.NestedString(obj.Object, "spec", "names", "singular") if err != nil || !ok { return true, nil, fmt.Errorf("%w: unable to create crd template", err) } if appMeta.Config().Crd { logrus.WithField("crd", name).Info("put CRD under crds dir without templating") // do not template CRDs when placed to crds dir res, err := yaml.Marshal(obj) if err != nil { return true, nil, fmt.Errorf("%w: unable to create crd template", err) } return true, &result{ name: name + "-crd.yaml", data: res, }, nil } var labels, annotations string if len(obj.GetAnnotations()) != 0 { a := obj.GetAnnotations() certName := a["cert-manager.io/inject-ca-from"] if certName != "" { certName = strings.TrimPrefix(certName, appMeta.Namespace()+"/") certName = appMeta.TrimName(certName) a["cert-manager.io/inject-ca-from"] = fmt.Sprintf(`{{ .Release.Namespace }}/{{ include "%[1]s.fullname" . }}-%[2]s`, appMeta.ChartName(), certName) } annotations, err = yamlformat.Marshal(map[string]interface{}{"annotations": a}, 2) if err != nil { return true, nil, err } } if len(obj.GetLabels()) != 0 { l := obj.GetLabels() // provided by Helm delete(l, "app.kubernetes.io/name") delete(l, "app.kubernetes.io/instance") delete(l, "app.kubernetes.io/version") delete(l, "app.kubernetes.io/managed-by") delete(l, "helm.sh/chart") if len(l) != 0 { labels, err = yamlformat.Marshal(l, 4) if err != nil { return true, nil, err } labels = strings.Trim(labels, "\n") } } specUnstr, ok, err := unstructured.NestedMap(obj.Object, "spec") if err != nil || !ok { return true, nil, fmt.Errorf("%w: unable to create crd template", err) } spec := v1.CustomResourceDefinitionSpec{} err = runtime.DefaultUnstructuredConverter.FromUnstructured(specUnstr, &spec) if err != nil { return true, nil, fmt.Errorf("%w: unable to cast to crd spec", err) } if spec.Conversion != nil { conv := spec.Conversion if conv.Strategy == v1.WebhookConverter { wh := conv.Webhook if wh != nil && wh.ClientConfig != nil && wh.ClientConfig.Service != nil { wh.ClientConfig.Service.Name = appMeta.TemplatedName(wh.ClientConfig.Service.Name) wh.ClientConfig.Service.Namespace = strings.ReplaceAll(wh.ClientConfig.Service.Namespace, appMeta.Namespace(), `{{ .Release.Namespace }}`) } } } specYaml, _ := yaml.Marshal(spec) specYaml = yamlformat.Indent(specYaml, 2) specYaml = bytes.TrimRight(specYaml, "\n ") res := fmt.Sprintf(crdTeml, obj.GetName(), appMeta.ChartName(), annotations, labels, string(specYaml)) res = strings.ReplaceAll(res, "\n\n", "\n") return true, &result{ name: name + "-crd.yaml", data: []byte(res), }, nil } type result struct { name string data []byte } func (r *result) Filename() string { return r.name } func (r *result) Values() helmify.Values { return helmify.Values{} } func (r *result) Write(writer io.Writer) error { _, err := writer.Write(r.data) return err } 07070100000069000081A4000000000000000000000001673B898300000436000000000000000000000000000000000000002D00000000helmify-0.4.16/pkg/processor/crd/crd_test.gopackage crd import ( "testing" "github.com/arttor/helmify/pkg/metadata" "github.com/arttor/helmify/internal" "github.com/stretchr/testify/assert" ) const ( strCRD = `apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: my-operator-system/my-operator-serving-cert creationTimestamp: null name: cephvolumes.test.example.com labels: example: true spec: group: test.example.com names: kind: CephVolume listKind: CephVolumeList plural: cephvolumes singular: cephvolume scope: Namespaced ` ) func Test_crd_Process(t *testing.T) { var testInstance crd t.Run("processed", func(t *testing.T) { obj := internal.GenerateObj(strCRD) processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, true, processed) }) t.Run("skipped", func(t *testing.T) { obj := internal.TestNs processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, false, processed) }) } 0707010000006A000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000002700000000helmify-0.4.16/pkg/processor/daemonset0707010000006B000081A4000000000000000000000001673B898300000F42000000000000000000000000000000000000003400000000helmify-0.4.16/pkg/processor/daemonset/daemonset.gopackage daemonset import ( "fmt" "github.com/arttor/helmify/pkg/processor/pod" "io" "strings" "text/template" "github.com/arttor/helmify/pkg/helmify" "github.com/arttor/helmify/pkg/processor" yamlformat "github.com/arttor/helmify/pkg/yaml" "github.com/iancoleman/strcase" appsv1 "k8s.io/api/apps/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" ) var daemonsetGVC = schema.GroupVersionKind{ Group: "apps", Version: "v1", Kind: "DaemonSet", } var daemonsetTempl, _ = template.New("daemonset").Parse( `{{- .Meta }} spec: selector: {{ .Selector }} template: metadata: labels: {{ .PodLabels }} {{- .PodAnnotations }} spec: {{ .Spec }}`) const selectorTempl = `%[1]s {{- include "%[2]s.selectorLabels" . | nindent 6 }} %[3]s` // New creates processor for k8s Daemonset resource. func New() helmify.Processor { return &daemonset{} } type daemonset struct{} // Process k8s Daemonset object into template. Returns false if not capable of processing given resource type. func (d daemonset) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstructured) (bool, helmify.Template, error) { if obj.GroupVersionKind() != daemonsetGVC { return false, nil, nil } dae := appsv1.DaemonSet{} err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &dae) if err != nil { return true, nil, fmt.Errorf("%w: unable to cast to daemonset", err) } meta, err := processor.ProcessObjMeta(appMeta, obj) if err != nil { return true, nil, err } values := helmify.Values{} name := appMeta.TrimName(obj.GetName()) matchLabels, err := yamlformat.Marshal(map[string]interface{}{"matchLabels": dae.Spec.Selector.MatchLabels}, 0) if err != nil { return true, nil, err } matchExpr := "" if dae.Spec.Selector.MatchExpressions != nil { matchExpr, err = yamlformat.Marshal(map[string]interface{}{"matchExpressions": dae.Spec.Selector.MatchExpressions}, 0) if err != nil { return true, nil, err } } selector := fmt.Sprintf(selectorTempl, matchLabels, appMeta.ChartName(), matchExpr) selector = strings.Trim(selector, " \n") selector = string(yamlformat.Indent([]byte(selector), 4)) podLabels, err := yamlformat.Marshal(dae.Spec.Template.ObjectMeta.Labels, 8) if err != nil { return true, nil, err } podLabels += fmt.Sprintf("\n {{- include \"%s.selectorLabels\" . | nindent 8 }}", appMeta.ChartName()) podAnnotations := "" if len(dae.Spec.Template.ObjectMeta.Annotations) != 0 { podAnnotations, err = yamlformat.Marshal(map[string]interface{}{"annotations": dae.Spec.Template.ObjectMeta.Annotations}, 6) if err != nil { return true, nil, err } podAnnotations = "\n" + podAnnotations } nameCamel := strcase.ToLowerCamel(name) specMap, podValues, err := pod.ProcessSpec(nameCamel, appMeta, dae.Spec.Template.Spec) if err != nil { return true, nil, err } err = values.Merge(podValues) if err != nil { return true, nil, err } spec, err := yamlformat.Marshal(specMap, 6) if err != nil { return true, nil, err } spec = strings.ReplaceAll(spec, "'", "") return true, &result{ values: values, data: struct { Meta string Selector string PodLabels string PodAnnotations string Spec string }{ Meta: meta, Selector: selector, PodLabels: podLabels, PodAnnotations: podAnnotations, Spec: spec, }, }, nil } type result struct { data struct { Meta string Selector string PodLabels string PodAnnotations string Spec string } values helmify.Values } func (r *result) Filename() string { return "daemonset.yaml" } func (r *result) Values() helmify.Values { return r.values } func (r *result) Write(writer io.Writer) error { return daemonsetTempl.Execute(writer, r.data) } 0707010000006C000081A4000000000000000000000001673B898300000741000000000000000000000000000000000000003900000000helmify-0.4.16/pkg/processor/daemonset/daemonset_test.gopackage daemonset import ( "testing" "github.com/arttor/helmify/pkg/metadata" "github.com/arttor/helmify/internal" "github.com/stretchr/testify/assert" ) const ( strDepl = `apiVersion: apps/v1 kind: DaemonSet metadata: name: fluentd-elasticsearch namespace: kube-system labels: k8s-app: fluentd-logging spec: selector: matchLabels: name: fluentd-elasticsearch template: metadata: labels: name: fluentd-elasticsearch spec: tolerations: # this toleration is to have the daemonset runnable on master nodes # remove it if your masters can't run pods - key: node-role.kubernetes.io/master operator: Exists effect: NoSchedule containers: - name: fluentd-elasticsearch image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2 resources: limits: memory: 200Mi requests: cpu: 100m memory: 200Mi volumeMounts: - name: varlog mountPath: /var/log - name: varlibdockercontainers mountPath: /var/lib/docker/containers readOnly: true terminationGracePeriodSeconds: 30 volumes: - name: varlog hostPath: path: /var/log - name: varlibdockercontainers hostPath: path: /var/lib/docker/containers ` ) func Test_daemonset_Process(t *testing.T) { var testInstance daemonset t.Run("processed", func(t *testing.T) { obj := internal.GenerateObj(strDepl) processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, true, processed) }) t.Run("skipped", func(t *testing.T) { obj := internal.TestNs processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, false, processed) }) } 0707010000006D000081A4000000000000000000000001673B8983000006DC000000000000000000000000000000000000002800000000helmify-0.4.16/pkg/processor/default.gopackage processor import ( "io" "github.com/arttor/helmify/pkg/helmify" yamlformat "github.com/arttor/helmify/pkg/yaml" "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" ) var nsGVK = schema.GroupVersionKind{ Group: "", Version: "v1", Kind: "Namespace", } // Default default processor for unknown resources. func Default() helmify.Processor { return &dft{} } type dft struct{} // Process unknown resource to a helm template. Default processor just templates obj name and adds helm annotations. func (d dft) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstructured) (bool, helmify.Template, error) { if obj.GroupVersionKind() == nsGVK { // Skip namespaces from processing because namespace will be handled by Helm. return true, nil, nil } logrus.WithFields(logrus.Fields{ "ApiVersion": obj.GetAPIVersion(), "Kind": obj.GetKind(), "Name": obj.GetName(), }).Warn("Unsupported resource: using default processor.") name := appMeta.TrimName(obj.GetName()) meta, err := ProcessObjMeta(appMeta, obj) if err != nil { return true, nil, err } delete(obj.Object, "apiVersion") delete(obj.Object, "kind") delete(obj.Object, "metadata") body, err := yamlformat.Marshal(obj.Object, 0) if err != nil { return true, nil, err } return true, &defaultResult{ data: []byte(meta + "\n" + body), name: name, }, nil } type defaultResult struct { data []byte name string } func (r *defaultResult) Filename() string { return r.name + ".yaml" } func (r *defaultResult) Values() helmify.Values { return helmify.Values{} } func (r *defaultResult) Write(writer io.Writer) error { _, err := writer.Write(r.data) return err } 0707010000006E000081A4000000000000000000000001673B898300000473000000000000000000000000000000000000002D00000000helmify-0.4.16/pkg/processor/default_test.gopackage processor import ( "github.com/arttor/helmify/pkg/config" "testing" "github.com/arttor/helmify/internal" "github.com/arttor/helmify/pkg/metadata" "github.com/stretchr/testify/assert" ) const pvcYaml = `apiVersion: v1 kind: PersistentVolumeClaim metadata: name: my-operator-pvc-lim spec: accessModes: - ReadWriteOnce resources: requests: storage: 2Gi storageClassName: cust1-mypool-lim` func Test_dft_Process(t *testing.T) { t.Run("skip namespace", func(t *testing.T) { testMeta := metadata.New(config.Config{ChartName: "chart-name"}) testMeta.Load(internal.TestNs) testProcessor := Default() processed, templ, err := testProcessor.Process(testMeta, internal.TestNs) assert.NoError(t, err) assert.True(t, processed) assert.Nil(t, templ) }) t.Run("process", func(t *testing.T) { obj := internal.GenerateObj(pvcYaml) testMeta := metadata.New(config.Config{ChartName: "chart-name"}) testMeta.Load(obj) testProcessor := Default() processed, templ, err := testProcessor.Process(testMeta, obj) assert.NoError(t, err) assert.True(t, processed) assert.NotNil(t, templ) }) } 0707010000006F000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000002800000000helmify-0.4.16/pkg/processor/deployment07070100000070000081A4000000000000000000000001673B898300001AB0000000000000000000000000000000000000003600000000helmify-0.4.16/pkg/processor/deployment/deployment.gopackage deployment import ( "fmt" "io" "regexp" "strings" "text/template" "github.com/arttor/helmify/pkg/processor/pod" "github.com/arttor/helmify/pkg/helmify" "github.com/arttor/helmify/pkg/processor" yamlformat "github.com/arttor/helmify/pkg/yaml" "github.com/iancoleman/strcase" appsv1 "k8s.io/api/apps/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" ) var deploymentGVC = schema.GroupVersionKind{ Group: "apps", Version: "v1", Kind: "Deployment", } var deploymentTempl, _ = template.New("deployment").Parse( `{{- .Meta }} spec: {{- if .Replicas }} {{ .Replicas }} {{- end }} {{- if .RevisionHistoryLimit }} {{ .RevisionHistoryLimit }} {{- end }} selector: {{ .Selector }} template: metadata: labels: {{ .PodLabels }} {{- .PodAnnotations }} spec: {{ .Spec }}`) const selectorTempl = `%[1]s {{- include "%[2]s.selectorLabels" . | nindent 6 }} %[3]s` // New creates processor for k8s Deployment resource. func New() helmify.Processor { return &deployment{} } type deployment struct{} // Process k8s Deployment object into template. Returns false if not capable of processing given resource type. func (d deployment) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstructured) (bool, helmify.Template, error) { if obj.GroupVersionKind() != deploymentGVC { return false, nil, nil } depl := appsv1.Deployment{} err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &depl) if err != nil { return true, nil, fmt.Errorf("%w: unable to cast to deployment", err) } meta, err := processor.ProcessObjMeta(appMeta, obj) if err != nil { return true, nil, err } values := helmify.Values{} name := appMeta.TrimName(obj.GetName()) replicas, err := processReplicas(name, &depl, &values) if err != nil { return true, nil, err } revisionHistoryLimit, err := processRevisionHistoryLimit(name, &depl, &values) if err != nil { return true, nil, err } matchLabels, err := yamlformat.Marshal(map[string]interface{}{"matchLabels": depl.Spec.Selector.MatchLabels}, 0) if err != nil { return true, nil, err } matchExpr := "" if depl.Spec.Selector.MatchExpressions != nil { matchExpr, err = yamlformat.Marshal(map[string]interface{}{"matchExpressions": depl.Spec.Selector.MatchExpressions}, 0) if err != nil { return true, nil, err } } selector := fmt.Sprintf(selectorTempl, matchLabels, appMeta.ChartName(), matchExpr) selector = strings.Trim(selector, " \n") selector = string(yamlformat.Indent([]byte(selector), 4)) podLabels, err := yamlformat.Marshal(depl.Spec.Template.ObjectMeta.Labels, 8) if err != nil { return true, nil, err } podLabels += fmt.Sprintf("\n {{- include \"%s.selectorLabels\" . | nindent 8 }}", appMeta.ChartName()) podAnnotations := "" if len(depl.Spec.Template.ObjectMeta.Annotations) != 0 { podAnnotations, err = yamlformat.Marshal(map[string]interface{}{"annotations": depl.Spec.Template.ObjectMeta.Annotations}, 6) if err != nil { return true, nil, err } podAnnotations = "\n" + podAnnotations } nameCamel := strcase.ToLowerCamel(name) specMap, podValues, err := pod.ProcessSpec(nameCamel, appMeta, depl.Spec.Template.Spec) if err != nil { return true, nil, err } err = values.Merge(podValues) if err != nil { return true, nil, err } spec, err := yamlformat.Marshal(specMap, 6) if err != nil { return true, nil, err } if appMeta.Config().AddWebhookOption { spec = addWebhookOption(spec) } spec = replaceSingleQuotes(spec) return true, &result{ values: values, data: struct { Meta string Replicas string RevisionHistoryLimit string Selector string PodLabels string PodAnnotations string Spec string }{ Meta: meta, Replicas: replicas, RevisionHistoryLimit: revisionHistoryLimit, Selector: selector, PodLabels: podLabels, PodAnnotations: podAnnotations, Spec: spec, }, }, nil } func replaceSingleQuotes(s string) string { r := regexp.MustCompile(`'({{((.*|.*\n.*))}}.*)'`) return r.ReplaceAllString(s, "${1}") } func addWebhookOption(manifest string) string { webhookOptionHeader := " {{- if .Values.webhook.enabled }}" webhookOptionFooter := " {{- end }}" volumes := ` - name: cert secret: defaultMode: 420 secretName: webhook-server-cert` volumeMounts := ` - mountPath: /tmp/k8s-webhook-server/serving-certs name: cert readOnly: true` manifest = strings.ReplaceAll(manifest, volumes, fmt.Sprintf("%s\n%s\n%s", webhookOptionHeader, volumes, webhookOptionFooter)) manifest = strings.ReplaceAll(manifest, volumeMounts, fmt.Sprintf("%s\n%s\n%s", webhookOptionHeader, volumeMounts, webhookOptionFooter)) re := regexp.MustCompile(` - containerPort: \d+ name: webhook-server protocol: TCP`) manifest = re.ReplaceAllString(manifest, fmt.Sprintf("%s\n%s\n%s", webhookOptionHeader, re.FindString(manifest), webhookOptionFooter)) return manifest } func processReplicas(name string, deployment *appsv1.Deployment, values *helmify.Values) (string, error) { if deployment.Spec.Replicas == nil { return "", nil } replicasTpl, err := values.Add(int64(*deployment.Spec.Replicas), name, "replicas") if err != nil { return "", err } replicas, err := yamlformat.Marshal(map[string]interface{}{"replicas": replicasTpl}, 2) if err != nil { return "", err } replicas = strings.ReplaceAll(replicas, "'", "") return replicas, nil } func processRevisionHistoryLimit(name string, deployment *appsv1.Deployment, values *helmify.Values) (string, error) { if deployment.Spec.RevisionHistoryLimit == nil { return "", nil } revisionHistoryLimitTpl, err := values.Add(int64(*deployment.Spec.RevisionHistoryLimit), name, "revisionHistoryLimit") if err != nil { return "", err } revisionHistoryLimit, err := yamlformat.Marshal(map[string]interface{}{"revisionHistoryLimit": revisionHistoryLimitTpl}, 2) if err != nil { return "", err } revisionHistoryLimit = strings.ReplaceAll(revisionHistoryLimit, "'", "") return revisionHistoryLimit, nil } type result struct { data struct { Meta string Replicas string RevisionHistoryLimit string Selector string PodLabels string PodAnnotations string Spec string } values helmify.Values } func (r *result) Filename() string { return "deployment.yaml" } func (r *result) Values() helmify.Values { return r.values } func (r *result) Write(writer io.Writer) error { return deploymentTempl.Execute(writer, r.data) } 07070100000071000081A4000000000000000000000001673B8983000012E3000000000000000000000000000000000000003B00000000helmify-0.4.16/pkg/processor/deployment/deployment_test.gopackage deployment import ( "testing" "github.com/arttor/helmify/pkg/metadata" "github.com/arttor/helmify/internal" "github.com/stretchr/testify/assert" ) const ( strDepl = `apiVersion: apps/v1 kind: Deployment metadata: labels: control-plane: controller-manager name: my-operator-controller-manager namespace: my-operator-system spec: revisionHistoryLimit: 5 replicas: 1 selector: matchLabels: control-plane: controller-manager template: metadata: labels: control-plane: controller-manager spec: containers: - args: - --secure-listen-address=0.0.0.0:8443 - --upstream=http://127.0.0.1:8080/ - --logtostderr=true - --v=10 image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0 name: kube-rbac-proxy ports: - containerPort: 8443 name: https - args: - --health-probe-bind-address=:8081 - --metrics-bind-address=127.0.0.1:8080 - --leader-elect command: - /manager volumeMounts: - mountPath: /controller_manager_config.yaml name: manager-config subPath: controller_manager_config.yaml - name: secret-volume mountPath: /my.ca - name: sample-pv-storage mountPath: "/usr/share/nginx/html" env: - name: VAR1 valueFrom: secretKeyRef: name: my-operator-secret-vars key: VAR1 - name: VAR2 valueFrom: configMapKeyRef: name: my-operator-configmap-vars key: VAR2 - name: VAR3 valueFrom: fieldRef: fieldPath: metadata.namespace - name: VAR4 valueFrom: resourceFieldRef: resource: limits.cpu - name: VAR5 value: "123" - name: VAR6 valueFrom: fieldRef: fieldPath: metadata.labels['app.kubernetes.io/something'] image: controller:latest livenessProbe: httpGet: path: /healthz port: 8081 initialDelaySeconds: 15 periodSeconds: 20 name: manager readinessProbe: httpGet: path: /readyz port: 8081 initialDelaySeconds: 5 periodSeconds: 10 resources: limits: cpu: 100m memory: 30Mi requests: cpu: 100m memory: 20Mi securityContext: allowPrivilegeEscalation: false securityContext: runAsNonRoot: true serviceAccountName: my-operator-controller-manager terminationGracePeriodSeconds: 10 volumes: - configMap: name: my-operator-manager-config name: manager-config - name: secret-volume secret: secretName: my-operator-secret-ca - name: sample-pv-storage persistentVolumeClaim: claimName: my-sample-pv-claim ` ) func Test_deployment_Process(t *testing.T) { var testInstance deployment t.Run("processed", func(t *testing.T) { obj := internal.GenerateObj(strDepl) processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, true, processed) }) t.Run("skipped", func(t *testing.T) { obj := internal.TestNs processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, false, processed) }) } var singleQuotesTest = []struct { input string expected string }{ { "{{ .Values.x }}", "{{ .Values.x }}", }, { "'{{ .Values.x }}'", "{{ .Values.x }}", }, { "'{{ .Values.x }}:{{ .Values.y }}'", "{{ .Values.x }}:{{ .Values.y }}", }, { "'{{ .Values.x }}:{{ .Values.y \n\t| default .Chart.AppVersion}}'", "{{ .Values.x }}:{{ .Values.y \n\t| default .Chart.AppVersion}}", }, { "echo 'x'", "echo 'x'", }, { "abcd: x.y['x/y']", "abcd: x.y['x/y']", }, { "abcd: x.y[\"'{{}}'\"]", "abcd: x.y[\"{{}}\"]", }, { "image: '{{ .Values.x }}'", "image: {{ .Values.x }}", }, { "'{{ .Values.x }} y'", "{{ .Values.x }} y", }, { "\t\t- mountPath: './x.y'", "\t\t- mountPath: './x.y'", }, { "'{{}}'", "{{}}", }, { "'{{ {nested} }}'", "{{ {nested} }}", }, { "'{{ '{{nested}}' }}'", "{{ '{{nested}}' }}", }, { "'{{ unbalanced }'", "'{{ unbalanced }'", }, { "'{{\nincomplete content'", "'{{\nincomplete content'", }, { "'{{ @#$%^&*() }}'", "{{ @#$%^&*() }}", }, } func Test_replaceSingleQuotes(t *testing.T) { for _, tt := range singleQuotesTest { t.Run(tt.input, func(t *testing.T) { s := replaceSingleQuotes(tt.input) if s != tt.expected { t.Errorf("got %q, want %q", s, tt.expected) } }) } } 07070100000072000081A4000000000000000000000001673B898300000063000000000000000000000000000000000000002400000000helmify-0.4.16/pkg/processor/doc.go// Package processor contains processors converting k8s objects to Helm template package processor 07070100000073000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000002100000000helmify-0.4.16/pkg/processor/job07070100000074000081A4000000000000000000000001673B898300000F7B000000000000000000000000000000000000002900000000helmify-0.4.16/pkg/processor/job/cron.gopackage job import ( "fmt" "github.com/arttor/helmify/pkg/helmify" "github.com/arttor/helmify/pkg/processor" "github.com/arttor/helmify/pkg/processor/pod" yamlformat "github.com/arttor/helmify/pkg/yaml" "github.com/iancoleman/strcase" "io" batchv1 "k8s.io/api/batch/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "strings" "text/template" ) var cronTempl, _ = template.New("cron").Parse( `{{ .Meta }} {{ .Spec }}`) var cronGVC = schema.GroupVersionKind{ Group: "batch", Version: "v1", Kind: "CronJob", } // NewCron creates processor for k8s CronJob resource. func NewCron() helmify.Processor { return &cron{} } type cron struct{} // Process k8s CronJob object into template. Returns false if not capable of processing given resource type. func (p cron) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstructured) (bool, helmify.Template, error) { if obj.GroupVersionKind() != cronGVC { return false, nil, nil } meta, err := processor.ProcessObjMeta(appMeta, obj) if err != nil { return true, nil, err } name := appMeta.TrimName(obj.GetName()) nameCamelCase := strcase.ToLowerCamel(name) jobObj := batchv1.CronJob{} err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &jobObj) if err != nil { return true, nil, fmt.Errorf("%w: unable to cast to Job", err) } spec := jobObj.Spec specMap, exists, err := unstructured.NestedMap(obj.Object, "spec") if err != nil { return true, nil, fmt.Errorf("%w: unable to get job spec", err) } if !exists { return true, nil, fmt.Errorf("no job spec presented") } values := helmify.Values{} // process job spec params: if spec.Schedule != "" { err := templateSpecVal(spec.Schedule, &values, specMap, nameCamelCase, "schedule") if err != nil { return true, nil, err } } if spec.Suspend != nil { err := templateSpecVal(*spec.Suspend, &values, specMap, nameCamelCase, "suspend") if err != nil { return true, nil, err } } if spec.FailedJobsHistoryLimit != nil { err := templateSpecVal(*spec.FailedJobsHistoryLimit, &values, specMap, nameCamelCase, "failedJobsHistoryLimit") if err != nil { return true, nil, err } } if spec.StartingDeadlineSeconds != nil { err := templateSpecVal(*spec.StartingDeadlineSeconds, &values, specMap, nameCamelCase, "startingDeadlineSeconds") if err != nil { return true, nil, err } } if spec.TimeZone != nil { err := templateSpecVal(*spec.TimeZone, &values, specMap, nameCamelCase, "timeZone") if err != nil { return true, nil, err } } if spec.SuccessfulJobsHistoryLimit != nil { err := templateSpecVal(*spec.SuccessfulJobsHistoryLimit, &values, specMap, nameCamelCase, "successfulJobsHistoryLimit") if err != nil { return true, nil, err } } // process job pod template: podSpecMap, podValues, err := pod.ProcessSpec(nameCamelCase, appMeta, jobObj.Spec.JobTemplate.Spec.Template.Spec) if err != nil { return true, nil, err } err = values.Merge(podValues) if err != nil { return true, nil, err } err = unstructured.SetNestedMap(specMap, podSpecMap, "jobTemplate", "spec", "template", "spec") if err != nil { return true, nil, fmt.Errorf("%w: unable to template job spec", err) } specStr, err := yamlformat.Marshal(map[string]interface{}{"spec": specMap}, 0) if err != nil { return true, nil, err } specStr = strings.ReplaceAll(specStr, "'", "") return true, &resultCron{ name: name + ".yaml", data: struct { Meta string Spec string }{Meta: meta, Spec: specStr}, values: values, }, nil } type resultCron struct { name string data struct { Meta string Spec string } values helmify.Values } func (r *resultCron) Filename() string { return r.name } func (r *resultCron) Values() helmify.Values { return r.values } func (r *resultCron) Write(writer io.Writer) error { return cronTempl.Execute(writer, r.data) } 07070100000075000081A4000000000000000000000001673B898300000449000000000000000000000000000000000000002E00000000helmify-0.4.16/pkg/processor/job/cron_test.gopackage job import ( "github.com/arttor/helmify/internal" "github.com/arttor/helmify/pkg/metadata" "github.com/stretchr/testify/assert" "testing" ) const ( strCron = `apiVersion: batch/v1 kind: CronJob metadata: name: cron-job spec: schedule: "* * * * *" jobTemplate: spec: template: spec: containers: - name: hello image: busybox:1.28 imagePullPolicy: IfNotPresent command: - /bin/sh - -c - date; echo Hello from the Kubernetes cluster restartPolicy: OnFailure` ) func Test_Cron_Process(t *testing.T) { var testInstance cron t.Run("processed", func(t *testing.T) { obj := internal.GenerateObj(strCron) processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, true, processed) }) t.Run("skipped", func(t *testing.T) { obj := internal.TestNs processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, false, processed) }) } 07070100000076000081A4000000000000000000000001673B89830000112E000000000000000000000000000000000000002800000000helmify-0.4.16/pkg/processor/job/job.gopackage job import ( "fmt" "github.com/arttor/helmify/pkg/helmify" "github.com/arttor/helmify/pkg/processor" "github.com/arttor/helmify/pkg/processor/pod" yamlformat "github.com/arttor/helmify/pkg/yaml" "github.com/iancoleman/strcase" "io" batchv1 "k8s.io/api/batch/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "strings" "text/template" ) var jobTempl, _ = template.New("job").Parse( `{{ .Meta }} {{ .Spec }}`) var jobGVC = schema.GroupVersionKind{ Group: "batch", Version: "v1", Kind: "Job", } // NewJob creates processor for k8s Job resource. func NewJob() helmify.Processor { return &job{} } type job struct{} // Process k8s Job object into template. Returns false if not capable of processing given resource type. func (p job) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstructured) (bool, helmify.Template, error) { if obj.GroupVersionKind() != jobGVC { return false, nil, nil } meta, err := processor.ProcessObjMeta(appMeta, obj) if err != nil { return true, nil, err } name := appMeta.TrimName(obj.GetName()) nameCamelCase := strcase.ToLowerCamel(name) jobObj := batchv1.Job{} err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &jobObj) if err != nil { return true, nil, fmt.Errorf("%w: unable to cast to Job", err) } spec := jobObj.Spec specMap, exists, err := unstructured.NestedMap(obj.Object, "spec") if err != nil { return true, nil, fmt.Errorf("%w: unable to get job spec", err) } if !exists { return true, nil, fmt.Errorf("no job spec presented") } values := helmify.Values{} // process job spec params: if spec.BackoffLimit != nil { err := templateSpecVal(*spec.BackoffLimit, &values, specMap, nameCamelCase, "backoffLimit") if err != nil { return true, nil, err } } if spec.ActiveDeadlineSeconds != nil { err := templateSpecVal(*spec.ActiveDeadlineSeconds, &values, specMap, nameCamelCase, "activeDeadlineSeconds") if err != nil { return true, nil, err } } if spec.Completions != nil { err := templateSpecVal(*spec.Completions, &values, specMap, nameCamelCase, "completions") if err != nil { return true, nil, err } } if spec.Parallelism != nil { err := templateSpecVal(*spec.Parallelism, &values, specMap, nameCamelCase, "parallelism") if err != nil { return true, nil, err } } if spec.Suspend != nil { err := templateSpecVal(*spec.Suspend, &values, specMap, nameCamelCase, "suspend") if err != nil { return true, nil, err } } if spec.ActiveDeadlineSeconds != nil { err := templateSpecVal(*spec.ActiveDeadlineSeconds, &values, specMap, nameCamelCase, "activeDeadlineSeconds") if err != nil { return true, nil, err } } // process job pod template: podSpecMap, podValues, err := pod.ProcessSpec(nameCamelCase, appMeta, jobObj.Spec.Template.Spec) if err != nil { return true, nil, err } err = values.Merge(podValues) if err != nil { return true, nil, err } err = unstructured.SetNestedMap(specMap, podSpecMap, "template", "spec") if err != nil { return true, nil, fmt.Errorf("%w: unable to template job spec", err) } specStr, err := yamlformat.Marshal(map[string]interface{}{"spec": specMap}, 0) if err != nil { return true, nil, err } specStr = strings.ReplaceAll(specStr, "'", "") return true, &result{ name: name + ".yaml", data: struct { Meta string Spec string }{Meta: meta, Spec: specStr}, values: values, }, nil } type result struct { name string data struct { Meta string Spec string } values helmify.Values } func (r *result) Filename() string { return r.name } func (r *result) Values() helmify.Values { return r.values } func (r *result) Write(writer io.Writer) error { return jobTempl.Execute(writer, r.data) } func templateSpecVal(val any, values *helmify.Values, specMap map[string]interface{}, objName string, fieldName ...string) error { valName := []string{objName} valName = append(valName, fieldName...) templatedVal, err := values.Add(val, valName...) if err != nil { return fmt.Errorf("%w: unable to set %q to values", err, strings.Join(valName, ".")) } err = unstructured.SetNestedField(specMap, templatedVal, fieldName...) if err != nil { return fmt.Errorf("%w: unable to template job %q", err, strings.Join(valName, ".")) } return nil } 07070100000077000081A4000000000000000000000001673B8983000003A0000000000000000000000000000000000000002D00000000helmify-0.4.16/pkg/processor/job/job_test.gopackage job import ( "github.com/arttor/helmify/internal" "github.com/arttor/helmify/pkg/metadata" "github.com/stretchr/testify/assert" "testing" ) const ( strJob = `apiVersion: batch/v1 kind: Job metadata: name: batch-job spec: template: spec: containers: - name: pi image: perl:5.34.0 command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"] restartPolicy: Never backoffLimit: 4` ) func Test_configMap_Process(t *testing.T) { var testInstance job t.Run("processed", func(t *testing.T) { obj := internal.GenerateObj(strJob) processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, true, processed) }) t.Run("skipped", func(t *testing.T) { obj := internal.TestNs processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, false, processed) }) } 07070100000078000081A4000000000000000000000001673B898300000B88000000000000000000000000000000000000002500000000helmify-0.4.16/pkg/processor/meta.gopackage processor import ( "fmt" "strings" "github.com/iancoleman/strcase" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "github.com/arttor/helmify/pkg/helmify" yamlformat "github.com/arttor/helmify/pkg/yaml" ) const metaTemplate = `apiVersion: %[1]s kind: %[2]s metadata: name: %[3]s %[7]s labels: %[5]s {{- include "%[4]s.labels" . | nindent 4 }} %[6]s` const annotationsTemplate = ` annotations: {{- toYaml .Values.%[1]s.%[2]s.annotations | nindent 4 }}` type MetaOpt interface { apply(*options) } type options struct { values helmify.Values annotations bool } type annotationsOption struct { values helmify.Values } func (a annotationsOption) apply(opts *options) { opts.annotations = true opts.values = a.values } func WithAnnotations(values helmify.Values) MetaOpt { return annotationsOption{ values: values, } } // ProcessObjMeta - returns object apiVersion, kind and metadata as helm template. func ProcessObjMeta(appMeta helmify.AppMetadata, obj *unstructured.Unstructured, opts ...MetaOpt) (string, error) { options := &options{} for _, opt := range opts { opt.apply(options) } var err error var labels, annotations, namespace string if len(obj.GetLabels()) != 0 { l := obj.GetLabels() // provided by Helm delete(l, "app.kubernetes.io/name") delete(l, "app.kubernetes.io/instance") delete(l, "app.kubernetes.io/version") delete(l, "app.kubernetes.io/managed-by") delete(l, "helm.sh/chart") // Since we delete labels above, it is possible that at this point there are no more labels. if len(l) > 0 { labels, err = yamlformat.Marshal(l, 4) if err != nil { return "", err } } } if len(obj.GetAnnotations()) != 0 { annotations, err = yamlformat.Marshal(map[string]interface{}{"annotations": obj.GetAnnotations()}, 2) if err != nil { return "", err } } if (obj.GetNamespace() != "") && (appMeta.Config().PreserveNs) { namespace, err = yamlformat.Marshal(map[string]interface{}{"namespace": obj.GetNamespace()}, 2) if err != nil { return "", err } } templatedName := appMeta.TemplatedName(obj.GetName()) apiVersion, kind := obj.GetObjectKind().GroupVersionKind().ToAPIVersionAndKind() var metaStr string if options.values != nil && options.annotations { name := strcase.ToLowerCamel(appMeta.TrimName(obj.GetName())) kind := strcase.ToLowerCamel(kind) valuesAnnotations := make(map[string]interface{}) for k, v := range obj.GetAnnotations() { valuesAnnotations[k] = v } err = unstructured.SetNestedField(options.values, valuesAnnotations, name, kind, "annotations") if err != nil { return "", err } annotations = fmt.Sprintf(annotationsTemplate, name, kind) } metaStr = fmt.Sprintf(metaTemplate, apiVersion, kind, templatedName, appMeta.ChartName(), labels, annotations, namespace) metaStr = strings.Trim(metaStr, " \n") metaStr = strings.ReplaceAll(metaStr, "\n\n", "\n") return metaStr, nil } 07070100000079000081A4000000000000000000000001673B898300000202000000000000000000000000000000000000002A00000000helmify-0.4.16/pkg/processor/meta_test.gopackage processor import ( "github.com/arttor/helmify/pkg/config" "testing" "github.com/arttor/helmify/internal" "github.com/arttor/helmify/pkg/metadata" "github.com/stretchr/testify/assert" ) func TestProcessObjMeta(t *testing.T) { testMeta := metadata.New(config.Config{ChartName: "chart-name"}) testMeta.Load(internal.TestNs) res, err := ProcessObjMeta(testMeta, internal.TestNs) assert.NoError(t, err) assert.Contains(t, res, "chart-name.labels") assert.Contains(t, res, "chart-name.fullname") } 0707010000007A000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000002100000000helmify-0.4.16/pkg/processor/pod0707010000007B000081A4000000000000000000000001673B89830000245B000000000000000000000000000000000000002800000000helmify-0.4.16/pkg/processor/pod/pod.gopackage pod import ( "fmt" "strings" "github.com/arttor/helmify/pkg/cluster" "github.com/arttor/helmify/pkg/helmify" securityContext "github.com/arttor/helmify/pkg/processor/security-context" "github.com/iancoleman/strcase" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" ) const imagePullPolicyTemplate = "{{ .Values.%[1]s.%[2]s.imagePullPolicy }}" const envValue = "{{ quote .Values.%[1]s.%[2]s.%[3]s.%[4]s }}" func ProcessSpec(objName string, appMeta helmify.AppMetadata, spec corev1.PodSpec) (map[string]interface{}, helmify.Values, error) { values, err := processPodSpec(objName, appMeta, &spec) if err != nil { return nil, nil, err } // replace PVC to templated name for i := 0; i < len(spec.Volumes); i++ { vol := spec.Volumes[i] if vol.PersistentVolumeClaim == nil { continue } tempPVCName := appMeta.TemplatedName(vol.PersistentVolumeClaim.ClaimName) spec.Volumes[i].PersistentVolumeClaim.ClaimName = tempPVCName } // replace container resources with template to values. specMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&spec) if err != nil { return nil, nil, fmt.Errorf("%w: unable to convert podSpec to map", err) } specMap, values, err = processNestedContainers(specMap, objName, values, "containers") if err != nil { return nil, nil, err } specMap, values, err = processNestedContainers(specMap, objName, values, "initContainers") if err != nil { return nil, nil, err } if appMeta.Config().ImagePullSecrets { if _, defined := specMap["imagePullSecrets"]; !defined { specMap["imagePullSecrets"] = "{{ .Values.imagePullSecrets | default list | toJson }}" values["imagePullSecrets"] = []string{} } } err = securityContext.ProcessContainerSecurityContext(objName, specMap, &values) if err != nil { return nil, nil, err } if spec.SecurityContext != nil { securityContextMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&spec.SecurityContext) if err != nil { return nil, nil, err } if len(securityContextMap) > 0 { err = unstructured.SetNestedField(specMap, fmt.Sprintf(`{{- toYaml .Values.%[1]s.podSecurityContext | nindent 8 }}`, objName), "securityContext") if err != nil { return nil, nil, err } err = unstructured.SetNestedField(values, securityContextMap, objName, "podSecurityContext") if err != nil { return nil, nil, fmt.Errorf("%w: unable to set deployment value field", err) } } } // process nodeSelector if presented: if spec.NodeSelector != nil { err = unstructured.SetNestedField(specMap, fmt.Sprintf(`{{- toYaml .Values.%s.nodeSelector | nindent 8 }}`, objName), "nodeSelector") if err != nil { return nil, nil, err } err = unstructured.SetNestedStringMap(values, spec.NodeSelector, objName, "nodeSelector") if err != nil { return nil, nil, err } } return specMap, values, nil } func processNestedContainers(specMap map[string]interface{}, objName string, values map[string]interface{}, containerKey string) (map[string]interface{}, map[string]interface{}, error) { containers, _, err := unstructured.NestedSlice(specMap, containerKey) if err != nil { return nil, nil, err } if len(containers) > 0 { containers, values, err = processContainers(objName, values, containerKey, containers) if err != nil { return nil, nil, err } err = unstructured.SetNestedSlice(specMap, containers, containerKey) if err != nil { return nil, nil, err } } return specMap, values, nil } func processContainers(objName string, values helmify.Values, containerType string, containers []interface{}) ([]interface{}, helmify.Values, error) { for i := range containers { containerName := strcase.ToLowerCamel((containers[i].(map[string]interface{})["name"]).(string)) res, exists, err := unstructured.NestedMap(values, objName, containerName, "resources") if err != nil { return nil, nil, err } if exists && len(res) > 0 { err = unstructured.SetNestedField(containers[i].(map[string]interface{}), fmt.Sprintf(`{{- toYaml .Values.%s.%s.resources | nindent 10 }}`, objName, containerName), "resources") if err != nil { return nil, nil, err } } args, exists, err := unstructured.NestedStringSlice(containers[i].(map[string]interface{}), "args") if err != nil { return nil, nil, err } if exists && len(args) > 0 { err = unstructured.SetNestedField(containers[i].(map[string]interface{}), fmt.Sprintf(`{{- toYaml .Values.%[1]s.%[2]s.args | nindent 8 }}`, objName, containerName), "args") if err != nil { return nil, nil, err } err = unstructured.SetNestedStringSlice(values, args, objName, containerName, "args") if err != nil { return nil, nil, fmt.Errorf("%w: unable to set deployment value field", err) } } } return containers, values, nil } func processPodSpec(name string, appMeta helmify.AppMetadata, pod *corev1.PodSpec) (helmify.Values, error) { values := helmify.Values{} for i, c := range pod.Containers { processed, err := processPodContainer(name, appMeta, c, &values) if err != nil { return nil, err } pod.Containers[i] = processed } for i, c := range pod.InitContainers { processed, err := processPodContainer(name, appMeta, c, &values) if err != nil { return nil, err } pod.InitContainers[i] = processed } for _, v := range pod.Volumes { if v.ConfigMap != nil { v.ConfigMap.Name = appMeta.TemplatedName(v.ConfigMap.Name) } if v.Secret != nil { v.Secret.SecretName = appMeta.TemplatedName(v.Secret.SecretName) } } pod.ServiceAccountName = appMeta.TemplatedName(pod.ServiceAccountName) for i, s := range pod.ImagePullSecrets { pod.ImagePullSecrets[i].Name = appMeta.TemplatedName(s.Name) } return values, nil } func processPodContainer(name string, appMeta helmify.AppMetadata, c corev1.Container, values *helmify.Values) (corev1.Container, error) { index := strings.LastIndex(c.Image, ":") if strings.Contains(c.Image, "@") && strings.Count(c.Image, ":") >= 2 { last := strings.LastIndex(c.Image, ":") index = strings.LastIndex(c.Image[:last], ":") } if index < 0 { return c, fmt.Errorf("wrong image format: %q", c.Image) } repo, tag := c.Image[:index], c.Image[index+1:] containerName := strcase.ToLowerCamel(c.Name) c.Image = fmt.Sprintf("{{ .Values.%[1]s.%[2]s.image.repository }}:{{ .Values.%[1]s.%[2]s.image.tag | default .Chart.AppVersion }}", name, containerName) err := unstructured.SetNestedField(*values, repo, name, containerName, "image", "repository") if err != nil { return c, fmt.Errorf("%w: unable to set deployment value field", err) } err = unstructured.SetNestedField(*values, tag, name, containerName, "image", "tag") if err != nil { return c, fmt.Errorf("%w: unable to set deployment value field", err) } c, err = processEnv(name, appMeta, c, values) if err != nil { return c, err } for _, e := range c.EnvFrom { if e.SecretRef != nil { e.SecretRef.Name = appMeta.TemplatedName(e.SecretRef.Name) } if e.ConfigMapRef != nil { e.ConfigMapRef.Name = appMeta.TemplatedName(e.ConfigMapRef.Name) } } c.Env = append(c.Env, corev1.EnvVar{ Name: cluster.DomainEnv, Value: fmt.Sprintf("{{ quote .Values.%s }}", cluster.DomainKey), }) for k, v := range c.Resources.Requests { err = unstructured.SetNestedField(*values, v.ToUnstructured(), name, containerName, "resources", "requests", k.String()) if err != nil { return c, fmt.Errorf("%w: unable to set container resources value", err) } } for k, v := range c.Resources.Limits { err = unstructured.SetNestedField(*values, v.ToUnstructured(), name, containerName, "resources", "limits", k.String()) if err != nil { return c, fmt.Errorf("%w: unable to set container resources value", err) } } if c.ImagePullPolicy != "" { err = unstructured.SetNestedField(*values, string(c.ImagePullPolicy), name, containerName, "imagePullPolicy") if err != nil { return c, fmt.Errorf("%w: unable to set container imagePullPolicy", err) } c.ImagePullPolicy = corev1.PullPolicy(fmt.Sprintf(imagePullPolicyTemplate, name, containerName)) } return c, nil } func processEnv(name string, appMeta helmify.AppMetadata, c corev1.Container, values *helmify.Values) (corev1.Container, error) { containerName := strcase.ToLowerCamel(c.Name) for i := 0; i < len(c.Env); i++ { if c.Env[i].ValueFrom != nil { switch { case c.Env[i].ValueFrom.SecretKeyRef != nil: c.Env[i].ValueFrom.SecretKeyRef.Name = appMeta.TemplatedName(c.Env[i].ValueFrom.SecretKeyRef.Name) case c.Env[i].ValueFrom.ConfigMapKeyRef != nil: c.Env[i].ValueFrom.ConfigMapKeyRef.Name = appMeta.TemplatedName(c.Env[i].ValueFrom.ConfigMapKeyRef.Name) case c.Env[i].ValueFrom.FieldRef != nil, c.Env[i].ValueFrom.ResourceFieldRef != nil: // nothing to change here, keep the original value } continue } err := unstructured.SetNestedField(*values, c.Env[i].Value, name, containerName, "env", strcase.ToLowerCamel(strings.ToLower(c.Env[i].Name))) if err != nil { return c, fmt.Errorf("%w: unable to set deployment value field", err) } c.Env[i].Value = fmt.Sprintf(envValue, name, containerName, "env", strcase.ToLowerCamel(strings.ToLower(c.Env[i].Name))) } return c, nil } 0707010000007C000081A4000000000000000000000001673B89830000228E000000000000000000000000000000000000002D00000000helmify-0.4.16/pkg/processor/pod/pod_test.gopackage pod import ( "testing" "github.com/arttor/helmify/pkg/helmify" "github.com/arttor/helmify/pkg/metadata" appsv1 "k8s.io/api/apps/v1" "k8s.io/apimachinery/pkg/runtime" "github.com/arttor/helmify/internal" "github.com/stretchr/testify/assert" ) const ( strDeployment = ` apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment labels: app: nginx spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.14.2 args: - --test - --arg ports: - containerPort: 80 ` strDeploymentWithTagAndDigest = ` apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment labels: app: nginx spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.14.2@sha256:cb5c1bddd1b5665e1867a7fa1b5fa843a47ee433bbb75d4293888b71def53229 ports: - containerPort: 80 ` strDeploymentWithNoArgs = ` apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment labels: app: nginx spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.14.2 ports: - containerPort: 80 ` strDeploymentWithPort = ` apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment labels: app: nginx spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: localhost:6001/my_project:latest ports: - containerPort: 80 ` strDeploymentWithPodSecurityContext = ` apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment labels: app: nginx spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: localhost:6001/my_project:latest securityContext: fsGroup: 20000 runAsGroup: 30000 runAsNonRoot: true runAsUser: 65532 ` ) func Test_pod_Process(t *testing.T) { t.Run("deployment with args", func(t *testing.T) { var deploy appsv1.Deployment obj := internal.GenerateObj(strDeployment) err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &deploy) specMap, tmpl, err := ProcessSpec("nginx", &metadata.Service{}, deploy.Spec.Template.Spec) assert.NoError(t, err) assert.Equal(t, map[string]interface{}{ "containers": []interface{}{ map[string]interface{}{ "args": "{{- toYaml .Values.nginx.nginx.args | nindent 8 }}", "env": []interface{}{ map[string]interface{}{ "name": "KUBERNETES_CLUSTER_DOMAIN", "value": "{{ quote .Values.kubernetesClusterDomain }}", }, }, "image": "{{ .Values.nginx.nginx.image.repository }}:{{ .Values.nginx.nginx.image.tag | default .Chart.AppVersion }}", "name": "nginx", "ports": []interface{}{ map[string]interface{}{ "containerPort": int64(80), }, }, "resources": map[string]interface{}{}, }, }, }, specMap) assert.Equal(t, helmify.Values{ "nginx": map[string]interface{}{ "nginx": map[string]interface{}{ "image": map[string]interface{}{ "repository": "nginx", "tag": "1.14.2", }, "args": []interface{}{ "--test", "--arg", }, }, }, }, tmpl) }) t.Run("deployment with no args", func(t *testing.T) { var deploy appsv1.Deployment obj := internal.GenerateObj(strDeploymentWithNoArgs) err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &deploy) specMap, tmpl, err := ProcessSpec("nginx", &metadata.Service{}, deploy.Spec.Template.Spec) assert.NoError(t, err) assert.Equal(t, map[string]interface{}{ "containers": []interface{}{ map[string]interface{}{ "env": []interface{}{ map[string]interface{}{ "name": "KUBERNETES_CLUSTER_DOMAIN", "value": "{{ quote .Values.kubernetesClusterDomain }}", }, }, "image": "{{ .Values.nginx.nginx.image.repository }}:{{ .Values.nginx.nginx.image.tag | default .Chart.AppVersion }}", "name": "nginx", "ports": []interface{}{ map[string]interface{}{ "containerPort": int64(80), }, }, "resources": map[string]interface{}{}, }, }, }, specMap) assert.Equal(t, helmify.Values{ "nginx": map[string]interface{}{ "nginx": map[string]interface{}{ "image": map[string]interface{}{ "repository": "nginx", "tag": "1.14.2", }, }, }, }, tmpl) }) t.Run("deployment with image tag and digest", func(t *testing.T) { var deploy appsv1.Deployment obj := internal.GenerateObj(strDeploymentWithTagAndDigest) err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &deploy) specMap, tmpl, err := ProcessSpec("nginx", &metadata.Service{}, deploy.Spec.Template.Spec) assert.NoError(t, err) assert.Equal(t, map[string]interface{}{ "containers": []interface{}{ map[string]interface{}{ "env": []interface{}{ map[string]interface{}{ "name": "KUBERNETES_CLUSTER_DOMAIN", "value": "{{ quote .Values.kubernetesClusterDomain }}", }, }, "image": "{{ .Values.nginx.nginx.image.repository }}:{{ .Values.nginx.nginx.image.tag | default .Chart.AppVersion }}", "name": "nginx", "ports": []interface{}{ map[string]interface{}{ "containerPort": int64(80), }, }, "resources": map[string]interface{}{}, }, }, }, specMap) assert.Equal(t, helmify.Values{ "nginx": map[string]interface{}{ "nginx": map[string]interface{}{ "image": map[string]interface{}{ "repository": "nginx", "tag": "1.14.2@sha256:cb5c1bddd1b5665e1867a7fa1b5fa843a47ee433bbb75d4293888b71def53229", }, }, }, }, tmpl) }) t.Run("deployment with image tag and port", func(t *testing.T) { var deploy appsv1.Deployment obj := internal.GenerateObj(strDeploymentWithPort) err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &deploy) specMap, tmpl, err := ProcessSpec("nginx", &metadata.Service{}, deploy.Spec.Template.Spec) assert.NoError(t, err) assert.Equal(t, map[string]interface{}{ "containers": []interface{}{ map[string]interface{}{ "env": []interface{}{ map[string]interface{}{ "name": "KUBERNETES_CLUSTER_DOMAIN", "value": "{{ quote .Values.kubernetesClusterDomain }}", }, }, "image": "{{ .Values.nginx.nginx.image.repository }}:{{ .Values.nginx.nginx.image.tag | default .Chart.AppVersion }}", "name": "nginx", "ports": []interface{}{ map[string]interface{}{ "containerPort": int64(80), }, }, "resources": map[string]interface{}{}, }, }, }, specMap) assert.Equal(t, helmify.Values{ "nginx": map[string]interface{}{ "nginx": map[string]interface{}{ "image": map[string]interface{}{ "repository": "localhost:6001/my_project", "tag": "latest", }, }, }, }, tmpl) }) t.Run("deployment with securityContext", func(t *testing.T) { var deploy appsv1.Deployment obj := internal.GenerateObj(strDeploymentWithPodSecurityContext) err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &deploy) specMap, tmpl, err := ProcessSpec("nginx", &metadata.Service{}, deploy.Spec.Template.Spec) assert.NoError(t, err) assert.Equal(t, map[string]interface{}{ "containers": []interface{}{ map[string]interface{}{ "env": []interface{}{ map[string]interface{}{ "name": "KUBERNETES_CLUSTER_DOMAIN", "value": "{{ quote .Values.kubernetesClusterDomain }}", }, }, "image": "{{ .Values.nginx.nginx.image.repository }}:{{ .Values.nginx.nginx.image.tag | default .Chart.AppVersion }}", "name": "nginx", "resources": map[string]interface{}{}, }, }, "securityContext": "{{- toYaml .Values.nginx.podSecurityContext | nindent 8 }}", }, specMap) assert.Equal(t, helmify.Values{ "nginx": map[string]interface{}{ "podSecurityContext": map[string]interface{}{ "fsGroup": int64(20000), "runAsGroup": int64(30000), "runAsNonRoot": true, "runAsUser": int64(65532), }, "nginx": map[string]interface{}{ "image": map[string]interface{}{ "repository": "localhost:6001/my_project", "tag": "latest", }, }, }, }, tmpl) }) } 0707010000007D000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000003100000000helmify-0.4.16/pkg/processor/poddisruptionbudget0707010000007E000081A4000000000000000000000001673B8983000009D6000000000000000000000000000000000000003800000000helmify-0.4.16/pkg/processor/poddisruptionbudget/pdb.gopackage poddisruptionbudget import ( "bytes" "fmt" "io" "github.com/arttor/helmify/pkg/processor" "github.com/arttor/helmify/pkg/helmify" yamlformat "github.com/arttor/helmify/pkg/yaml" "github.com/iancoleman/strcase" policyv1 "k8s.io/api/policy/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "sigs.k8s.io/yaml" ) const ( pdbTempSpec = ` spec: minAvailable: {{ .Values.%[1]s.minAvailable }} maxUnavailable: {{ .Values.%[1]s.maxUnavailable }} selector: %[2]s {{- include "%[3]s.selectorLabels" . | nindent 6 }}` ) var pdbGVC = schema.GroupVersionKind{ Group: "policy", Version: "v1", Kind: "PodDisruptionBudget", } // New creates processor for k8s Service resource. func New() helmify.Processor { return &pdb{} } type pdb struct{} // Process k8s Service object into template. Returns false if not capable of processing given resource type. func (r pdb) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstructured) (bool, helmify.Template, error) { if obj.GroupVersionKind() != pdbGVC { return false, nil, nil } pdb := policyv1.PodDisruptionBudget{} err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &pdb) if err != nil { return true, nil, fmt.Errorf("%w: unable to cast to pdb", err) } spec := pdb.Spec values := helmify.Values{} meta, err := processor.ProcessObjMeta(appMeta, obj) if err != nil { return true, nil, err } name := appMeta.TrimName(obj.GetName()) nameCamel := strcase.ToLowerCamel(name) selector, _ := yaml.Marshal(pdb.Spec.Selector) selector = yamlformat.Indent(selector, 4) selector = bytes.TrimRight(selector, "\n ") if spec.MaxUnavailable != nil { _, err := values.Add(spec.MaxUnavailable.IntValue(), nameCamel, "maxUnavailable") if err != nil { return true, nil, err } } if spec.MinAvailable != nil { _, err := values.Add(spec.MinAvailable.IntValue(), nameCamel, "minAvailable") if err != nil { return true, nil, err } } res := meta + fmt.Sprintf(pdbTempSpec, nameCamel, selector, appMeta.ChartName()) return true, &result{ name: name, data: res, values: values, }, nil } type result struct { name string data string values helmify.Values } func (r *result) Filename() string { return r.name + ".yaml" } func (r *result) Values() helmify.Values { return r.values } func (r *result) Write(writer io.Writer) error { _, err := writer.Write([]byte(r.data)) return err } 0707010000007F000081A4000000000000000000000001673B8983000003CE000000000000000000000000000000000000003D00000000helmify-0.4.16/pkg/processor/poddisruptionbudget/pdb_test.gopackage poddisruptionbudget import ( "os" "testing" "github.com/arttor/helmify/pkg/metadata" "github.com/arttor/helmify/internal" "github.com/stretchr/testify/assert" ) const pdbYaml = `apiVersion: policy/v1 kind: PodDisruptionBudget metadata: labels: control-plane: controller-manager name: my-operator-controller-manager-pdb namespace: my-operator-system spec: minAvailable: 2 selector: matchLabels: control-plane: controller-manager` func Test_pdb_Process(t *testing.T) { var testInstance pdb t.Run("processed", func(t *testing.T) { obj := internal.GenerateObj(pdbYaml) processed, tt, err := testInstance.Process(&metadata.Service{}, obj) _ = tt.Write(os.Stdout) assert.NoError(t, err) assert.Equal(t, true, processed) }) t.Run("skipped", func(t *testing.T) { obj := internal.TestNs processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, false, processed) }) } 07070100000080000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000002200000000helmify-0.4.16/pkg/processor/rbac07070100000081000081A4000000000000000000000001673B8983000009FB000000000000000000000000000000000000003800000000helmify-0.4.16/pkg/processor/rbac/clusterrolebinding.gopackage rbac import ( "fmt" "io" "strings" "text/template" "github.com/arttor/helmify/pkg/processor" "github.com/arttor/helmify/pkg/helmify" yamlformat "github.com/arttor/helmify/pkg/yaml" rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" ) var clusterRoleBindingTempl, _ = template.New("clusterRoleBinding").Parse( `{{ .Meta }} {{ .RoleRef }} {{ .Subjects }}`) var clusterRoleBindingGVC = schema.GroupVersionKind{ Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "ClusterRoleBinding", } // ClusterRoleBinding creates processor for k8s ClusterRoleBinding resource. func ClusterRoleBinding() helmify.Processor { return &clusterRoleBinding{} } type clusterRoleBinding struct{} // Process k8s ClusterRoleBinding object into template. Returns false if not capable of processing given resource type. func (r clusterRoleBinding) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstructured) (bool, helmify.Template, error) { if obj.GroupVersionKind() != clusterRoleBindingGVC { return false, nil, nil } rb := rbacv1.ClusterRoleBinding{} err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &rb) if err != nil { return true, nil, fmt.Errorf("%w: unable to cast to RoleBinding", err) } meta, err := processor.ProcessObjMeta(appMeta, obj) if err != nil { return true, nil, err } rb.RoleRef.Name = appMeta.TemplatedName(rb.RoleRef.Name) roleRef, err := yamlformat.Marshal(map[string]interface{}{"roleRef": &rb.RoleRef}, 0) if err != nil { return true, nil, err } for i, s := range rb.Subjects { s.Namespace = "{{ .Release.Namespace }}" s.Name = appMeta.TemplatedName(s.Name) rb.Subjects[i] = s } subjects, err := yamlformat.Marshal(map[string]interface{}{"subjects": &rb.Subjects}, 0) if err != nil { return true, nil, err } return true, &crbResult{ name: appMeta.TrimName(obj.GetName()), data: struct { Meta string RoleRef string Subjects string }{ Meta: meta, RoleRef: roleRef, Subjects: subjects, }, }, nil } type crbResult struct { name string data struct { Meta string RoleRef string Subjects string } } func (r *crbResult) Filename() string { return strings.TrimSuffix(r.name, "-rolebinding") + "-rbac.yaml" } func (r *crbResult) Values() helmify.Values { return helmify.Values{} } func (r *crbResult) Write(writer io.Writer) error { return clusterRoleBindingTempl.Execute(writer, r.data) } 07070100000082000081A4000000000000000000000001673B898300000408000000000000000000000000000000000000003D00000000helmify-0.4.16/pkg/processor/rbac/clusterrolebinding_test.gopackage rbac import ( "testing" "github.com/arttor/helmify/pkg/metadata" "github.com/arttor/helmify/internal" "github.com/stretchr/testify/assert" ) const clusterRoleBindingYaml = `apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: my-operator-manager-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: my-operator-manager-role subjects: - kind: ServiceAccount name: my-operator-controller-manager namespace: my-operator-system` func Test_clusterRoleBinding_Process(t *testing.T) { var testInstance clusterRoleBinding t.Run("processed", func(t *testing.T) { obj := internal.GenerateObj(clusterRoleBindingYaml) processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, true, processed) }) t.Run("skipped", func(t *testing.T) { obj := internal.TestNs processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, false, processed) }) } 07070100000083000081A4000000000000000000000001673B898300000A65000000000000000000000000000000000000002A00000000helmify-0.4.16/pkg/processor/rbac/role.gopackage rbac import ( "fmt" "io" "strings" "text/template" "github.com/arttor/helmify/pkg/processor" "github.com/arttor/helmify/pkg/helmify" yamlformat "github.com/arttor/helmify/pkg/yaml" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" ) var roleTempl, _ = template.New("clusterRole").Parse( `{{ .Meta }} {{- if .AggregationRule }} {{ .AggregationRule }} {{- end}} {{ .Rules }}`) var clusterRoleGVC = schema.GroupVersionKind{ Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "ClusterRole", } var roleGVC = schema.GroupVersionKind{ Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "Role", } // Role creates processor for k8s Role and ClusterRole resources. func Role() helmify.Processor { return &role{} } type role struct{} // Process k8s ClusterRole object into template. Returns false if not capable of processing given resource type. func (r role) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstructured) (bool, helmify.Template, error) { var aggregationRule string if obj.GroupVersionKind() != clusterRoleGVC && obj.GroupVersionKind() != roleGVC { return false, nil, nil } meta, err := processor.ProcessObjMeta(appMeta, obj) if err != nil { return true, nil, err } if existingAggRule := obj.Object["aggregationRule"]; existingAggRule != nil { if obj.GroupVersionKind().Kind == "Role" { return true, nil, fmt.Errorf("unable to set aggregationRule to the kind Role in %q: unsupported", obj.GetName()) } if existingAggRule.(map[string]interface{})["clusterRoleSelectors"] != nil { aggRuleMap := map[string]interface{}{"aggregationRule": existingAggRule} aggregationRule, err = yamlformat.Marshal(aggRuleMap, 0) if err != nil { return true, nil, err } } } rules, err := yamlformat.Marshal(map[string]interface{}{"rules": obj.Object["rules"]}, 0) if err != nil { return true, nil, err } return true, &crResult{ name: appMeta.TrimName(obj.GetName()), data: struct { Meta string AggregationRule string Rules string }{Meta: meta, AggregationRule: aggregationRule, Rules: rules}, }, nil } type crResult struct { name string data struct { Meta string AggregationRule string Rules string } } func (r *crResult) Filename() string { return strings.TrimSuffix(r.name, "-role") + "-rbac.yaml" } func (r *crResult) GVK() schema.GroupVersionKind { return clusterRoleGVC } func (r *crResult) Values() helmify.Values { return helmify.Values{} } func (r *crResult) Write(writer io.Writer) error { return roleTempl.Execute(writer, r.data) } 07070100000084000081A4000000000000000000000001673B8983000003E9000000000000000000000000000000000000002F00000000helmify-0.4.16/pkg/processor/rbac/role_test.gopackage rbac import ( "testing" "github.com/arttor/helmify/pkg/metadata" "github.com/arttor/helmify/internal" "github.com/stretchr/testify/assert" ) const clusterRoleYaml = `apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: creationTimestamp: null name: my-operator-manager-role aggregationRule: clusterRoleSelectors: - matchExpressions: - key: my.operator.dev/release operator: Exists rules: - apiGroups: - "" resources: - pods verbs: - get - list` func Test_clusterRole_Process(t *testing.T) { var testInstance role t.Run("processed", func(t *testing.T) { obj := internal.GenerateObj(clusterRoleYaml) processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, true, processed) }) t.Run("skipped", func(t *testing.T) { obj := internal.TestNs processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, false, processed) }) } 07070100000085000081A4000000000000000000000001673B898300000998000000000000000000000000000000000000003100000000helmify-0.4.16/pkg/processor/rbac/rolebinding.gopackage rbac import ( "fmt" "io" "strings" "text/template" "github.com/arttor/helmify/pkg/processor" "github.com/arttor/helmify/pkg/helmify" yamlformat "github.com/arttor/helmify/pkg/yaml" rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" ) var roleBindingTempl, _ = template.New("roleBinding").Parse( `{{- .Meta }} {{ .RoleRef }} {{ .Subjects }}`) var roleBindingGVC = schema.GroupVersionKind{ Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "RoleBinding", } // RoleBinding creates processor for k8s RoleBinding resource. func RoleBinding() helmify.Processor { return &roleBinding{} } type roleBinding struct{} // Process k8s RoleBinding object into helm template. Returns false if not capable of processing given resource type. func (r roleBinding) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstructured) (bool, helmify.Template, error) { if obj.GroupVersionKind() != roleBindingGVC { return false, nil, nil } rb := rbacv1.RoleBinding{} err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &rb) if err != nil { return true, nil, fmt.Errorf("%w: unable to cast to RoleBinding", err) } meta, err := processor.ProcessObjMeta(appMeta, obj) if err != nil { return true, nil, err } rb.RoleRef.Name = appMeta.TemplatedName(rb.RoleRef.Name) roleRef, err := yamlformat.Marshal(map[string]interface{}{"roleRef": &rb.RoleRef}, 0) if err != nil { return true, nil, err } for i, s := range rb.Subjects { s.Namespace = "{{ .Release.Namespace }}" s.Name = appMeta.TemplatedName(s.Name) rb.Subjects[i] = s } subjects, err := yamlformat.Marshal(map[string]interface{}{"subjects": &rb.Subjects}, 0) if err != nil { return true, nil, err } return true, &rbResult{ name: appMeta.TrimName(obj.GetName()), data: struct { Meta string RoleRef string Subjects string }{ Meta: meta, RoleRef: roleRef, Subjects: subjects, }, }, nil } type rbResult struct { name string data struct { Meta string RoleRef string Subjects string } } func (r *rbResult) Filename() string { return strings.TrimSuffix(r.name, "-rolebinding") + "-rbac.yaml" } func (r *rbResult) Values() helmify.Values { return helmify.Values{} } func (r *rbResult) Write(writer io.Writer) error { return roleBindingTempl.Execute(writer, r.data) } 07070100000086000081A4000000000000000000000001673B89830000040E000000000000000000000000000000000000003600000000helmify-0.4.16/pkg/processor/rbac/rolebinding_test.gopackage rbac import ( "testing" "github.com/arttor/helmify/pkg/metadata" "github.com/arttor/helmify/internal" "github.com/stretchr/testify/assert" ) const roleBindingYaml = `apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: my-operator-leader-election-rolebinding namespace: my-operator-system roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: my-operator-leader-election-role subjects: - kind: ServiceAccount name: my-operator-controller-manager namespace: my-operator-system` func Test_roleBinding_Process(t *testing.T) { var testInstance roleBinding t.Run("processed", func(t *testing.T) { obj := internal.GenerateObj(roleBindingYaml) processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, true, processed) }) t.Run("skipped", func(t *testing.T) { obj := internal.TestNs processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, false, processed) }) } 07070100000087000081A4000000000000000000000001673B89830000054B000000000000000000000000000000000000003400000000helmify-0.4.16/pkg/processor/rbac/serviceaccount.gopackage rbac import ( "github.com/arttor/helmify/pkg/helmify" "github.com/arttor/helmify/pkg/processor" "io" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" ) var serviceAccountGVC = schema.GroupVersionKind{ Group: "", Version: "v1", Kind: "ServiceAccount", } // ServiceAccount creates processor for k8s ServiceAccount resource. func ServiceAccount() helmify.Processor { return &serviceAccount{} } type serviceAccount struct{} // Process k8s ServiceAccount object into helm template. Returns false if not capable of processing given resource type. func (sa serviceAccount) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstructured) (bool, helmify.Template, error) { if obj.GroupVersionKind() != serviceAccountGVC { return false, nil, nil } values := helmify.Values{} meta, err := processor.ProcessObjMeta(appMeta, obj, processor.WithAnnotations(values)) if err != nil { return true, nil, err } return true, &saResult{ data: []byte(meta), values: values, }, nil } type saResult struct { data []byte values helmify.Values } func (r *saResult) Filename() string { return "serviceaccount.yaml" } func (r *saResult) Values() helmify.Values { return r.values } func (r *saResult) Write(writer io.Writer) error { _, err := writer.Write(r.data) return err } 07070100000088000081A4000000000000000000000001673B89830000032D000000000000000000000000000000000000003900000000helmify-0.4.16/pkg/processor/rbac/serviceaccount_test.gopackage rbac import ( "testing" "github.com/arttor/helmify/pkg/metadata" "github.com/arttor/helmify/internal" "github.com/stretchr/testify/assert" ) const serviceAccountYaml = `apiVersion: v1 kind: ServiceAccount metadata: name: my-operator-controller-manager namespace: my-operator-system` func Test_serviceAccount_Process(t *testing.T) { var testInstance serviceAccount t.Run("processed", func(t *testing.T) { obj := internal.GenerateObj(serviceAccountYaml) processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, true, processed) }) t.Run("skipped", func(t *testing.T) { obj := internal.TestNs processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, false, processed) }) } 07070100000089000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000002400000000helmify-0.4.16/pkg/processor/secret0707010000008A000081A4000000000000000000000001673B898300000EC9000000000000000000000000000000000000002E00000000helmify-0.4.16/pkg/processor/secret/secret.gopackage secret import ( "fmt" "github.com/arttor/helmify/pkg/format" "io" "strings" "text/template" "github.com/arttor/helmify/pkg/processor" "github.com/arttor/helmify/pkg/helmify" yamlformat "github.com/arttor/helmify/pkg/yaml" "github.com/iancoleman/strcase" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" ) var secretTempl, _ = template.New("secret").Parse( `{{ .Meta }} {{- if .Data }} {{ .Data }} {{- end }} {{- if .StringData }} {{ .StringData }} {{- end }} {{- if .Type }} {{ .Type }} {{- end }}`) var configMapGVC = schema.GroupVersionKind{ Group: "", Version: "v1", Kind: "Secret", } // New creates processor for k8s Secret resource. func New() helmify.Processor { return &secret{} } type secret struct{} // Process k8s Secret object into template. Returns false if not capable of processing given resource type. func (d secret) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstructured) (bool, helmify.Template, error) { if obj.GroupVersionKind() != configMapGVC { return false, nil, nil } sec := corev1.Secret{} err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &sec) if err != nil { return true, nil, fmt.Errorf("%w: unable to cast to secret", err) } meta, err := processor.ProcessObjMeta(appMeta, obj) if err != nil { return true, nil, err } name := appMeta.TrimName(obj.GetName()) nameCamelCase := strcase.ToLowerCamel(name) secretType := string(sec.Type) if secretType != "" { secretType, err = yamlformat.Marshal(map[string]interface{}{"type": secretType}, 0) if err != nil { return true, nil, err } } values := helmify.Values{} var data, stringData string templatedData := map[string]string{} for key := range sec.Data { keyCamelCase := strcase.ToLowerCamel(key) if key == strings.ToUpper(key) { keyCamelCase = strcase.ToLowerCamel(strings.ToLower(key)) } templatedName, err := values.AddSecret(true, nameCamelCase, keyCamelCase) if err != nil { return true, nil, fmt.Errorf("%w: unable add secret to values", err) } templatedData[key] = templatedName } if len(templatedData) != 0 { data, err = yamlformat.Marshal(map[string]interface{}{"data": templatedData}, 0) if err != nil { return true, nil, err } data = strings.ReplaceAll(data, "'", "") data = format.FixUnterminatedQuotes(data) } templatedData = map[string]string{} for key := range sec.StringData { keyCamelCase := strcase.ToLowerCamel(key) if key == strings.ToUpper(key) { keyCamelCase = strcase.ToLowerCamel(strings.ToLower(key)) } templatedName, err := values.AddSecret(false, nameCamelCase, keyCamelCase) if err != nil { return true, nil, fmt.Errorf("%w: unable add secret to values", err) } templatedData[key] = templatedName } if len(templatedData) != 0 { stringData, err = yamlformat.Marshal(map[string]interface{}{"stringData": templatedData}, 0) if err != nil { return true, nil, err } stringData = strings.ReplaceAll(stringData, "'", "") stringData = format.FixUnterminatedQuotes(stringData) } return true, &result{ name: name + ".yaml", data: struct { Type string Meta string Data string StringData string }{Type: secretType, Meta: meta, Data: data, StringData: stringData}, values: values, }, nil } type result struct { name string data struct { Type string Meta string Data string StringData string } values helmify.Values } func (r *result) Filename() string { return r.name } func (r *result) Values() helmify.Values { return r.values } func (r *result) Write(writer io.Writer) error { return secretTempl.Execute(writer, r.data) } 0707010000008B000081A4000000000000000000000001673B89830000036F000000000000000000000000000000000000003300000000helmify-0.4.16/pkg/processor/secret/secret_test.gopackage secret import ( "testing" "github.com/arttor/helmify/pkg/metadata" "github.com/arttor/helmify/internal" "github.com/stretchr/testify/assert" ) const secretYaml = `apiVersion: v1 data: VAR1: bXlfc2VjcmV0X3Zhcl8x VAR2: bXlfc2VjcmV0X3Zhcl8y stringData: VAR3: string secret kind: Secret metadata: name: my-operator-secret-vars namespace: my-operator-system type: opaque` func Test_secret_Process(t *testing.T) { var testInstance secret t.Run("processed", func(t *testing.T) { obj := internal.GenerateObj(secretYaml) processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, true, processed) }) t.Run("skipped", func(t *testing.T) { obj := internal.TestNs processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, false, processed) }) } 0707010000008C000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000002E00000000helmify-0.4.16/pkg/processor/security-context0707010000008D000081A4000000000000000000000001673B898300000821000000000000000000000000000000000000004C00000000helmify-0.4.16/pkg/processor/security-context/container_security_context.gopackage security_context import ( "fmt" "github.com/arttor/helmify/pkg/helmify" "github.com/iancoleman/strcase" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) const ( sc = "securityContext" cscValueName = "containerSecurityContext" helmTemplate = "{{- toYaml .Values.%[1]s.%[2]s.containerSecurityContext | nindent 10 }}" ) // ProcessContainerSecurityContext adds 'securityContext' to the podSpec in specMap, if it doesn't have one already defined. func ProcessContainerSecurityContext(nameCamel string, specMap map[string]interface{}, values *helmify.Values) error { err := processSecurityContext(nameCamel, "containers", specMap, values) if err != nil { return err } err = processSecurityContext(nameCamel, "initContainers", specMap, values) if err != nil { return err } return nil } func processSecurityContext(nameCamel string, containerType string, specMap map[string]interface{}, values *helmify.Values) error { if containers, defined := specMap[containerType]; defined { for _, container := range containers.([]interface{}) { castedContainer := container.(map[string]interface{}) containerName := strcase.ToLowerCamel(castedContainer["name"].(string)) if _, defined2 := castedContainer["securityContext"]; defined2 { err := setSecContextValue(nameCamel, containerName, castedContainer, values) if err != nil { return err } } } err := unstructured.SetNestedField(specMap, containers, containerType) if err != nil { return err } } return nil } func setSecContextValue(resourceName string, containerName string, castedContainer map[string]interface{}, values *helmify.Values) error { if castedContainer["securityContext"] != nil { err := unstructured.SetNestedField(*values, castedContainer["securityContext"], resourceName, containerName, cscValueName) if err != nil { return err } valueString := fmt.Sprintf(helmTemplate, resourceName, containerName) err = unstructured.SetNestedField(castedContainer, valueString, sc) if err != nil { return err } } return nil } 0707010000008E000081A4000000000000000000000001673B898300000E27000000000000000000000000000000000000005100000000helmify-0.4.16/pkg/processor/security-context/container_security_context_test.gopackage security_context import ( "testing" "github.com/arttor/helmify/pkg/helmify" "github.com/stretchr/testify/assert" ) func TestProcessContainerSecurityContext(t *testing.T) { type args struct { nameCamel string specMap map[string]interface{} values *helmify.Values } tests := []struct { name string args args want *helmify.Values }{ { name: "test with empty specMap", args: args{ nameCamel: "someResourceName", specMap: map[string]interface{}{}, values: &helmify.Values{}, }, want: &helmify.Values{}, }, { name: "test with single container", args: args{ nameCamel: "someResourceName", specMap: map[string]interface{}{ "containers": []interface{}{ map[string]interface{}{ "name": "SomeContainerName", "securityContext": map[string]interface{}{ "privileged": true, }, }, }, }, values: &helmify.Values{}, }, want: &helmify.Values{ "someResourceName": map[string]interface{}{ "someContainerName": map[string]interface{}{ "containerSecurityContext": map[string]interface{}{ "privileged": true, }, }, }, }, }, { name: "test with multiple containers", args: args{ nameCamel: "someResourceName", specMap: map[string]interface{}{ "containers": []interface{}{ map[string]interface{}{ "name": "FirstContainer", "securityContext": map[string]interface{}{ "privileged": true, }, }, map[string]interface{}{ "name": "SecondContainer", "securityContext": map[string]interface{}{ "allowPrivilegeEscalation": true, }, }, }, }, values: &helmify.Values{}, }, want: &helmify.Values{ "someResourceName": map[string]interface{}{ "firstContainer": map[string]interface{}{ "containerSecurityContext": map[string]interface{}{ "privileged": true, }, }, "secondContainer": map[string]interface{}{ "containerSecurityContext": map[string]interface{}{ "allowPrivilegeEscalation": true, }, }, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ProcessContainerSecurityContext(tt.args.nameCamel, tt.args.specMap, tt.args.values) assert.Equal(t, tt.want, tt.args.values) }) } } func Test_setSecContextValue(t *testing.T) { type args struct { resourceName string containerName string castedContainer map[string]interface{} values *helmify.Values fieldName string useRenderedHelmTemplate bool } tests := []struct { name string args args want *helmify.Values }{ { name: "simple test with single container and single value", args: args{ resourceName: "someResource", containerName: "someContainer", castedContainer: map[string]interface{}{ "securityContext": map[string]interface{}{ "someField": "someValue", }, }, values: &helmify.Values{}, fieldName: "someField", useRenderedHelmTemplate: false, }, want: &helmify.Values{ "someResource": map[string]interface{}{ "someContainer": map[string]interface{}{ "containerSecurityContext": map[string]interface{}{ "someField": "someValue", }, }, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { setSecContextValue(tt.args.resourceName, tt.args.containerName, tt.args.castedContainer, tt.args.values) assert.Equal(t, tt.want, tt.args.values) }) } } 0707010000008F000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000002500000000helmify-0.4.16/pkg/processor/service07070100000090000081A4000000000000000000000001673B898300000A3B000000000000000000000000000000000000003000000000helmify-0.4.16/pkg/processor/service/ingress.gopackage service import ( "fmt" "github.com/arttor/helmify/pkg/helmify" "github.com/arttor/helmify/pkg/processor" yamlformat "github.com/arttor/helmify/pkg/yaml" "io" networkingv1 "k8s.io/api/networking/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "text/template" ) var ingressTempl, _ = template.New("ingress").Parse( `{{ .Meta }} {{ .Spec }}`) var ingressGVC = schema.GroupVersionKind{ Group: "networking.k8s.io", Version: "v1", Kind: "Ingress", } // NewIngress creates processor for k8s Ingress resource. func NewIngress() helmify.Processor { return &ingress{} } type ingress struct{} // Process k8s Service object into template. Returns false if not capable of processing given resource type. func (r ingress) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstructured) (bool, helmify.Template, error) { if obj.GroupVersionKind() != ingressGVC { return false, nil, nil } ing := networkingv1.Ingress{} err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &ing) if err != nil { return true, nil, fmt.Errorf("%w: unable to cast to ingress", err) } meta, err := processor.ProcessObjMeta(appMeta, obj) if err != nil { return true, nil, err } name := appMeta.TrimName(obj.GetName()) processIngressSpec(appMeta, &ing.Spec) spec, err := yamlformat.Marshal(map[string]interface{}{"spec": &ing.Spec}, 0) if err != nil { return true, nil, err } return true, &ingressResult{ name: name + ".yaml", data: struct { Meta string Spec string }{Meta: meta, Spec: spec}, }, nil } func processIngressSpec(appMeta helmify.AppMetadata, ing *networkingv1.IngressSpec) { if ing.DefaultBackend != nil && ing.DefaultBackend.Service != nil { ing.DefaultBackend.Service.Name = appMeta.TemplatedName(ing.DefaultBackend.Service.Name) } for i := range ing.Rules { if ing.Rules[i].IngressRuleValue.HTTP != nil { for j := range ing.Rules[i].IngressRuleValue.HTTP.Paths { if ing.Rules[i].IngressRuleValue.HTTP.Paths[j].Backend.Service != nil { ing.Rules[i].IngressRuleValue.HTTP.Paths[j].Backend.Service.Name = appMeta.TemplatedName(ing.Rules[i].IngressRuleValue.HTTP.Paths[j].Backend.Service.Name) } } } } } type ingressResult struct { name string data struct { Meta string Spec string } } func (r *ingressResult) Filename() string { return r.name } func (r *ingressResult) Values() helmify.Values { return helmify.Values{} } func (r *ingressResult) Write(writer io.Writer) error { return ingressTempl.Execute(writer, r.data) } 07070100000091000081A4000000000000000000000001673B898300000417000000000000000000000000000000000000003500000000helmify-0.4.16/pkg/processor/service/ingress_test.gopackage service import ( "testing" "github.com/arttor/helmify/pkg/metadata" "github.com/arttor/helmify/internal" "github.com/stretchr/testify/assert" ) const ingressYaml = `apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: myapp-ingress annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: rules: - http: paths: - path: /testpath pathType: Prefix backend: service: name: myapp-service port: number: 8443` func Test_ingress_Process(t *testing.T) { var testInstance ingress t.Run("processed", func(t *testing.T) { obj := internal.GenerateObj(ingressYaml) processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, true, processed) }) t.Run("skipped", func(t *testing.T) { obj := internal.TestNs processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, false, processed) }) } 07070100000092000081A4000000000000000000000001673B898300000F45000000000000000000000000000000000000003000000000helmify-0.4.16/pkg/processor/service/service.gopackage service import ( "bytes" "fmt" "io" "strings" "github.com/arttor/helmify/pkg/processor" "github.com/arttor/helmify/pkg/helmify" yamlformat "github.com/arttor/helmify/pkg/yaml" "github.com/iancoleman/strcase" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/intstr" "sigs.k8s.io/yaml" ) const ( svcTempSpec = ` spec: type: {{ .Values.%[1]s.type }} selector: %[2]s {{- include "%[3]s.selectorLabels" . | nindent 4 }} ports: {{- .Values.%[1]s.ports | toYaml | nindent 2 }}` ) const ( lbSourceRangesTempSpec = ` loadBalancerSourceRanges: {{- .Values.%[1]s.loadBalancerSourceRanges | toYaml | nindent 2 }}` ) var svcGVC = schema.GroupVersionKind{ Group: "", Version: "v1", Kind: "Service", } // New creates processor for k8s Service resource. func New() helmify.Processor { return &svc{} } type svc struct{} // Process k8s Service object into template. Returns false if not capable of processing given resource type. func (r svc) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstructured) (bool, helmify.Template, error) { if obj.GroupVersionKind() != svcGVC { return false, nil, nil } service := corev1.Service{} err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &service) if err != nil { return true, nil, fmt.Errorf("%w: unable to cast to service", err) } meta, err := processor.ProcessObjMeta(appMeta, obj) if err != nil { return true, nil, err } name := appMeta.TrimName(obj.GetName()) shortName := strings.TrimPrefix(name, "controller-manager-") shortNameCamel := strcase.ToLowerCamel(shortName) selector, _ := yaml.Marshal(service.Spec.Selector) selector = yamlformat.Indent(selector, 4) selector = bytes.TrimRight(selector, "\n ") values := helmify.Values{} svcType := service.Spec.Type if svcType == "" { svcType = corev1.ServiceTypeClusterIP } _ = unstructured.SetNestedField(values, string(svcType), shortNameCamel, "type") ports := make([]interface{}, len(service.Spec.Ports)) for i, p := range service.Spec.Ports { pMap := map[string]interface{}{ "port": int64(p.Port), } if p.Name != "" { pMap["name"] = p.Name } if p.NodePort != 0 { pMap["nodePort"] = int64(p.NodePort) } if p.Protocol != "" { pMap["protocol"] = string(p.Protocol) } if p.TargetPort.Type == intstr.Int { pMap["targetPort"] = int64(p.TargetPort.IntVal) } else { pMap["targetPort"] = p.TargetPort.StrVal } ports[i] = pMap } _ = unstructured.SetNestedSlice(values, ports, shortNameCamel, "ports") res := meta + fmt.Sprintf(svcTempSpec, shortNameCamel, selector, appMeta.ChartName()) res += parseLoadBalancerSourceRanges(values, service, shortNameCamel) if shortNameCamel == "webhookService" && appMeta.Config().AddWebhookOption { res = fmt.Sprintf("{{- if .Values.webhook.enabled }}\n%s\n{{- end }}", res) } return true, &result{ name: shortName, data: res, values: values, }, nil } func parseLoadBalancerSourceRanges(values helmify.Values, service corev1.Service, shortNameCamel string) string { if len(service.Spec.LoadBalancerSourceRanges) < 1 { return "" } lbSourceRanges := make([]interface{}, len(service.Spec.LoadBalancerSourceRanges)) for i, ip := range service.Spec.LoadBalancerSourceRanges { lbSourceRanges[i] = ip } _ = unstructured.SetNestedSlice(values, lbSourceRanges, shortNameCamel, "loadBalancerSourceRanges") return fmt.Sprintf(lbSourceRangesTempSpec, shortNameCamel) } type result struct { name string data string values helmify.Values } func (r *result) Filename() string { return r.name + ".yaml" } func (r *result) Values() helmify.Values { return r.values } func (r *result) Write(writer io.Writer) error { _, err := writer.Write([]byte(r.data)) return err } 07070100000093000081A4000000000000000000000001673B8983000003B3000000000000000000000000000000000000003500000000helmify-0.4.16/pkg/processor/service/service_test.gopackage service import ( "testing" "github.com/arttor/helmify/pkg/metadata" "github.com/arttor/helmify/internal" "github.com/stretchr/testify/assert" ) const svcYaml = `apiVersion: v1 kind: Service metadata: labels: control-plane: controller-manager name: my-operator-controller-manager-metrics-service namespace: my-operator-system spec: ports: - name: https port: 8443 targetPort: https selector: control-plane: controller-manager` func Test_svc_Process(t *testing.T) { var testInstance svc t.Run("processed", func(t *testing.T) { obj := internal.GenerateObj(svcYaml) processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, true, processed) }) t.Run("skipped", func(t *testing.T) { obj := internal.TestNs processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, false, processed) }) } 07070100000094000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000002900000000helmify-0.4.16/pkg/processor/statefulset07070100000095000081A4000000000000000000000001673B8983000010CE000000000000000000000000000000000000003800000000helmify-0.4.16/pkg/processor/statefulset/statefulset.gopackage statefulset import ( "fmt" "github.com/arttor/helmify/pkg/processor/pod" "io" "strings" "text/template" "github.com/arttor/helmify/pkg/helmify" "github.com/arttor/helmify/pkg/processor" yamlformat "github.com/arttor/helmify/pkg/yaml" "github.com/iancoleman/strcase" appsv1 "k8s.io/api/apps/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" ) var statefulsetGVC = schema.GroupVersionKind{ Group: "apps", Version: "v1", Kind: "StatefulSet", } var statefulsetTempl, _ = template.New("statefulset").Parse( `{{- .Meta }} spec: {{ .Spec }}`) // New creates processor for k8s StatefulSet resource. func New() helmify.Processor { return &statefulset{} } type statefulset struct{} // Process k8s StatefulSet object into template. Returns false if not capable of processing given resource type. func (d statefulset) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstructured) (bool, helmify.Template, error) { if obj.GroupVersionKind() != statefulsetGVC { return false, nil, nil } ss := appsv1.StatefulSet{} err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &ss) if err != nil { return true, nil, fmt.Errorf("%w: unable to cast to StatefulSet", err) } meta, err := processor.ProcessObjMeta(appMeta, obj) if err != nil { return true, nil, err } ssSpec := ss.Spec ssSpecMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&ssSpec) if err != nil { return true, nil, err } delete((ssSpecMap["template"].(map[string]interface{}))["metadata"].(map[string]interface{}), "creationTimestamp") values := helmify.Values{} name := appMeta.TrimName(obj.GetName()) nameCamel := strcase.ToLowerCamel(name) if ssSpec.ServiceName != "" { servName := appMeta.TemplatedName(ssSpec.ServiceName) ssSpecMap["serviceName"] = servName } if ssSpec.Replicas != nil { repl, err := values.Add(*ssSpec.Replicas, nameCamel, "replicas") if err != nil { return true, nil, err } ssSpecMap["replicas"] = repl } for i, claim := range ssSpec.VolumeClaimTemplates { volName := claim.ObjectMeta.Name delete(((ssSpecMap["volumeClaimTemplates"].([]interface{}))[i]).(map[string]interface{}), "status") if claim.Spec.StorageClassName != nil { scName := appMeta.TemplatedName(*claim.Spec.StorageClassName) err = unstructured.SetNestedField(((ssSpecMap["volumeClaimTemplates"].([]interface{}))[i]).(map[string]interface{}), scName, "spec", "storageClassName") if err != nil { return true, nil, err } } if claim.Spec.VolumeName != "" { vName := appMeta.TemplatedName(claim.Spec.VolumeName) err = unstructured.SetNestedField(((ssSpecMap["volumeClaimTemplates"].([]interface{}))[i]).(map[string]interface{}), vName, "spec", "volumeName") if err != nil { return true, nil, err } } resMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&claim.Spec.Resources) if err != nil { return true, nil, err } resName, err := values.AddYaml(resMap, 8, true, nameCamel, "volumeClaims", volName) if err != nil { return true, nil, err } err = unstructured.SetNestedField(((ssSpecMap["volumeClaimTemplates"].([]interface{}))[i]).(map[string]interface{}), resName, "spec", "resources") if err != nil { return true, nil, err } } // process pod spec: podSpecMap, podValues, err := pod.ProcessSpec(nameCamel, appMeta, ssSpec.Template.Spec) if err != nil { return true, nil, err } err = values.Merge(podValues) if err != nil { return true, nil, err } err = unstructured.SetNestedMap(ssSpecMap, podSpecMap, "template", "spec") if err != nil { return true, nil, err } spec, err := yamlformat.Marshal(ssSpecMap, 2) if err != nil { return true, nil, err } spec = strings.ReplaceAll(spec, "'", "") return true, &result{ values: values, data: struct { Meta string Spec string }{ Meta: meta, Spec: spec, }, }, nil } type result struct { data struct { Meta string Spec string } values helmify.Values } func (r *result) Filename() string { return "statefulset.yaml" } func (r *result) Values() helmify.Values { return r.values } func (r *result) Write(writer io.Writer) error { return statefulsetTempl.Execute(writer, r.data) } 07070100000096000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000002500000000helmify-0.4.16/pkg/processor/storage07070100000097000081A4000000000000000000000001673B898300000CA3000000000000000000000000000000000000002C00000000helmify-0.4.16/pkg/processor/storage/pvc.gopackage storage import ( "fmt" "github.com/arttor/helmify/pkg/helmify" "github.com/arttor/helmify/pkg/processor" yamlformat "github.com/arttor/helmify/pkg/yaml" "github.com/iancoleman/strcase" "io" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "strings" "text/template" ) var pvcTempl, _ = template.New("pvc").Parse( `{{ .Meta }} {{ .Spec }}`) var pvcGVC = schema.GroupVersionKind{ Group: "", Version: "v1", Kind: "PersistentVolumeClaim", } // New creates processor for k8s PVC resource. func New() helmify.Processor { return &pvc{} } type pvc struct{} // Process k8s PVC object into template. Returns false if not capable of processing given resource type. func (p pvc) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstructured) (bool, helmify.Template, error) { if obj.GroupVersionKind() != pvcGVC { return false, nil, nil } meta, err := processor.ProcessObjMeta(appMeta, obj) if err != nil { return true, nil, err } name := appMeta.TrimName(obj.GetName()) nameCamelCase := strcase.ToLowerCamel(name) values := helmify.Values{} claim := corev1.PersistentVolumeClaim{} err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &claim) if err != nil { return true, nil, fmt.Errorf("%w: unable to cast to PVC", err) } // template storage class name if claim.Spec.StorageClassName != nil { templatedSC, err := values.Add(*claim.Spec.StorageClassName, "pvc", nameCamelCase, "storageClass") if err != nil { return true, nil, err } claim.Spec.StorageClassName = &templatedSC } // template resources specMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&claim.Spec) if err != nil { return true, nil, err } storageReq, ok, _ := unstructured.NestedString(specMap, "resources", "requests", "storage") if ok { templatedStorageReq, err := values.Add(storageReq, "pvc", nameCamelCase, "storageRequest") if err != nil { return true, nil, err } err = unstructured.SetNestedField(specMap, templatedStorageReq, "resources", "requests", "storage") if err != nil { return true, nil, err } } storageLim, ok, _ := unstructured.NestedString(specMap, "resources", "limits", "storage") if ok { templatedStorageLim, err := values.Add(storageLim, "pvc", nameCamelCase, "storageLimit") if err != nil { return true, nil, err } err = unstructured.SetNestedField(specMap, templatedStorageLim, "resources", "limits", "storage") if err != nil { return true, nil, err } } spec, err := yamlformat.Marshal(map[string]interface{}{"spec": specMap}, 0) if err != nil { return true, nil, err } spec = strings.ReplaceAll(spec, "'", "") return true, &result{ name: name + ".yaml", data: struct { Meta string Spec string }{Meta: meta, Spec: spec}, values: values, }, nil } type result struct { name string data struct { Meta string Spec string } values helmify.Values } func (r *result) Filename() string { return r.name } func (r *result) Values() helmify.Values { return r.values } func (r *result) Write(writer io.Writer) error { return pvcTempl.Execute(writer, r.data) } 07070100000098000081A4000000000000000000000001673B89830000036B000000000000000000000000000000000000003100000000helmify-0.4.16/pkg/processor/storage/pvc_test.gopackage storage import ( "testing" "github.com/arttor/helmify/pkg/metadata" "github.com/arttor/helmify/internal" "github.com/stretchr/testify/assert" ) const pvcYaml = `apiVersion: v1 kind: PersistentVolumeClaim metadata: name: task-pv-claim spec: storageClassName: manual accessModes: - ReadWriteOnce resources: requests: storage: 3Gi limits: storage: 5Gi` func Test_PVC_Process(t *testing.T) { var testInstance pvc t.Run("processed", func(t *testing.T) { obj := internal.GenerateObj(pvcYaml) processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, true, processed) }) t.Run("skipped", func(t *testing.T) { obj := internal.TestNs processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, false, processed) }) } 07070100000099000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000002500000000helmify-0.4.16/pkg/processor/webhook0707010000009A000081A4000000000000000000000001673B898300000E0D000000000000000000000000000000000000002D00000000helmify-0.4.16/pkg/processor/webhook/cert.gopackage webhook import ( "bytes" "fmt" "io" "strings" "github.com/arttor/helmify/pkg/cluster" "github.com/arttor/helmify/pkg/helmify" yamlformat "github.com/arttor/helmify/pkg/yaml" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" "sigs.k8s.io/yaml" ) const ( WebhookHeader = `{{- if .Values.webhook.enabled }}` WebhookFooter = `{{- end }}` certTempl = `apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: {{ include "%[1]s.fullname" . }}-%[2]s labels: {{- include "%[1]s.labels" . | nindent 4 }} spec: %[3]s` certTemplWithAnno = `apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: {{ include "%[1]s.fullname" . }}-%[2]s annotations: "helm.sh/hook": post-install,post-upgrade "helm.sh/hook-weight": "2" labels: {{- include "%[1]s.labels" . | nindent 4 }} spec: %[3]s` ) var certGVC = schema.GroupVersionKind{ Group: "cert-manager.io", Version: "v1", Kind: "Certificate", } // Certificate creates processor for k8s Certificate resource. func Certificate() helmify.Processor { return &cert{} } type cert struct{} // Process k8s Certificate object into template. Returns false if not capable of processing given resource type. func (c cert) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstructured) (bool, helmify.Template, error) { if obj.GroupVersionKind() != certGVC { return false, nil, nil } name := appMeta.TrimName(obj.GetName()) dnsNames, _, err := unstructured.NestedSlice(obj.Object, "spec", "dnsNames") if err != nil { return true, nil, fmt.Errorf("%w: unable get cert dnsNames", err) } processedDnsNames := []interface{}{} for _, dnsName := range dnsNames { dns := dnsName.(string) templatedDns := appMeta.TemplatedString(dns) processedDns := strings.ReplaceAll(templatedDns, appMeta.Namespace(), "{{ .Release.Namespace }}") processedDns = strings.ReplaceAll(processedDns, cluster.DefaultDomain, fmt.Sprintf("{{ .Values.%s }}", cluster.DomainKey)) processedDnsNames = append(processedDnsNames, processedDns) } err = unstructured.SetNestedSlice(obj.Object, processedDnsNames, "spec", "dnsNames") if err != nil { return true, nil, fmt.Errorf("%w: unable set cert dnsNames", err) } issName, _, err := unstructured.NestedString(obj.Object, "spec", "issuerRef", "name") if err != nil { return true, nil, fmt.Errorf("%w: unable get cert issuerRef", err) } issName = appMeta.TemplatedName(issName) err = unstructured.SetNestedField(obj.Object, issName, "spec", "issuerRef", "name") if err != nil { return true, nil, fmt.Errorf("%w: unable set cert issuerRef", err) } spec, _ := yaml.Marshal(obj.Object["spec"]) spec = yamlformat.Indent(spec, 2) spec = bytes.TrimRight(spec, "\n ") tmpl := "" if appMeta.Config().CertManagerAsSubchart { tmpl = certTemplWithAnno } else { tmpl = certTempl } values := helmify.Values{} if appMeta.Config().AddWebhookOption { // Add webhook.enabled value to values.yaml _, _ = values.Add(true, "webhook", "enabled") tmpl = fmt.Sprintf("%s\n%s\n%s", WebhookHeader, tmpl, WebhookFooter) } res := fmt.Sprintf(tmpl, appMeta.ChartName(), name, string(spec)) return true, &certResult{ name: name, data: []byte(res), values: values, }, nil } type certResult struct { name string data []byte values helmify.Values } func (r *certResult) Filename() string { return r.name + ".yaml" } func (r *certResult) Values() helmify.Values { return r.values } func (r *certResult) Write(writer io.Writer) error { _, err := writer.Write(r.data) return err } 0707010000009B000081A4000000000000000000000001673B898300000405000000000000000000000000000000000000003200000000helmify-0.4.16/pkg/processor/webhook/cert_test.gopackage webhook import ( "testing" "github.com/arttor/helmify/pkg/metadata" "github.com/arttor/helmify/internal" "github.com/stretchr/testify/assert" ) const certYaml = `apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: my-operator-serving-cert namespace: my-operator-system spec: dnsNames: - my-operator-webhook-service.my-operator-system.svc - my-operator-webhook-service.my-operator-system.svc.cluster.local issuerRef: kind: Issuer name: my-operator-selfsigned-issuer secretName: webhook-server-cert` func Test_cert_Process(t *testing.T) { var testInstance cert t.Run("processed", func(t *testing.T) { obj := internal.GenerateObj(certYaml) processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, true, processed) }) t.Run("skipped", func(t *testing.T) { obj := internal.TestNs processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, false, processed) }) } 0707010000009C000081A4000000000000000000000001673B8983000008C3000000000000000000000000000000000000002F00000000helmify-0.4.16/pkg/processor/webhook/issuer.gopackage webhook import ( "bytes" "fmt" "io" "github.com/arttor/helmify/pkg/helmify" yamlformat "github.com/arttor/helmify/pkg/yaml" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" "sigs.k8s.io/yaml" ) const ( issuerTempl = `apiVersion: cert-manager.io/v1 kind: Issuer metadata: name: {{ include "%[1]s.fullname" . }}-%[2]s labels: {{- include "%[1]s.labels" . | nindent 4 }} spec: %[3]s` issuerTemplWithAnno = `apiVersion: cert-manager.io/v1 kind: Issuer metadata: name: {{ include "%[1]s.fullname" . }}-%[2]s annotations: "helm.sh/hook": post-install,post-upgrade "helm.sh/hook-weight": "1" labels: {{- include "%[1]s.labels" . | nindent 4 }} spec: %[3]s` ) var issuerGVC = schema.GroupVersionKind{ Group: "cert-manager.io", Version: "v1", Kind: "Issuer", } // Issuer creates processor for k8s Issuer resource. func Issuer() helmify.Processor { return &issuer{} } type issuer struct{} // Process k8s Issuer object into template. Returns false if not capable of processing given resource type. func (i issuer) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstructured) (bool, helmify.Template, error) { if obj.GroupVersionKind() != issuerGVC { return false, nil, nil } name := appMeta.TrimName(obj.GetName()) spec, _ := yaml.Marshal(obj.Object["spec"]) spec = yamlformat.Indent(spec, 2) spec = bytes.TrimRight(spec, "\n ") tmpl := "" if appMeta.Config().CertManagerAsSubchart { tmpl = issuerTemplWithAnno } else { tmpl = issuerTempl } values := helmify.Values{} if appMeta.Config().AddWebhookOption { // Add webhook.enabled value to values.yaml _, _ = values.Add(true, "webhook", "enabled") tmpl = fmt.Sprintf("%s\n%s\n%s", WebhookHeader, tmpl, WebhookFooter) } res := fmt.Sprintf(tmpl, appMeta.ChartName(), name, string(spec)) return true, &issResult{ name: name, data: []byte(res), }, nil } type issResult struct { name string data []byte values helmify.Values } func (r *issResult) Filename() string { return r.name + ".yaml" } func (r *issResult) Values() helmify.Values { return r.values } func (r *issResult) Write(writer io.Writer) error { _, err := writer.Write(r.data) return err } 0707010000009D000081A4000000000000000000000001673B89830000032E000000000000000000000000000000000000003400000000helmify-0.4.16/pkg/processor/webhook/issuer_test.gopackage webhook import ( "testing" "github.com/arttor/helmify/pkg/metadata" "github.com/arttor/helmify/internal" "github.com/stretchr/testify/assert" ) const issuerYaml = `apiVersion: cert-manager.io/v1 kind: Issuer metadata: name: my-operator-selfsigned-issuer namespace: my-operator-system spec: selfSigned: {}` func Test_issuer_Process(t *testing.T) { var testInstance issuer t.Run("processed", func(t *testing.T) { obj := internal.GenerateObj(issuerYaml) processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, true, processed) }) t.Run("skipped", func(t *testing.T) { obj := internal.TestNs processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, false, processed) }) } 0707010000009E000081A4000000000000000000000001673B898300000B9C000000000000000000000000000000000000003100000000helmify-0.4.16/pkg/processor/webhook/mutating.gopackage webhook import ( "bytes" "fmt" "io" "strings" "github.com/arttor/helmify/pkg/helmify" v1 "k8s.io/api/admissionregistration/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "sigs.k8s.io/yaml" ) const ( mwhTempl = `apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration metadata: name: {{ include "%[1]s.fullname" . }}-%[2]s annotations: cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/{{ include "%[1]s.fullname" . }}-%[3]s labels: {{- include "%[1]s.labels" . | nindent 4 }} webhooks: %[4]s` ) var mwhGVK = schema.GroupVersionKind{ Group: "admissionregistration.k8s.io", Version: "v1", Kind: "MutatingWebhookConfiguration", } // MutatingWebhook creates processor for k8s MutatingWebhookConfiguration resource. func MutatingWebhook() helmify.Processor { return &mwh{} } type mwh struct{} // Process k8s MutatingWebhookConfiguration object into template. Returns false if not capable of processing given resource type. func (w mwh) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstructured) (bool, helmify.Template, error) { if obj.GroupVersionKind() != mwhGVK { return false, nil, nil } name := appMeta.TrimName(obj.GetName()) whConf := v1.MutatingWebhookConfiguration{} err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &whConf) if err != nil { return true, nil, fmt.Errorf("%w: unable to cast to MutatingWebhookConfiguration", err) } for i, whc := range whConf.Webhooks { whc.ClientConfig.Service.Name = appMeta.TemplatedName(whc.ClientConfig.Service.Name) whc.ClientConfig.Service.Namespace = strings.ReplaceAll(whc.ClientConfig.Service.Namespace, appMeta.Namespace(), `{{ .Release.Namespace }}`) whConf.Webhooks[i] = whc } webhooks, _ := yaml.Marshal(whConf.Webhooks) webhooks = bytes.TrimRight(webhooks, "\n ") certName, _, err := unstructured.NestedString(obj.Object, "metadata", "annotations", "cert-manager.io/inject-ca-from") if err != nil { return true, nil, fmt.Errorf("%w: unable get webhook certName", err) } certName = strings.TrimPrefix(certName, appMeta.Namespace()+"/") certName = appMeta.TrimName(certName) tmpl := mwhTempl values := helmify.Values{} if appMeta.Config().AddWebhookOption { // Add webhook.enabled value to values.yaml _, _ = values.Add(true, "webhook", "enabled") tmpl = fmt.Sprintf("%s\n%s\n%s", WebhookHeader, mwhTempl, WebhookFooter) } res := fmt.Sprintf(tmpl, appMeta.ChartName(), name, certName, string(webhooks)) return true, &mwhResult{ name: name, data: []byte(res), }, nil } type mwhResult struct { name string data []byte values helmify.Values } func (r *mwhResult) Filename() string { return r.name + ".yaml" } func (r *mwhResult) Values() helmify.Values { return r.values } func (r *mwhResult) Write(writer io.Writer) error { _, err := writer.Write(r.data) return err } 0707010000009F000081A4000000000000000000000001673B898300000526000000000000000000000000000000000000003600000000helmify-0.4.16/pkg/processor/webhook/mutating_test.gopackage webhook import ( "testing" "github.com/arttor/helmify/pkg/metadata" "github.com/arttor/helmify/internal" "github.com/stretchr/testify/assert" ) const mwhYaml = `apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration metadata: annotations: cert-manager.io/inject-ca-from: my-operator-system/my-operator-serving-cert name: my-operator-mutating-webhook-configuration webhooks: - admissionReviewVersions: - v1 - v1beta1 clientConfig: service: name: my-operator-webhook-service namespace: my-operator-system path: /mutate-ceph-example-com-v1alpha1-volume failurePolicy: Fail name: vvolume.kb.io rules: - apiGroups: - test.example.com apiVersions: - v1alpha1 operations: - CREATE - UPDATE resources: - volumes sideEffects: None` func Test_mwh_Process(t *testing.T) { var testInstance mwh t.Run("processed", func(t *testing.T) { obj := internal.GenerateObj(mwhYaml) processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, true, processed) }) t.Run("skipped", func(t *testing.T) { obj := internal.TestNs processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, false, processed) }) } 070701000000A0000081A4000000000000000000000001673B898300000BAC000000000000000000000000000000000000003300000000helmify-0.4.16/pkg/processor/webhook/validating.gopackage webhook import ( "bytes" "fmt" "io" "strings" "github.com/arttor/helmify/pkg/helmify" v1 "k8s.io/api/admissionregistration/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "sigs.k8s.io/yaml" ) const ( vwhTempl = `apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: name: {{ include "%[1]s.fullname" . }}-%[2]s annotations: cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/{{ include "%[1]s.fullname" . }}-%[3]s labels: {{- include "%[1]s.labels" . | nindent 4 }} webhooks: %[4]s` ) var vwhGVK = schema.GroupVersionKind{ Group: "admissionregistration.k8s.io", Version: "v1", Kind: "ValidatingWebhookConfiguration", } // ValidatingWebhook creates processor for k8s ValidatingWebhookConfiguration resource. func ValidatingWebhook() helmify.Processor { return &vwh{} } type vwh struct{} // Process k8s ValidatingWebhookConfiguration object into template. Returns false if not capable of processing given resource type. func (w vwh) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstructured) (bool, helmify.Template, error) { if obj.GroupVersionKind() != vwhGVK { return false, nil, nil } name := appMeta.TrimName(obj.GetName()) whConf := v1.ValidatingWebhookConfiguration{} err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &whConf) if err != nil { return true, nil, fmt.Errorf("%w: unable to cast to ValidatingWebhookConfiguration", err) } for i, whc := range whConf.Webhooks { whc.ClientConfig.Service.Name = appMeta.TemplatedName(whc.ClientConfig.Service.Name) whc.ClientConfig.Service.Namespace = strings.ReplaceAll(whc.ClientConfig.Service.Namespace, appMeta.Namespace(), `{{ .Release.Namespace }}`) whConf.Webhooks[i] = whc } webhooks, _ := yaml.Marshal(whConf.Webhooks) webhooks = bytes.TrimRight(webhooks, "\n ") certName, _, err := unstructured.NestedString(obj.Object, "metadata", "annotations", "cert-manager.io/inject-ca-from") if err != nil { return true, nil, fmt.Errorf("%w: unable get webhook certName", err) } certName = strings.TrimPrefix(certName, appMeta.Namespace()+"/") certName = appMeta.TrimName(certName) tmpl := vwhTempl values := helmify.Values{} if appMeta.Config().AddWebhookOption { // Add webhook.enabled value to values.yaml _, _ = values.Add(true, "webhook", "enabled") tmpl = fmt.Sprintf("%s\n%s\n%s", WebhookHeader, mwhTempl, WebhookFooter) } res := fmt.Sprintf(tmpl, appMeta.ChartName(), name, certName, string(webhooks)) return true, &vwhResult{ name: name, data: []byte(res), }, nil } type vwhResult struct { name string data []byte values helmify.Values } func (r *vwhResult) Filename() string { return r.name + ".yaml" } func (r *vwhResult) Values() helmify.Values { return r.values } func (r *vwhResult) Write(writer io.Writer) error { _, err := writer.Write(r.data) return err } 070701000000A1000081A4000000000000000000000001673B89830000052C000000000000000000000000000000000000003800000000helmify-0.4.16/pkg/processor/webhook/validating_test.gopackage webhook import ( "testing" "github.com/arttor/helmify/pkg/metadata" "github.com/arttor/helmify/internal" "github.com/stretchr/testify/assert" ) const vwhYaml = `apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: annotations: cert-manager.io/inject-ca-from: my-operator-system/my-operator-serving-cert name: my-operator-validating-webhook-configuration webhooks: - admissionReviewVersions: - v1 - v1beta1 clientConfig: service: name: my-operator-webhook-service namespace: my-operator-system path: /validate-ceph-example-com-v1alpha1-volume failurePolicy: Fail name: vvolume.kb.io rules: - apiGroups: - test.example.com apiVersions: - v1alpha1 operations: - CREATE - UPDATE resources: - volumes sideEffects: None` func Test_vwh_Process(t *testing.T) { var testInstance vwh t.Run("processed", func(t *testing.T) { obj := internal.GenerateObj(vwhYaml) processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, true, processed) }) t.Run("skipped", func(t *testing.T) { obj := internal.TestNs processed, _, err := testInstance.Process(&metadata.Service{}, obj) assert.NoError(t, err) assert.Equal(t, false, processed) }) } 070701000000A2000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000001800000000helmify-0.4.16/pkg/yaml070701000000A3000081A4000000000000000000000001673B89830000029B000000000000000000000000000000000000002000000000helmify-0.4.16/pkg/yaml/yaml.gopackage yaml import ( "bytes" "sigs.k8s.io/yaml" ) // Indent - adds indentation to given content. func Indent(content []byte, n int) []byte { if n < 0 { return content } prefix := append([]byte("\n"), bytes.Repeat([]byte(" "), n)...) content = append(prefix[1:], content...) return bytes.ReplaceAll(content, []byte("\n"), prefix) } // Marshal object to yaml string with indentation. func Marshal(object interface{}, indent int) (string, error) { objectBytes, err := yaml.Marshal(object) if err != nil { return "", err } objectBytes = Indent(objectBytes, indent) objectBytes = bytes.TrimRight(objectBytes, "\n ") return string(objectBytes), nil } 070701000000A4000081A4000000000000000000000001673B8983000002E6000000000000000000000000000000000000002500000000helmify-0.4.16/pkg/yaml/yaml_test.gopackage yaml import ( "reflect" "testing" ) func TestIndent(t *testing.T) { type args struct { content []byte n int } tests := []struct { name string args args want []byte }{ { name: "negative", args: args{[]byte("a"), -1}, want: []byte("a"), }, { name: "none", args: args{[]byte("a"), 0}, want: []byte("a"), }, { name: "one", args: args{[]byte("a"), 1}, want: []byte(" a"), }, { name: "two", args: args{[]byte("a"), 2}, want: []byte(" a"), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := Indent(tt.args.content, tt.args.n); !reflect.DeepEqual(got, tt.want) { t.Errorf("Indent() = %v, want %v", got, tt.want) } }) } } 070701000000A5000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000001900000000helmify-0.4.16/test_data070701000000A6000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000001D00000000helmify-0.4.16/test_data/dir070701000000A7000041ED000000000000000000000002673B898300000000000000000000000000000000000000000000002900000000helmify-0.4.16/test_data/dir/another_dir070701000000A8000081A4000000000000000000000001673B898300000288000000000000000000000000000000000000003B00000000helmify-0.4.16/test_data/dir/another_dir/stateful_set.yamlapiVersion: apps/v1 kind: StatefulSet metadata: name: web spec: serviceName: "nginx" replicas: 2 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: registry.k8s.io/nginx-slim:0.8 ports: - containerPort: 80 name: web volumeMounts: - name: www mountPath: /usr/share/nginx/html volumeClaimTemplates: - metadata: name: www spec: accessModes: [ "ReadWriteOnce" ] resources: requests: storage: 1Gi070701000000A9000081A4000000000000000000000001673B8983000002B2000000000000000000000000000000000000002900000000helmify-0.4.16/test_data/dir/config.yamlapiVersion: v1 kind: ConfigMap metadata: name: my-config namespace: my-ns immutable: true data: dummyconfigmapkey: dummyconfigmapvalue my_config.properties: | health.healthProbeBindAddress=8081 metrics.bindAddress=127.0.0.1:8080 --- apiVersion: v1 kind: ConfigMap metadata: name: my-config-props namespace: my-ns data: my.prop1: "1" my.prop2: "val 1" my.prop3: "true" myval.yaml: | apiVersion: clickhouse.altinity.com/v1 kind: ClickHouseInstallationTemplate metadata: name: default-oneperhost-pod-template spec: templates: podTemplates: - name: default-oneperhost-pod-template distribution: "OnePerHost"070701000000AA000081A4000000000000000000000001673B8983000009D3000000000000000000000000000000000000002D00000000helmify-0.4.16/test_data/dir/deployment.yamlapiVersion: apps/v1 kind: Deployment metadata: labels: app: myapp name: myapp namespace: my-ns spec: replicas: 3 selector: matchLabels: app: myapp template: metadata: labels: app: myapp spec: containers: - name: app args: - --health-probe-bind-address=:8081 - --metrics-bind-address=127.0.0.1:8080 - --leader-elect command: - /manager volumeMounts: - mountPath: /my_config.properties name: manager-config subPath: my_config.properties - name: secret-volume mountPath: /my.ca - name: props mountPath: /etc/props - name: sample-pv-storage mountPath: "/usr/share/nginx/html" env: - name: VAR1 valueFrom: secretKeyRef: name: my-secret-vars key: VAR1 - name: VAR2 valueFrom: secretKeyRef: name: my-secret-vars key: VAR2 image: controller:latest livenessProbe: httpGet: path: /healthz port: 8081 initialDelaySeconds: 15 periodSeconds: 20 readinessProbe: httpGet: path: /readyz port: 8081 initialDelaySeconds: 5 periodSeconds: 10 resources: limits: cpu: 100m memory: 30Mi requests: cpu: 100m memory: 20Mi securityContext: allowPrivilegeEscalation: false - name: proxy-sidecar args: - --secure-listen-address=0.0.0.0:8443 - --v=10 image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0 ports: - containerPort: 8443 name: https securityContext: runAsNonRoot: true nodeSelector: region: east type: user-node terminationGracePeriodSeconds: 10 volumes: - configMap: name: my-config name: manager-config - configMap: name: my-config-props name: props - name: secret-volume secret: secretName: my-secret-ca - name: sample-pv-storage persistentVolumeClaim: claimName: my-sample-pv-claim070701000000AB000081A4000000000000000000000001673B898300000370000000000000000000000000000000000000002900000000helmify-0.4.16/test_data/dir/secret.yamlapiVersion: v1 kind: Secret metadata: name: my-secret-ca namespace: my-ns type: opaque data: ca.crt: | c3VwZXJsb25ndGVzdGNydC1zdXBlcmxvbmd0ZXN0Y3J0LXN1cGVybG9uZ3Rlc3RjcnQtc3 VwZXJsb25ndGVzdGNydC1zdXBlcmxvbmd0ZXN0Y3J0LXN1cGVybG9uZ3Rlc3RjcnQtc3Vw ZXJsb25ndGVzdGNydC0Kc3VwZXJsb25ndGVzdGNydC1zdXBlcmxvbmd0ZXN0Y3J0LXN1cG VybG9uZ3Rlc3RjcnQtc3VwZXJsb25ndGVzdGNydC1zdXBlcmxvbmd0ZXN0Y3J0LXN1cGVy bG9uZ3Rlc3RjcnQKc3VwZXJsb25ndGVzdGNydC1zdXBlcmxvbmd0ZXN0Y3J0LXN1cGVybG 9uZ3Rlc3RjcnQtc3VwZXJsb25ndGVzdGNydC1zdXBlcmxvbmd0ZXN0Y3J0LXN1cGVybG9u Z3Rlc3RjcnQ= --- apiVersion: v1 kind: Secret metadata: name: my-secret-vars namespace: my-ns type: opaque data: VAR1: bXlfc2VjcmV0X3Zhcl8x VAR2: bXlfc2VjcmV0X3Zhcl8y ELASTIC_FOOBAR_HUNTER123_MEOWTOWN_VERIFY: bXlfc2VjcmV0X3Zhcl8y stringData: str: | some big not so secret string with multiple lines070701000000AC000081A4000000000000000000000001673B898300000249000000000000000000000000000000000000002A00000000helmify-0.4.16/test_data/dir/service.yamlapiVersion: v1 kind: Service metadata: labels: app: myapp name: myapp-service namespace: my-ns spec: ports: - name: https port: 8443 targetPort: https selector: app: myapp --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: myapp-ingress annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: rules: - http: paths: - path: /testpath pathType: Prefix backend: service: name: myapp-service port: number: 8443070701000000AD000081A4000000000000000000000001673B8983000000E0000000000000000000000000000000000000002A00000000helmify-0.4.16/test_data/dir/storage.yamlapiVersion: v1 kind: PersistentVolumeClaim metadata: name: my-sample-pv-claim spec: storageClassName: manual accessModes: - ReadWriteOnce resources: requests: storage: 3Gi limits: storage: 5Gi070701000000AE000081A4000000000000000000000001673B8983000052E8000000000000000000000000000000000000002E00000000helmify-0.4.16/test_data/k8s-operator-ci.yamlapiVersion: v1 kind: Namespace metadata: labels: control-plane: controller-manager name: my-operator-system --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: my-operator-system/my-operator-serving-cert example-annotation: xyz creationTimestamp: null name: cephvolumes.test.example.com labels: example-label: my-app spec: group: test.example.com names: kind: CephVolume listKind: CephVolumeList plural: cephvolumes singular: cephvolume scope: Namespaced versions: - additionalPrinterColumns: - description: Ceph RBD pool name jsonPath: .spec.pool name: Pool type: string - description: Storage type jsonPath: .status.type name: Type type: string - description: Volume size jsonPath: .spec.size name: Size type: string - description: Max number of volume I/O operations per second jsonPath: .status.limits.iops name: IOPS type: string - description: true if volume contains latest type,size spec from Ceph jsonPath: .status.conditions[?(@.type=="Provided")].status name: Provided type: string - description: true if volume IOPS limits calculated. False indicates error - check reason for details jsonPath: .status.conditions[?(@.type=="Calculated")].status name: Calculated type: string - description: true if volume IOPS limits applied to volume. False indicates error - check reason for details jsonPath: .status.conditions[?(@.type=="Limited")].status name: Limited type: string - description: latest resource generation jsonPath: .metadata.generation name: gen type: string - description: latest observed generation of Limited condition jsonPath: .status.conditions[?(@.type=="Limited")].observedGeneration name: Lim-gen type: string name: v1alpha1 schema: openAPIV3Schema: description: CephVolume represents Ceph RBD volume properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: description: CephVolumeSpec defines the desired state of CephVolume properties: pool: description: Pool - volume pool name type: string size: anyOf: - type: integer - type: string description: Size - volume size pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true type: object status: description: CephVolumeStatus defines the observed state of CephVolume properties: conditions: description: 'Conditions represent the latest available observations of an object''s state Known .status.conditions.type are: "Provided". "Calculated", "Limited"' items: description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type \ // +patchStrategy=merge // +listType=map // +listMapKey=type \ Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" properties: lastTransitionTime: description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object type: array limits: description: Limits represent calculated IOPS limits properties: iops: description: IOPS - desired limit of IO operations per second. See Ceph rbd_qos_iops_limit property. format: int64 minimum: 0 type: integer iopsBurst: description: IOPSBurst - desired burst limit of IO operations. See Ceph rbd_qos_iops_burst property. format: int64 minimum: 0 type: integer readIOPS: description: ReadIOPS - desired limit of read operations per second. See Ceph rbd_qos_read_iops_limit property. format: int64 minimum: 0 type: integer readIOPSBurst: description: ReadIOPSBurst - desired burst limit of read operations. See Ceph rbd_qos_read_iops_burst property. format: int64 minimum: 0 type: integer writeIOPS: description: WriteIOPS - desired limit of write operations per second. See Ceph rbd_qos_write_iops_limit property format: int64 minimum: 0 type: integer writeIOPSBurst: description: WriteIOPSBurst - desired burst limit of write operations. See Ceph rbd_qos_write_iops_burst property. format: int64 minimum: 0 type: integer type: object type: description: Type - volume storage type. See StorageType CRD. type: string required: - conditions type: object type: object served: true storage: true subresources: status: {} status: acceptedNames: kind: "" plural: "" conditions: [] storedVersions: [] --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: my-operator-system/my-operator-serving-cert creationTimestamp: null name: manifestcephvolumes.test.example.com spec: group: test.example.com names: kind: ManifestCephVolume listKind: ManifestCephVolumeList plural: manifestcephvolumes singular: manifestcephvolume scope: Namespaced versions: - additionalPrinterColumns: - description: Ceph RBD pool name jsonPath: .spec.poolName name: PoolName type: string - description: Sync interval in seconds jsonPath: .spec.interval name: Interval type: string - description: Last update time jsonPath: .status.lastUpdate name: LastUpdate type: string name: v1alpha1 schema: openAPIV3Schema: description: ManifestCephVolume monitors given ceph pool and manifests containing volumes as CephVolume CR properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: description: ManifestCephVolumeSpec defines the desired state of ManifestCephVolume properties: interval: description: Interval - Ceph pool polling interval format: int32 minimum: 60 type: integer poolName: description: PoolName name of Ceph RBD pool to get volumes type: string required: - interval type: object status: description: ManifestCephVolumeStatus defines the observed state of ManifestCephVolume properties: lastUpdate: description: LastUpdate - time of last successful volumes update format: date-time type: string type: object type: object served: true storage: true subresources: status: {} status: acceptedNames: kind: "" plural: "" conditions: [] storedVersions: [] --- apiVersion: v1 kind: ServiceAccount metadata: name: my-operator-controller-manager namespace: my-operator-system annotations: k8s.acme.org/some-meta-data: "ACME Inc." --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: my-operator-leader-election-role namespace: my-operator-system rules: - apiGroups: - "" resources: - configmaps verbs: - get - list - watch - create - update - patch - delete - apiGroups: - coordination.k8s.io resources: - leases verbs: - get - list - watch - create - update - patch - delete - apiGroups: - "" resources: - events verbs: - create - patch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: creationTimestamp: null name: my-operator-manager-aggregated-role aggregationRule: clusterRoleSelectors: - matchExpressions: - key: app.kubernetes.io/instance operator: In values: - my-operator rules: [] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: creationTimestamp: null name: my-operator-manager-role rules: - apiGroups: - "" resources: - pods verbs: - get - list - apiGroups: - "" resources: - pods/exec verbs: - create - get - apiGroups: - test.example.com resources: - cephvolumes verbs: - create - delete - get - list - patch - update - watch - apiGroups: - test.example.com resources: - cephvolumes/finalizers verbs: - update - apiGroups: - test.example.com resources: - cephvolumes/status verbs: - get - patch - update - apiGroups: - test.example.com resources: - manifestcephvolumes verbs: - create - delete - get - list - patch - update - watch - apiGroups: - test.example.com resources: - manifestcephvolumes/finalizers verbs: - update - apiGroups: - test.example.com resources: - manifestcephvolumes/status verbs: - get - patch - update - apiGroups: - test.example.com resources: - storagetypes verbs: - get - list - watch - apiGroups: - test.example.com resources: - storagetypes/status verbs: - get --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: my-operator-metrics-reader rules: - nonResourceURLs: - /metrics verbs: - get --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: my-operator-proxy-role rules: - apiGroups: - authentication.k8s.io resources: - tokenreviews verbs: - create - apiGroups: - authorization.k8s.io resources: - subjectaccessreviews verbs: - create --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: my-operator-leader-election-rolebinding namespace: my-operator-system roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: my-operator-leader-election-role subjects: - kind: ServiceAccount name: my-operator-controller-manager namespace: my-operator-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: my-operator-manager-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: my-operator-manager-role subjects: - kind: ServiceAccount name: my-operator-controller-manager namespace: my-operator-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: my-operator-proxy-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: my-operator-proxy-role subjects: - kind: ServiceAccount name: my-operator-controller-manager namespace: my-operator-system --- apiVersion: v1 data: dummyconfigmapkey: dummyconfigmapvalue controller_manager_config.yaml: | apiVersion: controller-runtime.sigs.k8s.io/v1alpha1 kind: ControllerManagerConfig health: healthProbeBindAddress: :8081 metrics: bindAddress: 127.0.0.1:8080 webhook: port: 9443 leaderElection: leaderElect: true resourceName: 3a2e09e9.example.com rook: namespace: rook-ceph toolboxPodLabel: rook-ceph-tools kind: ConfigMap metadata: name: my-operator-manager-config namespace: my-operator-system --- apiVersion: v1 kind: Service metadata: labels: control-plane: controller-manager name: my-operator-controller-manager-metrics-service namespace: my-operator-system spec: ports: - name: https port: 8443 targetPort: https selector: control-plane: controller-manager --- apiVersion: apps/v1 kind: Deployment metadata: labels: control-plane: controller-manager name: my-operator-controller-manager namespace: my-operator-system spec: replicas: 1 revisionHistoryLimit: 5 selector: matchLabels: control-plane: controller-manager template: metadata: labels: control-plane: controller-manager spec: topologySpreadConstraints: - maxSkew: 1 topologyKey: kubernetes.io/hostname whenUnsatisfiable: DoNotSchedule matchLabelKeys: - app - pod-template-hash containers: - args: - --secure-listen-address=0.0.0.0:8443 - --upstream=http://127.0.0.1:8080/ - --logtostderr=true - --v=10 image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0 name: kube-rbac-proxy ports: - containerPort: 8443 name: https - args: - --health-probe-bind-address=:8081 - --metrics-bind-address=127.0.0.1:8080 - --leader-elect command: - /manager volumeMounts: - mountPath: /controller_manager_config.yaml name: manager-config subPath: controller_manager_config.yaml - name: secret-volume mountPath: /my.ca env: - name: VAR1 valueFrom: secretKeyRef: name: my-operator-secret-vars key: VAR1 - name: VAR2 value: "ciao" - name: VAR3_MY_ENV value: "ciao" - name: VAR4 valueFrom: configMapKeyRef: name: my-operator-configmap-vars key: VAR4 - name: VAR5 valueFrom: fieldRef: fieldPath: metadata.namespace - name: VAR6 valueFrom: resourceFieldRef: resource: limits.cpu image: busybox:latest imagePullPolicy: Always livenessProbe: httpGet: path: /healthz port: 8081 initialDelaySeconds: 15 periodSeconds: 20 name: manager readinessProbe: httpGet: path: /readyz port: 8081 initialDelaySeconds: 5 periodSeconds: 10 resources: limits: cpu: 100m memory: 30Mi requests: cpu: 100m memory: 20Mi securityContext: allowPrivilegeEscalation: false capabilities: drop: - ALL privileged: false readOnlyRootFilesystem: true runAsNonRoot: true runAsUser: 65532 seccompProfile: type: RuntimeDefault securityContext: runAsNonRoot: true nodeSelector: region: east type: user-node serviceAccountName: my-operator-controller-manager terminationGracePeriodSeconds: 10 volumes: - configMap: name: my-operator-manager-config name: manager-config - name: secret-volume secret: secretName: my-operator-secret-ca --- apiVersion: v1 data: VAR1: bXlfc2VjcmV0X3Zhcl8x VAR2: bXlfc2VjcmV0X3Zhcl8y kind: Secret metadata: name: my-operator-secret-vars namespace: my-operator-system type: opaque --- apiVersion: v1 data: VAR4: value for var4 kind: ConfigMap metadata: name: my-operator-configmap-vars namespace: my-operator-system --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: my-operator-pvc-lim spec: accessModes: - ReadWriteOnce resources: requests: storage: 2Gi storageClassName: cust1-mypool-lim 070701000000AF000081A4000000000000000000000001673B89830000602B000000000000000000000000000000000000003700000000helmify-0.4.16/test_data/k8s-operator-kustomize.outputapiVersion: v1 kind: Namespace metadata: labels: control-plane: controller-manager name: my-operator-system --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: my-operator-system/my-operator-serving-cert example-annotation: xyz creationTimestamp: null name: cephvolumes.test.example.com labels: example-label: my-app spec: group: test.example.com names: kind: CephVolume listKind: CephVolumeList plural: cephvolumes singular: cephvolume scope: Namespaced versions: - additionalPrinterColumns: - description: Ceph RBD pool name jsonPath: .spec.pool name: Pool type: string - description: Storage type jsonPath: .status.type name: Type type: string - description: Volume size jsonPath: .spec.size name: Size type: string - description: Max number of volume I/O operations per second jsonPath: .status.limits.iops name: IOPS type: string - description: true if volume contains latest type,size spec from Ceph jsonPath: .status.conditions[?(@.type=="Provided")].status name: Provided type: string - description: true if volume IOPS limits calculated. False indicates error - check reason for details jsonPath: .status.conditions[?(@.type=="Calculated")].status name: Calculated type: string - description: true if volume IOPS limits applied to volume. False indicates error - check reason for details jsonPath: .status.conditions[?(@.type=="Limited")].status name: Limited type: string - description: latest resource generation jsonPath: .metadata.generation name: gen type: string - description: latest observed generation of Limited condition jsonPath: .status.conditions[?(@.type=="Limited")].observedGeneration name: Lim-gen type: string name: v1alpha1 schema: openAPIV3Schema: description: CephVolume represents Ceph RBD volume properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: description: CephVolumeSpec defines the desired state of CephVolume properties: pool: description: Pool - volume pool name type: string size: anyOf: - type: integer - type: string description: Size - volume size pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true type: object status: description: CephVolumeStatus defines the observed state of CephVolume properties: conditions: description: 'Conditions represent the latest available observations of an object''s state Known .status.conditions.type are: "Provided". "Calculated", "Limited"' items: description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type \ // +patchStrategy=merge // +listType=map // +listMapKey=type \ Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" properties: lastTransitionTime: description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object type: array limits: description: Limits represent calculated IOPS limits properties: iops: description: IOPS - desired limit of IO operations per second. See Ceph rbd_qos_iops_limit property. format: int64 minimum: 0 type: integer iopsBurst: description: IOPSBurst - desired burst limit of IO operations. See Ceph rbd_qos_iops_burst property. format: int64 minimum: 0 type: integer readIOPS: description: ReadIOPS - desired limit of read operations per second. See Ceph rbd_qos_read_iops_limit property. format: int64 minimum: 0 type: integer readIOPSBurst: description: ReadIOPSBurst - desired burst limit of read operations. See Ceph rbd_qos_read_iops_burst property. format: int64 minimum: 0 type: integer writeIOPS: description: WriteIOPS - desired limit of write operations per second. See Ceph rbd_qos_write_iops_limit property format: int64 minimum: 0 type: integer writeIOPSBurst: description: WriteIOPSBurst - desired burst limit of write operations. See Ceph rbd_qos_write_iops_burst property. format: int64 minimum: 0 type: integer type: object type: description: Type - volume storage type. See StorageType CRD. type: string required: - conditions type: object type: object served: true storage: true subresources: status: {} status: acceptedNames: kind: "" plural: "" conditions: [] storedVersions: [] --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: my-operator-system/my-operator-serving-cert creationTimestamp: null name: manifestcephvolumes.test.example.com spec: conversion: strategy: Webhook webhook: clientConfig: service: name: my-operator-webhook-service namespace: my-operator-system path: /convert conversionReviewVersions: - v1 group: test.example.com names: kind: ManifestCephVolume listKind: ManifestCephVolumeList plural: manifestcephvolumes singular: manifestcephvolume scope: Namespaced versions: - additionalPrinterColumns: - description: Ceph RBD pool name jsonPath: .spec.poolName name: PoolName type: string - description: Sync interval in seconds jsonPath: .spec.interval name: Interval type: string - description: Last update time jsonPath: .status.lastUpdate name: LastUpdate type: string name: v1alpha1 schema: openAPIV3Schema: description: ManifestCephVolume monitors given ceph pool and manifests containing volumes as CephVolume CR properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: description: ManifestCephVolumeSpec defines the desired state of ManifestCephVolume properties: interval: description: Interval - Ceph pool polling interval format: int32 minimum: 60 type: integer poolName: description: PoolName name of Ceph RBD pool to get volumes type: string required: - interval type: object status: description: ManifestCephVolumeStatus defines the observed state of ManifestCephVolume properties: lastUpdate: description: LastUpdate - time of last successful volumes update format: date-time type: string type: object type: object served: true storage: true subresources: status: {} status: acceptedNames: kind: "" plural: "" conditions: [] storedVersions: [] --- apiVersion: v1 kind: ServiceAccount metadata: name: my-operator-controller-manager namespace: my-operator-system annotations: k8s.acme.org/some-meta-data: "ACME Inc." --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: my-operator-leader-election-role namespace: my-operator-system rules: - apiGroups: - "" resources: - configmaps verbs: - get - list - watch - create - update - patch - delete - apiGroups: - coordination.k8s.io resources: - leases verbs: - get - list - watch - create - update - patch - delete - apiGroups: - "" resources: - events verbs: - create - patch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: creationTimestamp: null name: my-operator-manager-aggregated-role aggregationRule: clusterRoleSelectors: - matchExpressions: - key: app.kubernetes.io/instance operator: In values: - my-operator rules: [] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: creationTimestamp: null name: my-operator-manager-role rules: - apiGroups: - "" resources: - pods verbs: - get - list - apiGroups: - "" resources: - pods/exec verbs: - create - get - apiGroups: - test.example.com resources: - cephvolumes verbs: - create - delete - get - list - patch - update - watch - apiGroups: - test.example.com resources: - cephvolumes/finalizers verbs: - update - apiGroups: - test.example.com resources: - cephvolumes/status verbs: - get - patch - update - apiGroups: - test.example.com resources: - manifestcephvolumes verbs: - create - delete - get - list - patch - update - watch - apiGroups: - test.example.com resources: - manifestcephvolumes/finalizers verbs: - update - apiGroups: - test.example.com resources: - manifestcephvolumes/status verbs: - get - patch - update - apiGroups: - test.example.com resources: - storagetypes verbs: - get - list - watch - apiGroups: - test.example.com resources: - storagetypes/status verbs: - get --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: my-operator-metrics-reader rules: - nonResourceURLs: - /metrics verbs: - get --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: my-operator-proxy-role rules: - apiGroups: - authentication.k8s.io resources: - tokenreviews verbs: - create - apiGroups: - authorization.k8s.io resources: - subjectaccessreviews verbs: - create --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: my-operator-leader-election-rolebinding namespace: my-operator-system roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: my-operator-leader-election-role subjects: - kind: ServiceAccount name: my-operator-controller-manager namespace: my-operator-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: my-operator-manager-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: my-operator-manager-role subjects: - kind: ServiceAccount name: my-operator-controller-manager namespace: my-operator-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: my-operator-proxy-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: my-operator-proxy-role subjects: - kind: ServiceAccount name: my-operator-controller-manager namespace: my-operator-system --- apiVersion: v1 data: dummyconfigmapkey: dummyconfigmapvalue controller_manager_config.yaml: | apiVersion: controller-runtime.sigs.k8s.io/v1alpha1 kind: ControllerManagerConfig health: healthProbeBindAddress: :8081 metrics: bindAddress: 127.0.0.1:8080 webhook: port: 9443 leaderElection: leaderElect: true resourceName: 3a2e09e9.example.com rook: namespace: rook-ceph toolboxPodLabel: rook-ceph-tools kind: ConfigMap metadata: name: my-operator-manager-config namespace: my-operator-system --- apiVersion: v1 kind: Service metadata: labels: control-plane: controller-manager name: my-operator-controller-manager-metrics-service namespace: my-operator-system spec: ports: - name: https port: 8443 targetPort: https selector: control-plane: controller-manager --- apiVersion: apps/v1 kind: Deployment metadata: labels: control-plane: controller-manager name: my-operator-controller-manager namespace: my-operator-system spec: replicas: 1 selector: matchLabels: control-plane: controller-manager template: metadata: labels: control-plane: controller-manager spec: topologySpreadConstraints: - maxSkew: 1 topologyKey: kubernetes.io/hostname whenUnsatisfiable: DoNotSchedule matchLabelKeys: - app - pod-template-hash imagePullSecrets: - name: my-operator-secret-registry-credentials containers: - args: - --secure-listen-address=0.0.0.0:8443 - --upstream=http://127.0.0.1:8080/ - --logtostderr=true - --v=10 image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0 name: kube-rbac-proxy ports: - containerPort: 8443 name: https - args: - --health-probe-bind-address=:8081 - --metrics-bind-address=127.0.0.1:8080 - --leader-elect command: - /manager volumeMounts: - mountPath: /controller_manager_config.yaml name: manager-config subPath: controller_manager_config.yaml - name: secret-volume mountPath: /my.ca env: - name: VAR1 valueFrom: secretKeyRef: name: my-operator-secret-vars key: VAR1 - name: VAR2 value: "ciao" - name: VAR3_MY_ENV value: "ciao" - name: VAR4 valueFrom: configMapKeyRef: name: my-operator-configmap-vars key: VAR4 - name: VAR5 valueFrom: fieldRef: fieldPath: metadata.namespace - name: VAR6 valueFrom: resourceFieldRef: resource: limits.cpu image: controller:latest imagePullPolicy: Always livenessProbe: httpGet: path: /healthz port: 8081 initialDelaySeconds: 15 periodSeconds: 20 name: manager readinessProbe: httpGet: path: /readyz port: 8081 initialDelaySeconds: 5 periodSeconds: 10 resources: limits: cpu: 100m memory: 30Mi requests: cpu: 100m memory: 20Mi securityContext: allowPrivilegeEscalation: false capabilities: drop: - ALL privileged: false readOnlyRootFilesystem: true runAsNonRoot: true runAsUser: 65532 seccompProfile: type: RuntimeDefault securityContext: runAsNonRoot: true nodeSelector: region: east type: user-node serviceAccountName: my-operator-controller-manager terminationGracePeriodSeconds: 10 volumes: - configMap: name: my-operator-manager-config name: manager-config - name: secret-volume secret: secretName: my-operator-secret-ca --- apiVersion: v1 kind: Service metadata: name: my-operator-webhook-service namespace: my-operator-system spec: ports: - port: 443 targetPort: 9443 selector: control-plane: controller-manager --- apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: my-operator-serving-cert namespace: my-operator-system spec: dnsNames: - my-operator-webhook-service.my-operator-system.svc - my-operator-webhook-service.my-operator-system.svc.cluster.local issuerRef: kind: Issuer name: my-operator-selfsigned-issuer secretName: webhook-server-cert --- apiVersion: cert-manager.io/v1 kind: Issuer metadata: name: my-operator-selfsigned-issuer namespace: my-operator-system spec: selfSigned: {} --- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: annotations: cert-manager.io/inject-ca-from: my-operator-system/my-operator-serving-cert name: my-operator-validating-webhook-configuration webhooks: - admissionReviewVersions: - v1 - v1beta1 clientConfig: service: name: my-operator-webhook-service namespace: my-operator-system path: /validate-ceph-example-com-v1alpha1-volume failurePolicy: Fail name: vvolume.kb.io rules: - apiGroups: - test.example.com apiVersions: - v1alpha1 operations: - CREATE - UPDATE resources: - volumes sideEffects: None --- apiVersion: v1 data: ca.crt: | c3VwZXJsb25ndGVzdGNydC1zdXBlcmxvbmd0ZXN0Y3J0LXN1cGVybG9uZ3Rlc3RjcnQtc3 VwZXJsb25ndGVzdGNydC1zdXBlcmxvbmd0ZXN0Y3J0LXN1cGVybG9uZ3Rlc3RjcnQtc3Vw ZXJsb25ndGVzdGNydC0Kc3VwZXJsb25ndGVzdGNydC1zdXBlcmxvbmd0ZXN0Y3J0LXN1cG VybG9uZ3Rlc3RjcnQtc3VwZXJsb25ndGVzdGNydC1zdXBlcmxvbmd0ZXN0Y3J0LXN1cGVy bG9uZ3Rlc3RjcnQKc3VwZXJsb25ndGVzdGNydC1zdXBlcmxvbmd0ZXN0Y3J0LXN1cGVybG 9uZ3Rlc3RjcnQtc3VwZXJsb25ndGVzdGNydC1zdXBlcmxvbmd0ZXN0Y3J0LXN1cGVybG9u Z3Rlc3RjcnQ= kind: Secret metadata: name: my-operator-secret-ca namespace: my-operator-system type: opaque --- apiVersion: v1 data: .dockerconfigjson: | ewogICAgImF1dGhzIjogewogICAgICAgICJmb28uYmFyLmlvIjogewogICAgICAgICAgIC AidXNlcm5hbWUiOiAidXNlcm5hbWUiLAogICAgICAgICAgICAicGFzc3dvcmQiOiAic2Vj cmV0IiwKICAgICAgICAgICAgImF1dGgiOiAiZFhObGNtNWhiV1U2YzJWamNtVjAiCiAgIC AgICAgfQogICAgfQp9 kind: Secret metadata: name: my-operator-secret-registry-credentials namespace: my-operator-system type: kubernetes.io/dockerconfigjson --- apiVersion: v1 data: VAR1: bXlfc2VjcmV0X3Zhcl8x VAR2: bXlfc2VjcmV0X3Zhcl8y kind: Secret metadata: name: my-operator-secret-vars namespace: my-operator-system type: opaque --- apiVersion: v1 data: VAR4: value for var4 kind: ConfigMap metadata: name: my-operator-configmap-vars namespace: my-operator-system --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: my-operator-pvc-lim spec: accessModes: - ReadWriteOnce resources: requests: storage: 2Gi storageClassName: cust1-mypool-lim --- apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration metadata: annotations: cert-manager.io/inject-ca-from: my-operator-system/my-operator-serving-cert name: my-operator-mutating-webhook-configuration webhooks: - admissionReviewVersions: - v1 clientConfig: service: name: my-operator-webhook-service namespace: my-operator-system path: /mutate-ceph-example-com-v1-mycluster failurePolicy: Fail name: mmycluster.kb.io rules: - apiGroups: - test.example.com apiVersions: - v1 operations: - CREATE - UPDATE resources: - myclusters sideEffects: None 070701000000B0000081A4000000000000000000000001673B8983000021BE000000000000000000000000000000000000002900000000helmify-0.4.16/test_data/sample-app.yamlapiVersion: apps/v1 kind: Deployment metadata: labels: app: myapp name: myapp namespace: my-ns spec: replicas: 3 revisionHistoryLimit: 5 selector: matchLabels: app: myapp template: metadata: labels: app: myapp spec: initContainers: - name: init-container image: bash:latest command: ["/bin/sh", "-c", "echo 'Initializing container...'"] containers: - name: app args: - --health-probe-bind-address=:8081 - --metrics-bind-address=127.0.0.1:8080 - --leader-elect command: - /manager volumeMounts: - mountPath: /my_config.properties name: manager-config subPath: my_config.properties - name: secret-volume mountPath: /my.ca - name: props mountPath: /etc/props - name: sample-pv-storage mountPath: "/usr/share/nginx/html" env: - name: VAR1 valueFrom: secretKeyRef: name: my-secret-vars key: VAR1 - name: VAR2 valueFrom: secretKeyRef: name: my-secret-vars key: VAR2 - name: APP_NAME valueFrom: fieldRef: fieldPath: metadata.labels['app.kubernetes.io/name'] - name: INSTANCE_NAME valueFrom: fieldRef: fieldPath: metadata.labels['app.kubernetes.io/instance'] image: controller:latest livenessProbe: httpGet: path: /healthz port: 8081 initialDelaySeconds: 15 periodSeconds: 20 readinessProbe: httpGet: path: /readyz port: 8081 initialDelaySeconds: 5 periodSeconds: 10 resources: limits: cpu: 100m memory: 30Mi requests: cpu: 100m memory: 20Mi securityContext: allowPrivilegeEscalation: false - name: proxy-sidecar args: - --secure-listen-address=0.0.0.0:8443 - --v=10 image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0 ports: - containerPort: 8443 name: https securityContext: runAsNonRoot: true fsGroup: 20000 runAsUser: 65532 nodeSelector: region: east type: user-node terminationGracePeriodSeconds: 10 volumes: - configMap: name: my-config name: manager-config - configMap: name: my-config-props name: props - name: secret-volume secret: secretName: my-secret-ca - name: sample-pv-storage persistentVolumeClaim: claimName: my-sample-pv-claim --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: my-sample-pv-claim spec: storageClassName: manual accessModes: - ReadWriteOnce resources: requests: storage: 3Gi limits: storage: 5Gi --- apiVersion: v1 kind: Service metadata: labels: app: myapp name: myapp-service namespace: my-ns spec: ports: - name: https port: 8443 targetPort: https selector: app: myapp --- apiVersion: v1 kind: Service metadata: labels: app: myapp name: myapp-lb-service namespace: my-ns spec: ports: - name: https port: 8443 targetPort: https selector: app: myapp type: LoadBalancer loadBalancerSourceRanges: - 10.0.0.0/8 --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: myapp-ingress annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: rules: - http: paths: - path: /testpath pathType: Prefix backend: service: name: myapp-service port: number: 8443 --- apiVersion: v1 kind: Secret metadata: name: my-secret-ca namespace: my-ns type: opaque data: ca.crt: | c3VwZXJsb25ndGVzdGNydC1zdXBlcmxvbmd0ZXN0Y3J0LXN1cGVybG9uZ3Rlc3RjcnQtc3 VwZXJsb25ndGVzdGNydC1zdXBlcmxvbmd0ZXN0Y3J0LXN1cGVybG9uZ3Rlc3RjcnQtc3Vw ZXJsb25ndGVzdGNydC0Kc3VwZXJsb25ndGVzdGNydC1zdXBlcmxvbmd0ZXN0Y3J0LXN1cG VybG9uZ3Rlc3RjcnQtc3VwZXJsb25ndGVzdGNydC1zdXBlcmxvbmd0ZXN0Y3J0LXN1cGVy bG9uZ3Rlc3RjcnQKc3VwZXJsb25ndGVzdGNydC1zdXBlcmxvbmd0ZXN0Y3J0LXN1cGVybG 9uZ3Rlc3RjcnQtc3VwZXJsb25ndGVzdGNydC1zdXBlcmxvbmd0ZXN0Y3J0LXN1cGVybG9u Z3Rlc3RjcnQ= --- apiVersion: v1 kind: Secret metadata: name: my-secret-vars namespace: my-ns type: opaque data: VAR1: bXlfc2VjcmV0X3Zhcl8x VAR2: bXlfc2VjcmV0X3Zhcl8y ELASTIC_FOOBAR_HUNTER123_MEOWTOWN_VERIFY: bXlfc2VjcmV0X3Zhcl8y stringData: str: | some big not so secret string with multiple lines --- apiVersion: v1 kind: ConfigMap metadata: name: my-config namespace: my-ns immutable: true data: dummyconfigmapkey: dummyconfigmapvalue my_config.properties: | health.healthProbeBindAddress=8081 metrics.bindAddress=127.0.0.1:8080 --- apiVersion: v1 kind: ConfigMap metadata: name: my-config-props namespace: my-ns data: my.prop1: "1" my.prop2: "val 1" my.prop3: "true" myval.yaml: | apiVersion: clickhouse.altinity.com/v1 kind: ClickHouseInstallationTemplate metadata: name: default-oneperhost-pod-template spec: templates: podTemplates: - name: default-oneperhost-pod-template distribution: "OnePerHost" --- apiVersion: apps/v1 kind: DaemonSet metadata: name: fluentd-elasticsearch namespace: kube-system labels: k8s-app: fluentd-logging spec: selector: matchLabels: name: fluentd-elasticsearch template: metadata: labels: name: fluentd-elasticsearch spec: tolerations: # this toleration is to have the daemonset runnable on master nodes # remove it if your masters can't run pods - key: node-role.kubernetes.io/master operator: Exists effect: NoSchedule containers: - name: fluentd-elasticsearch image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2 resources: limits: memory: 200Mi requests: cpu: 100m memory: 200Mi volumeMounts: - name: varlog mountPath: /var/log - name: varlibdockercontainers mountPath: /var/lib/docker/containers readOnly: true terminationGracePeriodSeconds: 30 volumes: - name: varlog hostPath: path: /var/log - name: varlibdockercontainers hostPath: path: /var/lib/docker/containers --- apiVersion: batch/v1 kind: Job metadata: name: batch-job spec: template: spec: containers: - name: pi image: perl:5.34.0 command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"] restartPolicy: Never backoffLimit: 4 --- apiVersion: batch/v1 kind: CronJob metadata: name: cron-job spec: schedule: "* * * * *" jobTemplate: spec: template: spec: containers: - name: hello image: busybox:1.28 imagePullPolicy: IfNotPresent command: - /bin/sh - -c - date; echo Hello from the Kubernetes cluster restartPolicy: OnFailure --- apiVersion: v1 kind: Service metadata: name: nginx labels: app: nginx spec: ports: - port: 80 name: web clusterIP: None selector: app: nginx --- apiVersion: apps/v1 kind: StatefulSet metadata: name: web spec: serviceName: "nginx" replicas: 2 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: registry.k8s.io/nginx-slim:0.8 ports: - containerPort: 80 name: web volumeMounts: - name: www mountPath: /usr/share/nginx/html volumeClaimTemplates: - metadata: name: www spec: accessModes: [ "ReadWriteOnce" ] resources: requests: storage: 1Gi --- apiVersion: policy/v1 kind: PodDisruptionBudget metadata: labels: app: nginx name: myapp-pdb spec: minAvailable: 2 selector: matchLabels: app: nginx 07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!869 blocks
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor