File helmify-0.4.18.obscpio of Package helmify
07070100000000000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000001700000000helmify-0.4.18/.github07070100000001000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000002100000000helmify-0.4.18/.github/workflows07070100000002000081A400000000000000000000000167C8625900000F8E000000000000000000000000000000000000002800000000helmify-0.4.18/.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
07070100000003000081A400000000000000000000000167C86259000001BF000000000000000000000000000000000000002D00000000helmify-0.4.18/.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 }}
07070100000004000081A400000000000000000000000167C86259000001A6000000000000000000000000000000000000001A00000000helmify-0.4.18/.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/
07070100000005000081A400000000000000000000000167C86259000005B5000000000000000000000000000000000000001E00000000helmify-0.4.18/.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
07070100000006000081A400000000000000000000000167C86259000004C1000000000000000000000000000000000000001F00000000helmify-0.4.18/.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: helmify07070100000007000081A400000000000000000000000167C8625900000427000000000000000000000000000000000000001700000000helmify-0.4.18/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.
07070100000008000081A400000000000000000000000167C8625900002C89000000000000000000000000000000000000001900000000helmify-0.4.18/README.md# Helmify
[](https://github.com/arttor/helmify/actions/workflows/ci.yml)



[](https://goreportcard.com/report/github.com/arttor/helmify)
[](https://pkg.go.dev/github.com/arttor/helmify?tab=doc)

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
07070100000009000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000001300000000helmify-0.4.18/cmd0707010000000A000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000001B00000000helmify-0.4.18/cmd/helmify0707010000000B000081A400000000000000000000000167C8625900001104000000000000000000000000000000000000002400000000helmify-0.4.18/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
}
0707010000000C000081A400000000000000000000000167C86259000001F0000000000000000000000000000000000000002300000000helmify-0.4.18/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)
}
}
0707010000000D000081A400000000000000000000000167C8625900000154000000000000000000000000000000000000002600000000helmify-0.4.18/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)
}
0707010000000E000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000001800000000helmify-0.4.18/examples0707010000000F000081A400000000000000000000000167C8625900000303000000000000000000000000000000000000002200000000helmify-0.4.18/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`07070100000010000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000001C00000000helmify-0.4.18/examples/app07070100000011000081A400000000000000000000000167C862590000015D000000000000000000000000000000000000002800000000helmify-0.4.18/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/
07070100000012000081A400000000000000000000000167C862590000046F000000000000000000000000000000000000002700000000helmify-0.4.18/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"
07070100000013000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000002600000000helmify-0.4.18/examples/app/templates07070100000014000081A400000000000000000000000167C86259000006CE000000000000000000000000000000000000003300000000helmify-0.4.18/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 }}
07070100000015000081A400000000000000000000000167C862590000028C000000000000000000000000000000000000003500000000helmify-0.4.18/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: Never
07070100000016000081A400000000000000000000000167C862590000033E000000000000000000000000000000000000003400000000helmify-0.4.18/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 }}
07070100000017000081A400000000000000000000000167C86259000005B6000000000000000000000000000000000000003500000000helmify-0.4.18/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: varlibdockercontainers
07070100000018000081A400000000000000000000000167C8625900000EEF000000000000000000000000000000000000003600000000helmify-0.4.18/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-claim
07070100000019000081A400000000000000000000000167C862590000018C000000000000000000000000000000000000003B00000000helmify-0.4.18/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 }}
0707010000001A000081A400000000000000000000000167C86259000001D6000000000000000000000000000000000000003500000000helmify-0.4.18/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 }}
0707010000001B000081A400000000000000000000000167C86259000001CC000000000000000000000000000000000000003E00000000helmify-0.4.18/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 }}
0707010000001C000081A400000000000000000000000167C8625900000109000000000000000000000000000000000000003800000000helmify-0.4.18/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: opaque
0707010000001D000081A400000000000000000000000167C86259000002A1000000000000000000000000000000000000003A00000000helmify-0.4.18/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: opaque
0707010000001E000081A400000000000000000000000167C86259000001CE000000000000000000000000000000000000003900000000helmify-0.4.18/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: Prefix
0707010000001F000081A400000000000000000000000167C86259000001D3000000000000000000000000000000000000003C00000000helmify-0.4.18/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 }}
07070100000020000081A400000000000000000000000167C8625900000186000000000000000000000000000000000000003500000000helmify-0.4.18/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 }}
07070100000021000081A400000000000000000000000167C8625900000162000000000000000000000000000000000000003900000000helmify-0.4.18/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 }}
07070100000022000081A400000000000000000000000167C862590000014C000000000000000000000000000000000000003100000000helmify-0.4.18/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 }}
07070100000023000081A400000000000000000000000167C862590000040E000000000000000000000000000000000000003700000000helmify-0.4.18/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 }}
07070100000024000081A400000000000000000000000167C86259000009DF000000000000000000000000000000000000002800000000helmify-0.4.18/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
07070100000025000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000002100000000helmify-0.4.18/examples/operator07070100000026000081A400000000000000000000000167C862590000015D000000000000000000000000000000000000002D00000000helmify-0.4.18/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/
07070100000027000081A400000000000000000000000167C8625900000474000000000000000000000000000000000000002C00000000helmify-0.4.18/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"
07070100000028000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000002B00000000helmify-0.4.18/examples/operator/templates07070100000029000081A400000000000000000000000167C8625900000700000000000000000000000000000000000000003800000000helmify-0.4.18/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 }}
0707010000002A000081A400000000000000000000000167C862590000278C000000000000000000000000000000000000003F00000000helmify-0.4.18/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: []
0707010000002B000081A400000000000000000000000167C86259000000D6000000000000000000000000000000000000003F00000000helmify-0.4.18/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 }}
0707010000002C000081A400000000000000000000000167C8625900001075000000000000000000000000000000000000003B00000000helmify-0.4.18/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 }}
strategy:
rollingUpdate:
maxSurge: {{ .Values.controllerManager.strategy.rollingUpdate.maxSurge | quote
}}
maxUnavailable: {{ .Values.controllerManager.strategy.rollingUpdate.maxUnavailable
| quote }}
type: {{ .Values.controllerManager.strategy.type | quote }}
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-ca
0707010000002D000081A400000000000000000000000167C86259000003EB000000000000000000000000000000000000004500000000helmify-0.4.18/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 }}'
0707010000002E000081A400000000000000000000000167C8625900000168000000000000000000000000000000000000004800000000helmify-0.4.18/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: []
0707010000002F000081A400000000000000000000000167C8625900000162000000000000000000000000000000000000003F00000000helmify-0.4.18/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 }}
07070100000030000081A400000000000000000000000167C86259000006A6000000000000000000000000000000000000003D00000000helmify-0.4.18/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 }}'
07070100000031000081A400000000000000000000000167C8625900000C8B000000000000000000000000000000000000004700000000helmify-0.4.18/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: []
07070100000032000081A400000000000000000000000167C86259000000F3000000000000000000000000000000000000004400000000helmify-0.4.18/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:
- get
07070100000033000081A400000000000000000000000167C86259000001B8000000000000000000000000000000000000004000000000helmify-0.4.18/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 }}
07070100000034000081A400000000000000000000000167C862590000031B000000000000000000000000000000000000004F00000000helmify-0.4.18/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: None
07070100000035000081A400000000000000000000000167C8625900000346000000000000000000000000000000000000003B00000000helmify-0.4.18/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 }}'
07070100000036000081A400000000000000000000000167C8625900000167000000000000000000000000000000000000003800000000helmify-0.4.18/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 }}
07070100000037000081A400000000000000000000000167C862590000010C000000000000000000000000000000000000003A00000000helmify-0.4.18/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: opaque
07070100000038000081A400000000000000000000000167C8625900000179000000000000000000000000000000000000004C00000000helmify-0.4.18/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/dockerconfigjson
07070100000039000081A400000000000000000000000167C8625900000170000000000000000000000000000000000000003C00000000helmify-0.4.18/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: opaque
0707010000003A000081A400000000000000000000000167C86259000000C6000000000000000000000000000000000000004200000000helmify-0.4.18/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: {}
0707010000003B000081A400000000000000000000000167C862590000010A000000000000000000000000000000000000003F00000000helmify-0.4.18/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 }}
0707010000003C000081A400000000000000000000000167C862590000021B000000000000000000000000000000000000003D00000000helmify-0.4.18/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-cert
0707010000003D000081A400000000000000000000000167C8625900000330000000000000000000000000000000000000005100000000helmify-0.4.18/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: None
0707010000003E000081A400000000000000000000000167C862590000017F000000000000000000000000000000000000004000000000helmify-0.4.18/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 }}
0707010000003F000081A400000000000000000000000167C8625900000830000000000000000000000000000000000000002D00000000helmify-0.4.18/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.
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
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
07070100000040000081A400000000000000000000000167C8625900001982000000000000000000000000000000000000001600000000helmify-0.4.18/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
)
07070100000041000081A400000000000000000000000167C8625900019CD2000000000000000000000000000000000000001600000000helmify-0.4.18/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=
07070100000042000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000001800000000helmify-0.4.18/internal07070100000043000081A400000000000000000000000167C86259000002C5000000000000000000000000000000000000002600000000helmify-0.4.18/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
}
07070100000044000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000001300000000helmify-0.4.18/pkg07070100000045000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000001700000000helmify-0.4.18/pkg/app07070100000046000081A400000000000000000000000167C8625900000A0F000000000000000000000000000000000000001E00000000helmify-0.4.18/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)
}
}
07070100000047000081A400000000000000000000000167C8625900000527000000000000000000000000000000000000002700000000helmify-0.4.18/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)
}
}
07070100000048000081A400000000000000000000000167C8625900000C59000000000000000000000000000000000000002200000000helmify-0.4.18/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
}
07070100000049000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000001B00000000helmify-0.4.18/pkg/cluster0707010000004A000081A400000000000000000000000167C8625900000094000000000000000000000000000000000000002500000000helmify-0.4.18/pkg/cluster/domain.gopackage cluster
const (
DefaultDomain = "cluster.local"
DomainKey = "kubernetesClusterDomain"
DomainEnv = "KUBERNETES_CLUSTER_DOMAIN"
)
0707010000004B000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000001A00000000helmify-0.4.18/pkg/config0707010000004C000081A400000000000000000000000167C86259000007F8000000000000000000000000000000000000002400000000helmify-0.4.18/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
}
0707010000004D000081A400000000000000000000000167C8625900000535000000000000000000000000000000000000002900000000helmify-0.4.18/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)
})
}
0707010000004E000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000001B00000000helmify-0.4.18/pkg/decoder0707010000004F000081A400000000000000000000000167C8625900000782000000000000000000000000000000000000002600000000helmify-0.4.18/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
}
07070100000050000081A400000000000000000000000167C86259000006BC000000000000000000000000000000000000002B00000000helmify-0.4.18/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")
}
07070100000051000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000001800000000helmify-0.4.18/pkg/file07070100000052000081A400000000000000000000000167C862590000079A000000000000000000000000000000000000002200000000helmify-0.4.18/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
}
}
}
07070100000053000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000001A00000000helmify-0.4.18/pkg/format07070100000054000081A400000000000000000000000167C8625900000265000000000000000000000000000000000000002800000000helmify-0.4.18/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()
}
07070100000055000081A400000000000000000000000167C8625900000703000000000000000000000000000000000000002D00000000helmify-0.4.18/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)
}
})
}
}
07070100000056000081A400000000000000000000000167C86259000000C4000000000000000000000000000000000000003200000000helmify-0.4.18/pkg/format/trailing_whitespaces.gopackage format
import "regexp"
var removeWhitespace = regexp.MustCompile(`(\s+)(\n|$)`)
func RemoveTrailingWhitespaces(in string) string {
return removeWhitespace.ReplaceAllString(in, "$2")
}
07070100000057000081A400000000000000000000000167C862590000027A000000000000000000000000000000000000003700000000helmify-0.4.18/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)
}
})
}
}
07070100000058000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000001800000000helmify-0.4.18/pkg/helm07070100000059000081A400000000000000000000000167C8625900000FB0000000000000000000000000000000000000002100000000helmify-0.4.18/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)
}
}
}
if len(templates) != 0 {
_, err = f.Write([]byte("\n"))
if err != nil {
return fmt.Errorf("%w: unable to write newline 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
}
0707010000005A000081A400000000000000000000000167C8625900000060000000000000000000000000000000000000001F00000000helmify-0.4.18/pkg/helm/doc.go// Package helm contains code for writing templates to a filesystem as Helm chart.
package helm
0707010000005B000081A400000000000000000000000167C862590000178B000000000000000000000000000000000000002000000000helmify-0.4.18/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))
}
0707010000005C000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000001B00000000helmify-0.4.18/pkg/helmify0707010000005D000081A400000000000000000000000167C862590000074C000000000000000000000000000000000000002400000000helmify-0.4.18/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
}
0707010000005E000081A400000000000000000000000167C8625900000BE8000000000000000000000000000000000000002500000000helmify-0.4.18/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
}
0707010000005F000081A400000000000000000000000167C8625900000952000000000000000000000000000000000000002A00000000helmify-0.4.18/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")
})
}
07070100000060000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000001C00000000helmify-0.4.18/pkg/metadata07070100000061000081A400000000000000000000000167C8625900000CF6000000000000000000000000000000000000002800000000helmify-0.4.18/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])
}
07070100000062000081A400000000000000000000000167C8625900000CD8000000000000000000000000000000000000002D00000000helmify-0.4.18/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)
}
07070100000063000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000001D00000000helmify-0.4.18/pkg/processor07070100000064000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000002700000000helmify-0.4.18/pkg/processor/configmap07070100000065000081A400000000000000000000000167C8625900001233000000000000000000000000000000000000003400000000helmify-0.4.18/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)
}
07070100000066000081A400000000000000000000000167C86259000003F5000000000000000000000000000000000000003900000000helmify-0.4.18/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)
})
}
07070100000067000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000002100000000helmify-0.4.18/pkg/processor/crd07070100000068000081A400000000000000000000000167C8625900001097000000000000000000000000000000000000002800000000helmify-0.4.18/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
}
07070100000069000081A400000000000000000000000167C8625900000436000000000000000000000000000000000000002D00000000helmify-0.4.18/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)
})
}
0707010000006A000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000002700000000helmify-0.4.18/pkg/processor/daemonset0707010000006B000081A400000000000000000000000167C8625900000F42000000000000000000000000000000000000003400000000helmify-0.4.18/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)
}
0707010000006C000081A400000000000000000000000167C8625900000741000000000000000000000000000000000000003900000000helmify-0.4.18/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)
})
}
0707010000006D000081A400000000000000000000000167C86259000006DC000000000000000000000000000000000000002800000000helmify-0.4.18/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
}
0707010000006E000081A400000000000000000000000167C8625900000473000000000000000000000000000000000000002D00000000helmify-0.4.18/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)
})
}
0707010000006F000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000002800000000helmify-0.4.18/pkg/processor/deployment07070100000070000081A400000000000000000000000167C8625900002377000000000000000000000000000000000000003600000000helmify-0.4.18/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"
"k8s.io/apimachinery/pkg/util/intstr"
)
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 }}
{{- if .Strategy }}
{{ .Strategy }}
{{- 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
}
strategy, err := processStrategy(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
Strategy string
Selector string
PodLabels string
PodAnnotations string
Spec string
}{
Meta: meta,
Replicas: replicas,
RevisionHistoryLimit: revisionHistoryLimit,
Strategy: strategy,
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
}
func processStrategy(name string, deployment *appsv1.Deployment, values *helmify.Values) (string, error) {
if deployment.Spec.Strategy.Type == "" {
return "", nil
}
allowedStrategyTypes := map[appsv1.DeploymentStrategyType]bool{
appsv1.RecreateDeploymentStrategyType: true,
appsv1.RollingUpdateDeploymentStrategyType: true,
}
if !allowedStrategyTypes[deployment.Spec.Strategy.Type] {
return "", fmt.Errorf("invalid deployment strategy type: %s", deployment.Spec.Strategy.Type)
}
strategyTypeTpl, err := values.Add(string(deployment.Spec.Strategy.Type), name, "strategy", "type")
if err != nil {
return "", err
}
strategyMap := map[string]interface{}{
"type": strategyTypeTpl,
}
if deployment.Spec.Strategy.Type == appsv1.RollingUpdateDeploymentStrategyType {
if rollingUpdate := deployment.Spec.Strategy.RollingUpdate; rollingUpdate != nil {
rollingUpdateMap := map[string]interface{}{}
setRollingUpdateField := func(value *intstr.IntOrString, fieldName string) error {
var tpl string
var err error
if value.Type == intstr.Int {
tpl, err = values.Add(value.IntValue(), name, "strategy", "rollingUpdate", fieldName)
} else {
tpl, err = values.Add(value.String(), name, "strategy", "rollingUpdate", fieldName)
}
if err != nil {
return err
}
rollingUpdateMap[fieldName] = tpl
return nil
}
if rollingUpdate.MaxSurge != nil {
if err := setRollingUpdateField(rollingUpdate.MaxSurge, "maxSurge"); err != nil {
return "", err
}
}
if rollingUpdate.MaxUnavailable != nil {
if err := setRollingUpdateField(rollingUpdate.MaxUnavailable, "maxUnavailable"); err != nil {
return "", err
}
}
strategyMap["rollingUpdate"] = rollingUpdateMap
}
}
strategy, err := yamlformat.Marshal(map[string]interface{}{"strategy": strategyMap}, 2)
if err != nil {
return "", err
}
strategy = strings.ReplaceAll(strategy, "'", "")
return strategy, nil
}
type result struct {
data struct {
Meta string
Replicas string
RevisionHistoryLimit string
Strategy 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)
}
07070100000071000081A400000000000000000000000167C8625900001302000000000000000000000000000000000000003B00000000helmify-0.4.18/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
strategy:
type: Recreate
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)
}
})
}
}
07070100000072000081A400000000000000000000000167C8625900000063000000000000000000000000000000000000002400000000helmify-0.4.18/pkg/processor/doc.go// Package processor contains processors converting k8s objects to Helm template
package processor
07070100000073000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000002100000000helmify-0.4.18/pkg/processor/job07070100000074000081A400000000000000000000000167C8625900000F7B000000000000000000000000000000000000002900000000helmify-0.4.18/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)
}
07070100000075000081A400000000000000000000000167C8625900000449000000000000000000000000000000000000002E00000000helmify-0.4.18/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)
})
}
07070100000076000081A400000000000000000000000167C862590000112E000000000000000000000000000000000000002800000000helmify-0.4.18/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
}
07070100000077000081A400000000000000000000000167C86259000003A0000000000000000000000000000000000000002D00000000helmify-0.4.18/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)
})
}
07070100000078000081A400000000000000000000000167C8625900000B88000000000000000000000000000000000000002500000000helmify-0.4.18/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
}
07070100000079000081A400000000000000000000000167C8625900000202000000000000000000000000000000000000002A00000000helmify-0.4.18/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")
}
0707010000007A000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000002100000000helmify-0.4.18/pkg/processor/pod0707010000007B000081A400000000000000000000000167C862590000245B000000000000000000000000000000000000002800000000helmify-0.4.18/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
}
0707010000007C000081A400000000000000000000000167C862590000228E000000000000000000000000000000000000002D00000000helmify-0.4.18/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)
})
}
0707010000007D000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000003100000000helmify-0.4.18/pkg/processor/poddisruptionbudget0707010000007E000081A400000000000000000000000167C86259000009D6000000000000000000000000000000000000003800000000helmify-0.4.18/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
}
0707010000007F000081A400000000000000000000000167C86259000003CE000000000000000000000000000000000000003D00000000helmify-0.4.18/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)
})
}
07070100000080000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000002200000000helmify-0.4.18/pkg/processor/rbac07070100000081000081A400000000000000000000000167C86259000009FB000000000000000000000000000000000000003800000000helmify-0.4.18/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)
}
07070100000082000081A400000000000000000000000167C8625900000408000000000000000000000000000000000000003D00000000helmify-0.4.18/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)
})
}
07070100000083000081A400000000000000000000000167C8625900000A65000000000000000000000000000000000000002A00000000helmify-0.4.18/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)
}
07070100000084000081A400000000000000000000000167C86259000003E9000000000000000000000000000000000000002F00000000helmify-0.4.18/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)
})
}
07070100000085000081A400000000000000000000000167C8625900000998000000000000000000000000000000000000003100000000helmify-0.4.18/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)
}
07070100000086000081A400000000000000000000000167C862590000040E000000000000000000000000000000000000003600000000helmify-0.4.18/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)
})
}
07070100000087000081A400000000000000000000000167C862590000054B000000000000000000000000000000000000003400000000helmify-0.4.18/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
}
07070100000088000081A400000000000000000000000167C862590000032D000000000000000000000000000000000000003900000000helmify-0.4.18/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)
})
}
07070100000089000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000002400000000helmify-0.4.18/pkg/processor/secret0707010000008A000081A400000000000000000000000167C8625900000EC9000000000000000000000000000000000000002E00000000helmify-0.4.18/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)
}
0707010000008B000081A400000000000000000000000167C862590000036F000000000000000000000000000000000000003300000000helmify-0.4.18/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)
})
}
0707010000008C000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000002E00000000helmify-0.4.18/pkg/processor/security-context0707010000008D000081A400000000000000000000000167C8625900000821000000000000000000000000000000000000004C00000000helmify-0.4.18/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
}
0707010000008E000081A400000000000000000000000167C8625900000E27000000000000000000000000000000000000005100000000helmify-0.4.18/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)
})
}
}
0707010000008F000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000002500000000helmify-0.4.18/pkg/processor/service07070100000090000081A400000000000000000000000167C8625900000A3B000000000000000000000000000000000000003000000000helmify-0.4.18/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)
}
07070100000091000081A400000000000000000000000167C8625900000417000000000000000000000000000000000000003500000000helmify-0.4.18/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)
})
}
07070100000092000081A400000000000000000000000167C8625900000F45000000000000000000000000000000000000003000000000helmify-0.4.18/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
}
07070100000093000081A400000000000000000000000167C86259000003B3000000000000000000000000000000000000003500000000helmify-0.4.18/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)
})
}
07070100000094000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000002900000000helmify-0.4.18/pkg/processor/statefulset07070100000095000081A400000000000000000000000167C86259000010CE000000000000000000000000000000000000003800000000helmify-0.4.18/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)
}
07070100000096000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000002500000000helmify-0.4.18/pkg/processor/storage07070100000097000081A400000000000000000000000167C8625900000CA3000000000000000000000000000000000000002C00000000helmify-0.4.18/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)
}
07070100000098000081A400000000000000000000000167C862590000036B000000000000000000000000000000000000003100000000helmify-0.4.18/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)
})
}
07070100000099000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000002500000000helmify-0.4.18/pkg/processor/webhook0707010000009A000081A400000000000000000000000167C8625900000E0D000000000000000000000000000000000000002D00000000helmify-0.4.18/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
}
0707010000009B000081A400000000000000000000000167C8625900000405000000000000000000000000000000000000003200000000helmify-0.4.18/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)
})
}
0707010000009C000081A400000000000000000000000167C86259000008C3000000000000000000000000000000000000002F00000000helmify-0.4.18/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
}
0707010000009D000081A400000000000000000000000167C862590000032E000000000000000000000000000000000000003400000000helmify-0.4.18/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)
})
}
0707010000009E000081A400000000000000000000000167C8625900000B9C000000000000000000000000000000000000003100000000helmify-0.4.18/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
}
0707010000009F000081A400000000000000000000000167C8625900000526000000000000000000000000000000000000003600000000helmify-0.4.18/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)
})
}
070701000000A0000081A400000000000000000000000167C8625900000BAC000000000000000000000000000000000000003300000000helmify-0.4.18/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
}
070701000000A1000081A400000000000000000000000167C862590000052C000000000000000000000000000000000000003800000000helmify-0.4.18/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)
})
}
070701000000A2000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000001800000000helmify-0.4.18/pkg/yaml070701000000A3000081A400000000000000000000000167C862590000029B000000000000000000000000000000000000002000000000helmify-0.4.18/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
}
070701000000A4000081A400000000000000000000000167C86259000002E6000000000000000000000000000000000000002500000000helmify-0.4.18/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)
}
})
}
}
070701000000A5000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000001900000000helmify-0.4.18/test_data070701000000A6000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000001D00000000helmify-0.4.18/test_data/dir070701000000A7000041ED00000000000000000000000267C8625900000000000000000000000000000000000000000000002900000000helmify-0.4.18/test_data/dir/another_dir070701000000A8000081A400000000000000000000000167C8625900000288000000000000000000000000000000000000003B00000000helmify-0.4.18/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: 1Gi070701000000A9000081A400000000000000000000000167C86259000002B2000000000000000000000000000000000000002900000000helmify-0.4.18/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"070701000000AA000081A400000000000000000000000167C86259000009D3000000000000000000000000000000000000002D00000000helmify-0.4.18/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-claim070701000000AB000081A400000000000000000000000167C8625900000370000000000000000000000000000000000000002900000000helmify-0.4.18/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 lines070701000000AC000081A400000000000000000000000167C8625900000249000000000000000000000000000000000000002A00000000helmify-0.4.18/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: 8443070701000000AD000081A400000000000000000000000167C86259000000E0000000000000000000000000000000000000002A00000000helmify-0.4.18/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: 5Gi070701000000AE000081A400000000000000000000000167C86259000052E8000000000000000000000000000000000000002E00000000helmify-0.4.18/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
070701000000AF000081A400000000000000000000000167C8625900006090000000000000000000000000000000000000003700000000helmify-0.4.18/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
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
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
070701000000B0000081A400000000000000000000000167C86259000021BE000000000000000000000000000000000000002900000000helmify-0.4.18/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!!!875 blocks