diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..512c302 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +root = true + +[*] +insert_final_newline = true +charset = utf-8 +trim_trailing_whitespace = true +indent_style = space +indent_size = 2 + +[{Makefile,go.mod,go.sum,*.go,.gitmodules}] +indent_style = tab +indent_size = 4 + +[*.md] +indent_size = 4 +trim_trailing_whitespace = false + +[Taskfile.yaml] +indent_style = tab diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml new file mode 100644 index 0000000..a1267f6 --- /dev/null +++ b/.github/workflows/lint.yaml @@ -0,0 +1,44 @@ +name: Lint + +on: + workflow_dispatch: + pull_request: + branches: [ main ] + push: + branches: + - main + paths-ignore: + - 'README.md' + - 'docs/**' + - '.github/**' + - 'LICENSE' + - 'Taskfile.yaml' + - '.gitignore' + - '.gosec.config.json' + - '.editorconfig' + - '.goreleaser.yaml' + +jobs: + lint: + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v3.5.3 + + - name: Set up Go + uses: actions/setup-go@v4.1.0 + with: + go-version: '1.20' + + - name: Install Task + uses: arduino/setup-task@v1.0.3 + with: + version: 3.x + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Install Linter + run: task tools:install:golangci-lint + + - name: Lint + run: task lint diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..72b5e0c --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,56 @@ +name: Release + +on: + workflow_dispatch: + inputs: + semver: + type: string + description: 'Semver (eg: v1.2.3)' + required: true +permissions: + contents: write +jobs: + release: + if: github.triggering_actor == 'zcubbs' + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v3.5.3 + + - name: Set up Go + uses: actions/setup-go@v4.1.0 + with: + go-version: '1.21' + + # remove tests in order to clean dependencies + - name: Remove xxx_test.go files + run: rm -rf *_test.go ./examples ./images + + # cleanup test dependencies + - name: Cleanup dependencies + run: go mod tidy + + - name: List files + run: tree -Cfi + - name: Write new go.mod into logs + run: cat go.mod + - name: Write new go.sum into logs + run: cat go.sum + + - name: Create tag + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + git config --global user.name '${{ github.triggering_actor }}' + git config --global user.email "${{ github.triggering_actor}}@users.noreply.github.com" + + git add . + git commit --allow-empty -m 'bump ${{ inputs.semver }}' + git tag ${{ inputs.semver }} + git push origin ${{ inputs.semver }} + + - name: Release + uses: softprops/action-gh-release@v1 + with: + name: ${{ inputs.semver }} + tag_name: ${{ inputs.semver }} diff --git a/.github/workflows/scan.yaml b/.github/workflows/scan.yaml new file mode 100644 index 0000000..2d9e052 --- /dev/null +++ b/.github/workflows/scan.yaml @@ -0,0 +1,44 @@ +name: Scan + +on: + workflow_dispatch: + pull_request: + branches: [ main ] + push: + branches: + - main + paths-ignore: + - 'README.md' + - 'docs/**' + - '.github/**' + - 'LICENSE' + - 'Taskfile.yaml' + - '.gitignore' + - '.gosec.config.json' + - '.editorconfig' + - '.goreleaser.yaml' + +jobs: + scan: + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v3.5.3 + + - name: Set up Go + uses: actions/setup-go@v4.1.0 + with: + go-version: '1.20' + + - name: Install Task + uses: arduino/setup-task@v1.0.3 + with: + version: 3.x + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Install Security Scanner + run: task tools:install:gosec + + - name: Run Go Security Scanner + run: task scan diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..aa84ebb --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,47 @@ +name: Test + +on: + workflow_dispatch: + pull_request: + branches: [ main ] + push: + branches: + - main + paths-ignore: + - 'README.md' + - 'docs/**' + - '.github/**' + - 'LICENSE' + - 'Taskfile.yaml' + - '.gitignore' + - '.gosec.config.json' + - '.editorconfig' + - '.goreleaser.yaml' + +jobs: + test: + runs-on: ubuntu-latest + services: + mailhog: + image: mailhog/mailhog + ports: + - 1025:1025 + - 8025:8025 + + steps: + - name: Checkout Code + uses: actions/checkout@v3.5.3 + + - name: Set up Go + uses: actions/setup-go@v4.1.0 + with: + go-version: '1.20' + + - name: Install Task + uses: arduino/setup-task@v1.0.3 + with: + version: 3.x + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Test + run: task test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c749986 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/.idea/ +*.iml diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 0000000..cd83e79 --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,9 @@ +linters-settings: + gosec: + # To specify a set of rules to explicitly exclude. + # Available rules: https://github.com/securego/gosec#available-rules + excludes: + - G204 # Audit use of command execution + - G304 # File path provided as taint input + - G303 # Creating tempfile using a predictable path + - G306 # Poor file permissions used when writing to a new file diff --git a/.gosec.config.json b/.gosec.config.json new file mode 100644 index 0000000..7339e33 --- /dev/null +++ b/.gosec.config.json @@ -0,0 +1,5 @@ +{ + "global": { + "exclude": "G204,G302,G303,G304,G306,G404,G402" + } +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ed0644f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Zakaria EL BOUWAB + +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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..320ee50 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# Go K8s + +A K8s collection of Go Packages. + +[![tag](https://img.shields.io/github/tag/zcubbs/go-k8s)](https://github.com/zcubbs/go-k8s/releases) +![Go Version](https://img.shields.io/badge/Go-%3E%3D%201.21-%23007d9c) +[![GoDoc](https://godoc.org/github.com/zcubbs/go-k8s?status.svg)](https://pkg.go.dev/github.com/zcubbs/go-k8s) +[![Lint](https://github.com/zcubbs/go-k8s/actions/workflows/lint.yaml/badge.svg)](https://github.com/zcubbs/go-k8s/actions/workflows/lint.yaml) +[![Scan](https://github.com/zcubbs/go-k8s/actions/workflows/scan.yaml/badge.svg?branch=main)](https://github.com/zcubbs/go-k8s/actions/workflows/scan.yaml) +![Build Status](https://github.com/zcubbs/go-k8s/actions/workflows/test.yaml/badge.svg) +[![Go Report Card](https://goreportcard.com/badge/github.com/zcubbs/go-k8s)](https://goreportcard.com/report/github.com/zcubbs/go-k8s) +[![Contributors](https://img.shields.io/github/contributors/zcubbs/go-k8s)](https://github.com/zcubbs/go-k8s/graphs/contributors) +[![License](https://img.shields.io/github/license/zcubbs/go-k8s.svg)](./LICENSE) diff --git a/Taskfile.yaml b/Taskfile.yaml new file mode 100644 index 0000000..47c2d68 --- /dev/null +++ b/Taskfile.yaml @@ -0,0 +1,41 @@ +version: '3' + +env: + GOOS: "{{OS}}" + +tasks: + build: + desc: Build Go Binaries + cmds: + - go build -v ./... + + test: + desc: Run tests + cmds: + - go test -v ./... + + lint: + desc: Run linter + cmds: + - golangci-lint run ./... -v --timeout 5m + + scan: + desc: Run security scan + cmds: + - gosec -conf .gosec.config.json "./..." + + tools: + desc: Install tools + cmds: + - task: tools:install:golangci-lint + - task: tools:install:gosec + + tools:install:golangci-lint: + desc: Install golangci-lint + cmds: + - go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest + + tools:install:gosec: + desc: Install gosec + cmds: + - go install github.com/securego/gosec/v2/cmd/gosec@latest diff --git a/argocd/application.go b/argocd/application.go new file mode 100644 index 0000000..3ba6827 --- /dev/null +++ b/argocd/application.go @@ -0,0 +1,145 @@ +package argocd + +import ( + "fmt" + "github.com/zcubbs/go-k8s/kubernetes" + "github.com/zcubbs/x/pretty" +) + +type Application struct { + Name string `json:"name"` + Namespace string `json:"namespace"` + IsOCI bool `json:"isOCI"` + OCIChartName string `json:"ociChartName"` + OCIChartRevision string `json:"ociChartRevision"` + OCIRepoURL string `json:"ociRepoURL"` + IsHelm bool `json:"isHelm"` + HelmValueFiles []string `json:"helmValueFiles"` + Project string `json:"project"` + RepoURL string `json:"repoURL"` + TargetRevision string `json:"targetRevision"` + Path string `json:"path"` + Recurse bool `json:"recurse"` + CreateNamespace bool `json:"createNamespace"` + Prune bool `json:"prune"` + SelfHeal bool `json:"selfHeal"` + AllowEmpty bool `json:"allowEmpty"` + + ArgoNamespace string `json:"argoNamespace"` +} + +func CreateApplication(app Application, _ string, debug bool) error { + if err := validateApp(&app); err != nil { + return err + } + + if debug { + pretty.PrintJson(app) + } + + // create app + if app.IsOCI { + // Apply template + err := kubernetes.ApplyManifest(argoAppOciTmpl, app, debug) + if err != nil { + return fmt.Errorf("failed to create application: %s, %w", app.Name, err) + } + return nil + } + + return kubernetes.ApplyManifest(argoAppTmpl, app, debug) +} + +func validateApp(app *Application) error { + if !app.IsHelm && app.IsOCI { + return fmt.Errorf("oci flag can only be used with helm charts. helm is false") + } + + if app.IsOCI && app.OCIChartName == "" { + return fmt.Errorf("oci chart name cannot be empty, when oci is true") + } + + if (!app.IsOCI && !app.IsHelm) && app.Path == "" { + return fmt.Errorf("path cannot be empty, when helm is false") + } + + if app.ArgoNamespace == "" { + app.ArgoNamespace = argocdNamespace + } + + if app.Namespace == "" { + return fmt.Errorf("namespace cannot be empty") + } + + return nil +} + +var argoAppTmpl = `--- + +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: {{ .Name }} + namespace: {{ .ArgoNamespace }} +spec: + project: {{ .Project }} + source: + repoURL: {{ .RepoURL }} + targetRevision: {{ .TargetRevision }} + path: {{ .Path }} + {{ if .IsHelm }} + helm: + passCredentials: true + valueFiles: + {{- range .HelmValueFiles }} + - {{ . }} + {{- end }} + {{ else }} + directory: + recurse: {{ .Recurse }} + {{ end }} + destination: + server: https://kubernetes.default.svc + namespace: {{ .Namespace }} + syncPolicy: + syncOptions: + - CreateNamespace={{ .CreateNamespace }} + automated: + prune: {{ .Prune }} + selfHeal: {{ .SelfHeal }} + allowEmpty: {{ .AllowEmpty }} +` + +var argoAppOciTmpl = `--- + +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: {{ .AppName }} + namespace: {{ .ArgoNamespace }} +spec: + project: {{ .Project }} + sources: + - repoURL: {{ .OciRepoURL }} + targetRevision: {{ .OciChartRevision }} + chart: {{ .OciChartName }} + helm: + passCredentials: true + valueFiles: + {{- range .HelmValueFiles }} + - {{ . }} + {{- end }} + - repoURL: {{ .RepoURL }} + targetRevision: {{ .TargetRevision }} + ref: values + destination: + server: https://kubernetes.default.svc + namespace: {{ .AppNamespace }} + syncPolicy: + syncOptions: + - CreateNamespace={{ .CreateNamespace }} + automated: + prune: {{ .Prune }} + selfHeal: {{ .SelfHeal }} + allowEmpty: {{ .AllowEmpty }} +` diff --git a/argocd/argocd.go b/argocd/argocd.go new file mode 100644 index 0000000..1f04490 --- /dev/null +++ b/argocd/argocd.go @@ -0,0 +1,179 @@ +package argocd + +import ( + "context" + "fmt" + "github.com/zcubbs/go-k8s/helm" + "github.com/zcubbs/go-k8s/kubernetes" + "golang.org/x/crypto/bcrypt" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "time" +) + +const ( + argocdChartName = "argo-cd" + argocdHelmRepoName = "argocd" + argocdHelmRepoURL = "https://argoproj.github.io/argo-helm" + argocdChartVersion = "" // latest + argocdNamespace = "argocd" +) + +const ( + argocdServerDeploymentName = "argo-cd-argocd-server" + argocdRepoServerDeploymentName = "argo-cd-argocd-repo-server" + argocdRedisDeploymentName = "argo-cd-argocd-redis" + argocdDexServerDeploymentName = "argo-cd-argocd-dex-server" + argocdApplicationsetControllerDeploymentName = "argo-cd-argocd-applicationset-controller" + argocdNotificationsControllerDeploymentName = "argo-cd-argocd-notifications-controller" +) + +func Install(values Values, kubeconfig string, debug bool) error { + if err := validateValues(values); err != nil { + return err + } + + vals := map[string]interface{}{ + "configs.params.server\\.insecure": values.Insecure, + } + + err := helm.Install(helm.Chart{ + Name: argocdChartName, + Repo: argocdHelmRepoName, + URL: argocdHelmRepoURL, + Version: values.ChartVersion, + Values: vals, + ValuesFiles: nil, + Namespace: argocdNamespace, + Upgrade: true, + CreateNamespace: true, + }, kubeconfig, debug) + if err != nil { + return err + } + + // wait for argocd server to be ready + ctxWithTimeout, cancel := context.WithTimeout(context.Background(), 3*time.Minute) + defer cancel() + err = kubernetes.IsDeploymentReady( + ctxWithTimeout, + kubeconfig, + argocdNamespace, + []string{ + argocdServerDeploymentName, + argocdRepoServerDeploymentName, + argocdRedisDeploymentName, + argocdDexServerDeploymentName, + argocdApplicationsetControllerDeploymentName, + argocdNotificationsControllerDeploymentName, + }, + debug, + ) + if err != nil { + return fmt.Errorf("failed to wait for argocd server to be ready \n %w", err) + } + + return nil +} + +func Uninstall(kubeconfig string, debug bool) error { + return helm.Uninstall(helm.Chart{ + Name: argocdChartName, + Namespace: argocdNamespace, + }, kubeconfig, debug) +} + +type Values struct { + Insecure bool + ChartVersion string + AdminPassword string +} + +const patchPasswordAnnotation = "patched-password" + +func PatchPassword(values Values, kubeconfig string, debug bool) error { + secret, err := kubernetes.GetSecret(kubeconfig, argocdNamespace, "argocd-secret") + if err != nil { + return fmt.Errorf("failed to get argocd-secret: %w", err) + } + + if _, ok := secret.Annotations[patchPasswordAnnotation]; ok { + if debug { + fmt.Println("argocd-secret already patched") + } + + return nil + } + + hashedPassword, err := hashPassword(values.AdminPassword) + if err != nil { + return fmt.Errorf("failed to hash password: %w", err) + } + err = kubernetes.CreateGenericSecret( + context.Background(), + kubeconfig, + v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argocd-secret", + Namespace: argocdNamespace, + Annotations: map[string]string{ + patchPasswordAnnotation: "true", + }, + }, + StringData: map[string]string{ + "admin.password": hashedPassword, + "admin.passwordMtime": "'$(date +%FT%T%Z)'", + }, + }, + []string{argocdNamespace}, + true, + debug, + ) + if err != nil { + return fmt.Errorf("failed to create argocd-secret: %w", err) + } + + err = kubernetes.RestartPods(kubeconfig, argocdNamespace, + []string{ + argocdServerDeploymentName, + argocdDexServerDeploymentName, + argocdRepoServerDeploymentName, + argocdRedisDeploymentName, + argocdApplicationsetControllerDeploymentName, + argocdNotificationsControllerDeploymentName, + }, + debug) + if err != nil { + return fmt.Errorf("failed to restart argocd server pod: %w", err) + } + + // wait for argocd server to be ready + ctxWithTimeout, cancel := context.WithTimeout(context.Background(), 3*time.Minute) + defer cancel() + err = kubernetes.IsDeploymentReady( + ctxWithTimeout, + kubeconfig, + argocdNamespace, + []string{ + argocdServerDeploymentName, + }, + debug, + ) + if err != nil { + return fmt.Errorf("failed to wait for argocd server to be ready \n %w", err) + } + + return nil +} + +func hashPassword(password string) (string, error) { + bytes, err := bcrypt.GenerateFromPassword([]byte(password), 10) + return string(bytes), err +} + +func validateValues(values Values) error { + if values.ChartVersion == "" { + values.ChartVersion = argocdChartVersion + } + return nil +} diff --git a/argocd/project.go b/argocd/project.go new file mode 100644 index 0000000..466e13c --- /dev/null +++ b/argocd/project.go @@ -0,0 +1,43 @@ +package argocd + +import ( + "fmt" + "github.com/zcubbs/go-k8s/kubernetes" +) + +type Project struct { + Name string `mapstructure:"name" json:"name" yaml:"name"` + Namespace string `mapstructure:"namespace" json:"namespace" yaml:"namespace"` +} + +func CreateProject(project Project, _ string, debug bool) error { + project.Namespace = argocdNamespace + // Apply template + err := kubernetes.ApplyManifest(projectTmpl, project, debug) + if err != nil { + return fmt.Errorf("failed to create project: %w", err) + } + return nil +} + +var projectTmpl = `--- + +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: {{ .Name }} + namespace: {{ .Namespace }} + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + description: {{ .Name }} + sourceRepos: + - '*' + clusterResourceWhitelist: + - group: '*' + kind: '*' + destinations: + - namespace: '*' + server: https://kubernetes.default.svc + +` diff --git a/argocd/repository.go b/argocd/repository.go new file mode 100644 index 0000000..042cc27 --- /dev/null +++ b/argocd/repository.go @@ -0,0 +1,95 @@ +package argocd + +import ( + "fmt" + "github.com/zcubbs/go-k8s/kubernetes" + "github.com/zcubbs/x/secret" + "strings" +) + +const Git = "git" +const Helm = "helm" + +type Repository struct { + Name string `json:"name"` + Url string `json:"url"` + + Username string `json:"username"` + Password string `json:"password"` + + Type string `json:"type"` +} + +func CreateRepository(repo Repository, _ string, debug bool) error { + if repo.Type != Git && repo.Type != Helm { + return fmt.Errorf("invalid repository type: %s, must be git of helm", repo.Type) + } + + urlValid := strings.HasPrefix(repo.Url, "http://") || strings.HasPrefix(repo.Url, "https://") + if !urlValid && repo.Type == Git { + return fmt.Errorf("error: repository url must be valid url: %s, (http://... or https://...)", repo.Url) + } + + if repo.Type == Git { + urlValid = strings.HasSuffix(repo.Url, ".git") + if !urlValid { + repo.Url = repo.Url + ".git" + } + } + + username, err := secret.Provide(repo.Username) + if err != nil { + return fmt.Errorf("failed to provide argocd repository username \n %w", err) + } + + password, err := secret.Provide(repo.Password) + if err != nil { + return fmt.Errorf("failed to provide argocd repository password \n %w", err) + } + + tmpValues := repoTmplValues{ + Name: repo.Name, + Namespace: argocdNamespace, + Type: repo.Type, + Url: repo.Url, + Username: username, + Password: password, + } + + err = kubernetes.ApplyManifest(repoTmpl, tmpValues, debug) + if err != nil { + return err + } + + return nil +} + +type repoTmplValues struct { + Name string + Namespace string + Type string + Url string + Username string + Password string +} + +var repoTmpl = `--- + +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Name }} + namespace: {{ .Namespace }} + labels: + argocd.argoproj.io/secret-type: repository +stringData: + type: {{ .Type }} + name: {{ .Name }} +{{- if eq .Type "helm" }} + enableOCI: "true" +{{- end }} + url: {{ .Url }} + username: {{ .Username }} + password: {{ .Password }} +type: Opaque +` diff --git a/awx/awx.go b/awx/awx.go new file mode 100644 index 0000000..7e6c6d4 --- /dev/null +++ b/awx/awx.go @@ -0,0 +1,158 @@ +package awx + +import ( + "context" + "fmt" + "github.com/zcubbs/go-k8s/helm" + "github.com/zcubbs/go-k8s/kubernetes" + "time" +) + +const ( + awxInstanceDefaultName = "awx" + awxNamespace = "awx" + awxOperatorRepoUrl = "https://ansible.github.io/awx-operator/" + awxOperatorChartName = "awx-operator" + awxOperatorChartVersion = "" + + awxOperatorDeploymentName = "awx-operator" + + awxServiceTypeClusterIP = "ClusterIP" + awxServiceTypeNodePort = "NodePort" +) + +type Values struct { + InstanceName string + ChartVersion string + AdminUser string + AdminPass string + ServiceType string + IsNodePort bool + Namespace string +} + +func Install(values Values, kubeconfig string, debug bool) error { + if err := validateValues(&values); err != nil { + return err + } + + err := helm.Install(helm.Chart{ + Name: awxOperatorChartName, + Repo: awxOperatorChartName, + URL: awxOperatorRepoUrl, + Version: values.ChartVersion, + Values: nil, + ValuesFiles: nil, + Namespace: awxNamespace, + Upgrade: true, + CreateNamespace: true, + }, kubeconfig, debug) + if err != nil { + return fmt.Errorf("failed to install awx-operator \n %w", err) + } + + // wait for awx-operator to be ready + ctxWithTimeout, cancel := context.WithTimeout(context.Background(), 3*time.Minute) + defer cancel() + err = kubernetes.IsDeploymentReady( + ctxWithTimeout, + kubeconfig, + awxNamespace, + []string{ + awxOperatorDeploymentName, + }, + debug, + ) + if err != nil { + return fmt.Errorf("failed to wait for awx-operator to be ready \n %w", err) + } + + // apply awx instance + err = addInstance(instanceTmplValues{ + Name: values.InstanceName, + Namespace: values.Namespace, + IsIpv6: false, + AdminUser: values.AdminUser, + AdminPassword: values.AdminPass, + NoLog: true, + }, kubeconfig, debug) + if err != nil { + return fmt.Errorf("failed to apply awx instance \n %w", err) + } + + return nil +} + +func Uninstall(kubeconfig string, debug bool) error { + return helm.Uninstall(helm.Chart{ + Name: awxOperatorChartName, + Namespace: awxNamespace, + }, kubeconfig, debug) +} + +func addInstance(values instanceTmplValues, _ string, debug bool) error { + err := kubernetes.ApplyManifest(adminPasswordSecretTmpl, values, debug) + if err != nil { + return fmt.Errorf("failed to apply awx admin password secret \n %w", err) + } + + err = kubernetes.ApplyManifest(instanceTmpl, values, debug) + if err != nil { + return fmt.Errorf("failed to apply awx instance \n %w", err) + } + return nil +} + +type instanceTmplValues struct { + Name string + Namespace string + IsIpv6 bool + AdminUser string + AdminPassword string + NoLog bool +} + +var instanceTmpl = ` +apiVersion: awx.ansible.com/v1beta1 +kind: AWX +metadata: + name: {{ .Name }} + namespace: {{ .Namespace }} +spec: + service_type: ClusterIP + ingress_type: none + ipv6_disabled: true + no_log: true + admin_user: {{ .AdminUser }} + +` + +var adminPasswordSecretTmpl = ` +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Name }}-admin-password + namespace: {{ .Namespace }} +stringData: + password: {{ .Password }} + +` + +func validateValues(values *Values) error { + if values.ChartVersion == "" { + values.ChartVersion = awxOperatorChartVersion + } + if values.ServiceType == "" { + values.ServiceType = awxServiceTypeClusterIP + } + if values.ServiceType != awxServiceTypeClusterIP && values.ServiceType != awxServiceTypeNodePort { + return fmt.Errorf("invalid service type %s, must be %s or %s", values.ServiceType, awxServiceTypeClusterIP, awxServiceTypeNodePort) + } + if values.ServiceType == awxServiceTypeNodePort { + values.IsNodePort = true + } + if values.InstanceName == "" { + values.InstanceName = awxInstanceDefaultName + } + return nil +} diff --git a/certmanager/certmanager.go b/certmanager/certmanager.go new file mode 100644 index 0000000..0332794 --- /dev/null +++ b/certmanager/certmanager.go @@ -0,0 +1 @@ +package certmanager diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..3912ac0 --- /dev/null +++ b/go.mod @@ -0,0 +1,149 @@ +module github.com/zcubbs/go-k8s + +go 1.21 + +require ( + github.com/gofrs/flock v0.8.1 + github.com/pkg/errors v0.9.1 + github.com/zcubbs/x v0.1.7 + golang.org/x/crypto v0.13.0 + golang.org/x/text v0.13.0 + gopkg.in/yaml.v2 v2.4.0 + helm.sh/helm/v3 v3.12.3 + k8s.io/api v0.28.2 + k8s.io/apimachinery v0.28.2 + k8s.io/client-go v0.28.2 +) + +require ( + github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect + github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect + github.com/BurntSushi/toml v1.3.2 // indirect + github.com/MakeNowJust/heredoc v1.0.0 // indirect + github.com/Masterminds/goutils v1.1.1 // indirect + github.com/Masterminds/semver/v3 v3.2.1 // indirect + github.com/Masterminds/sprig/v3 v3.2.3 // indirect + github.com/Masterminds/squirrel v1.5.4 // indirect + github.com/Microsoft/hcsshim v0.11.1 // indirect + github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/chai2010/gettext-go v1.0.2 // indirect + github.com/containerd/containerd v1.7.6 // indirect + github.com/cyphar/filepath-securejoin v0.2.4 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/docker/cli v24.0.6+incompatible // indirect + github.com/docker/distribution v2.8.2+incompatible // indirect + github.com/docker/docker v24.0.6+incompatible // indirect + github.com/docker/docker-credential-helpers v0.8.0 // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-metrics v0.0.1 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/evanphx/json-patch v5.7.0+incompatible // indirect + github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect + github.com/fatih/color v1.15.0 // indirect + github.com/go-errors/errors v1.5.1 // indirect + github.com/go-gorp/gorp/v3 v3.1.0 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/go-openapi/jsonpointer v0.20.0 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.4 // indirect + github.com/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.1.2 // indirect + github.com/google/gnostic-models v0.6.8 // 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.1 // indirect + github.com/gorilla/mux v1.8.0 // indirect + github.com/gosuri/uitable v0.0.4 // indirect + github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/huandu/xstrings v1.4.0 // indirect + github.com/imdario/mergo v0.3.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.17.0 // indirect + github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect + github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect + github.com/lib/pq v1.10.9 // indirect + github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/go-wordwrap v1.0.1 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/moby/locker v1.0.1 // indirect + github.com/moby/spdystream v0.2.0 // indirect + github.com/moby/term v0.5.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect + github.com/morikuni/aec v1.0.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.0-rc5 // indirect + github.com/peterbourgon/diskv v2.0.1+incompatible // 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.12.0 // indirect + github.com/rivo/uniseg v0.4.4 // indirect + github.com/rubenv/sql-migrate v1.5.2 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/shirou/gopsutil v3.21.11+incompatible // indirect + github.com/shopspring/decimal v1.3.1 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/spf13/cast v1.5.1 // indirect + github.com/spf13/cobra v1.7.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xeipuuv/gojsonschema v1.2.0 // indirect + github.com/xlab/treeprint v1.2.0 // indirect + github.com/yusufpapurcu/wmi v1.2.3 // indirect + go.opentelemetry.io/otel v1.18.0 // indirect + go.opentelemetry.io/otel/metric v1.18.0 // indirect + go.opentelemetry.io/otel/trace v1.18.0 // indirect + go.starlark.net v0.0.0-20230921161717-a9587466d7a5 // indirect + golang.org/x/net v0.15.0 // indirect + golang.org/x/oauth2 v0.12.0 // indirect + golang.org/x/sync v0.3.0 // indirect + golang.org/x/sys v0.12.0 // indirect + golang.org/x/term v0.12.0 // indirect + golang.org/x/time v0.3.0 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect + google.golang.org/grpc v1.58.2 // indirect + google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/apiextensions-apiserver v0.28.2 // indirect + k8s.io/apiserver v0.28.2 // indirect + k8s.io/cli-runtime v0.28.2 // indirect + k8s.io/component-base v0.28.2 // indirect + k8s.io/klog/v2 v2.100.1 // indirect + k8s.io/kube-openapi v0.0.0-20230918164632-68afd615200d // indirect + k8s.io/kubectl v0.28.2 // indirect + k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect + oras.land/oras-go v1.2.4 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/kustomize/api v0.14.0 // indirect + sigs.k8s.io/kustomize/kyaml v0.14.3 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..0bd0cc0 --- /dev/null +++ b/go.sum @@ -0,0 +1,536 @@ +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= +github.com/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.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= +github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= +github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= +github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= +github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/Microsoft/hcsshim v0.11.1 h1:hJ3s7GbWlGK4YVV92sO88BQSyF4ZLVy7/awqOlPxFbA= +github.com/Microsoft/hcsshim v0.11.1/go.mod h1:nFJmaO4Zr5Y7eADdFOpYswDDlNVbvcIJJNJLECr5JQg= +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/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/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-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= +github.com/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/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/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= +github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= +github.com/containerd/containerd v1.7.6 h1:oNAVsnhPoy4BTPQivLgTzI9Oleml9l/+eYIDYXRCYo8= +github.com/containerd/containerd v1.7.6/go.mod h1:SY6lrkkuJT40BVNO37tlYTSnKJnP5AXBc0fhx0q+TJ4= +github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= +github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= +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.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= +github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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 v24.0.6+incompatible h1:fF+XCQCgJjjQNIMjzaSmiKJSCcfcXb3TWTcc7GAneOY= +github.com/docker/cli v24.0.6+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= +github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v24.0.6+incompatible h1:hceabKCtUgDqPu+qm0NgsaXf28Ljf4/pWFL7xjWWDgE= +github.com/docker/docker v24.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.8.0 h1:YQFtbBQb4VrpoPxhFuzEBPQ9E16qz5SpHLS+uswaCp8= +github.com/docker/docker-credential-helpers v0.8.0/go.mod h1:UGFXcuoQ5TxPiB54nHOZ32AWRqQdECoh/Mg0AlEYb40= +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.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= +github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI= +github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2Wvf/TIe2xdyJxTlb6obmF18d8QdkxNDu4= +github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +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/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI= +github.com/foxcpp/go-mockdns v1.0.0/go.mod h1:lgRN6+KxQBawyIghpnl5CezHFGS9VLzvtVlwxvzXTQ4= +github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= +github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= +github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= +github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= +github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/gobuffalo/logger v1.0.6 h1:nnZNpxYo0zx+Aj9RfMPBm+x9zAU2OayFh/xrAWi34HU= +github.com/gobuffalo/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/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/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/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.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k= +github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= +github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= +github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= +github.com/gorilla/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.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-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= +github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +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/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= +github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= +github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= +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.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= +github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +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.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI= +github.com/markbates/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.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +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.1.25 h1:dFwPR6SfLtrSwgDcIq2bcU/gVutB4sNApq2HBdqcakg= +github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= +github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= +github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= +github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/onsi/ginkgo/v2 v2.9.4 h1:xR7vG4IXt5RWx6FfIjyAtsoMAtnc3C/rFXBBd2AjZwE= +github.com/onsi/ginkgo/v2 v2.9.4/go.mod h1:gCQYp2Q+kSoIj7ykSVb9nskRSsR6PUj4AiLywzIhbKM= +github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= +github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= +github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +github.com/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/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= +github.com/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 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.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= +github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +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.5.2 h1:bMDqOnrJVV/6JQgQ/MxOpU+AdO8uzYYA/TxFUBzFtS0= +github.com/rubenv/sql-migrate v1.5.2/go.mod h1:H38GW8Vqf8F0Su5XignRyaRcbXbJunSWxs+kmzlg0Is= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= +github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= +github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= +github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= +github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= +github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= +github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +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= +github.com/zcubbs/x v0.1.7 h1:AVniiwILd5/nEYIYUISe5VcdcSgo5TK4+2vbY6zWofc= +github.com/zcubbs/x v0.1.7/go.mod h1:g+WTzTEdtnwi9RagbEPG/J8abt7tlI70trXo/S5iuc0= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/otel v1.18.0 h1:TgVozPGZ01nHyDZxK5WGPFB9QexeTMXEH7+tIClWfzs= +go.opentelemetry.io/otel v1.18.0/go.mod h1:9lWqYO0Db579XzVuCKFNPDl4s73Voa+zEck3wHaAYQI= +go.opentelemetry.io/otel/metric v1.18.0 h1:JwVzw94UYmbx3ej++CwLUQZxEODDj/pOuTCvzhtRrSQ= +go.opentelemetry.io/otel/metric v1.18.0/go.mod h1:nNSpsVDjWGfb7chbRLUNW+PBNdcSTHD4Uu5pfFMOI0k= +go.opentelemetry.io/otel/trace v1.18.0 h1:NY+czwbHbmndxojTEKiSMHkG2ClNH2PwmcHrdo0JY10= +go.opentelemetry.io/otel/trace v1.18.0/go.mod h1:T2+SGJGuYZY3bjj5rgh/hN7KIrlpWC5nS8Mjvzckz+0= +go.starlark.net v0.0.0-20230921161717-a9587466d7a5 h1:Hg7PpzzjFq0TBToBkMgmjY0W09rC52nmm+v2ME9ZT5w= +go.starlark.net v0.0.0-20230921161717-a9587466d7a5/go.mod h1:LcLNIzVOMp4oV+uusnpk+VU+SzXaJakUuBjoCSWH5dM= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +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.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= +golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/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-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-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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.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.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +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/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 h1:N3bU/SQDCDyD6R528GJ/PwW9KjYcJA3dgyH+MovAkIM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA= +google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I= +google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= +gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= +helm.sh/helm/v3 v3.12.3 h1:5y1+Sbty12t48T/t/CGNYUIME5BJ0WKfmW/sobYqkFg= +helm.sh/helm/v3 v3.12.3/go.mod h1:KPKQiX9IP5HX7o5YnnhViMnNuKiL/lJBVQ47GHe1R0k= +k8s.io/api v0.28.2 h1:9mpl5mOb6vXZvqbQmankOfPIGiudghwCoLl1EYfUZbw= +k8s.io/api v0.28.2/go.mod h1:RVnJBsjU8tcMq7C3iaRSGMeaKt2TWEUXcpIt/90fjEg= +k8s.io/apiextensions-apiserver v0.28.2 h1:J6/QRWIKV2/HwBhHRVITMLYoypCoPY1ftigDM0Kn+QU= +k8s.io/apiextensions-apiserver v0.28.2/go.mod h1:5tnkxLGa9nefefYzWuAlWZ7RZYuN/765Au8cWLA6SRg= +k8s.io/apimachinery v0.28.2 h1:KCOJLrc6gu+wV1BYgwik4AF4vXOlVJPdiqn0yAWWwXQ= +k8s.io/apimachinery v0.28.2/go.mod h1:RdzF87y/ngqk9H4z3EL2Rppv5jj95vGS/HaFXrLDApU= +k8s.io/apiserver v0.28.2 h1:rBeYkLvF94Nku9XfXyUIirsVzCzJBs6jMn3NWeHieyI= +k8s.io/apiserver v0.28.2/go.mod h1:f7D5e8wH8MWcKD7azq6Csw9UN+CjdtXIVQUyUhrtb+E= +k8s.io/cli-runtime v0.28.2 h1:64meB2fDj10/ThIMEJLO29a1oujSm0GQmKzh1RtA/uk= +k8s.io/cli-runtime v0.28.2/go.mod h1:bTpGOvpdsPtDKoyfG4EG041WIyFZLV9qq4rPlkyYfDA= +k8s.io/client-go v0.28.2 h1:DNoYI1vGq0slMBN/SWKMZMw0Rq+0EQW6/AK4v9+3VeY= +k8s.io/client-go v0.28.2/go.mod h1:sMkApowspLuc7omj1FOSUxSoqjr+d5Q0Yc0LOFnYFJY= +k8s.io/component-base v0.28.2 h1:Yc1yU+6AQSlpJZyvehm/NkJBII72rzlEsd6MkBQ+G0E= +k8s.io/component-base v0.28.2/go.mod h1:4IuQPQviQCg3du4si8GpMrhAIegxpsgPngPRR/zWpzc= +k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= +k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20230918164632-68afd615200d h1:/CFeJBjBrZvHX09rObS2+2iEEDevMWYc1v3aIYAjIYI= +k8s.io/kube-openapi v0.0.0-20230918164632-68afd615200d/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= +k8s.io/kubectl v0.28.2 h1:fOWOtU6S0smdNjG1PB9WFbqEIMlkzU5ahyHkc7ESHgM= +k8s.io/kubectl v0.28.2/go.mod h1:6EQWTPySF1fn7yKoQZHYf9TPwIl2AygHEcJoxFekr64= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +oras.land/oras-go v1.2.4 h1:djpBY2/2Cs1PV87GSJlxv4voajVOMZxqqtq9AB8YNvY= +oras.land/oras-go v1.2.4/go.mod h1:DYcGfb3YF1nKjcezfX2SNlDAeQFKSXmf+qrFmrh4324= +sigs.k8s.io/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.14.0 h1:6+QLmXXA8X4eDM7ejeaNUyruA1DDB3PVIjbpVhDOJRA= +sigs.k8s.io/kustomize/api v0.14.0/go.mod h1:vmOXlC8BcmcUJQjiceUbcyQ75JBP6eg8sgoyzc+eLpQ= +sigs.k8s.io/kustomize/kyaml v0.14.3 h1:WpabVAKZe2YEp/irTSHwD6bfjwZnTtSDewd2BVJGMZs= +sigs.k8s.io/kustomize/kyaml v0.14.3/go.mod h1:npvh9epWysfQ689Rtt/U+dpOJDTBn8kUnF1O6VzvmZA= +sigs.k8s.io/structured-merge-diff/v4 v4.3.0 h1:UZbZAZfX0wV2zr7YZorDz6GXROfDFj6LvqCRm4VUVKk= +sigs.k8s.io/structured-merge-diff/v4 v4.3.0/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/helm/chart.go b/helm/chart.go new file mode 100644 index 0000000..fee552e --- /dev/null +++ b/helm/chart.go @@ -0,0 +1,361 @@ +// Package helm. +/* +Copyright © 2023 zcubbs https://github.com/zcubbs +*/ + +package helm + +import ( + "fmt" + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/chart/loader" + "helm.sh/helm/v3/pkg/cli" + helmValues "helm.sh/helm/v3/pkg/cli/values" + "helm.sh/helm/v3/pkg/downloader" + "helm.sh/helm/v3/pkg/getter" + "helm.sh/helm/v3/pkg/release" + "os" +) + +type Chart struct { + Name string + Repo string + URL string + Version string + Values map[string]interface{} + ValuesFiles []string + Namespace string + Upgrade bool + CreateNamespace bool +} + +// Install installs a helm chart. +func Install(chart Chart, kubeconfig string, debug bool) error { + vals := convertMapToChartValues(chart.Values) + vals.ValueFiles = chart.ValuesFiles + chartOptions := installChartOptions{ + Kubeconfig: kubeconfig, + RepoName: chart.Repo, + RepoUrl: chart.URL, + ChartName: chart.Name, + Namespace: chart.Namespace, + ChartVersion: chart.Version, + ChartValues: vals, + Debug: debug, + Upgrade: chart.Upgrade, + } + + return installChart(chartOptions) +} + +func convertMapToChartValues(input map[string]interface{}) helmValues.Options { + var sets []string + recursiveBuildSets(input, "", &sets) + + return helmValues.Options{ + Values: sets, + } +} + +func recursiveBuildSets(input map[string]interface{}, parentKey string, sets *[]string) { + for key, value := range input { + if castedValue, ok := value.(map[string]interface{}); ok { + // If value is another map, recursively handle it + if parentKey == "" { + recursiveBuildSets(castedValue, key, sets) + } else { + recursiveBuildSets(castedValue, fmt.Sprintf("%s.%s", parentKey, key), sets) + } + } else { + // Else, add to the set slice + setValue := fmt.Sprintf("%s=%v", key, value) + if parentKey != "" { + setValue = fmt.Sprintf("%s.%s=%v", parentKey, key, value) + } + *sets = append(*sets, setValue) + } + } +} + +// Uninstall uninstalls a helm chart. +func Uninstall(chart Chart, kubeconfig string, debug bool) error { + return uninstallChart(kubeconfig, chart.Name, chart.Namespace, debug) +} + +type installChartOptions struct { + Kubeconfig string + RepoName string + RepoUrl string + ChartName string + Namespace string + ChartVersion string + ChartValues helmValues.Options + Debug bool + Upgrade bool + ValuesFile string +} + +func installChart(options installChartOptions) error { + var settings = cli.New() + settings.KubeConfig = options.Kubeconfig + settings.Debug = options.Debug + + actionConfig := new(action.Configuration) + + helmLog := chooseLogFunc(options.Debug) + + if err := actionConfig.Init( + settings.RESTClientGetter(), + options.Namespace, + os.Getenv("HELM_DRIVER"), + helmLog); err != nil { + return fmt.Errorf("failed to init action config. %s", err) + } + + // Add repo if it doesn't exist + err := RepoAdd(options.RepoName, options.RepoUrl, options.Debug) + if err != nil { + return fmt.Errorf("failed to add repo: %w", err) + } + + // Update charts from the helm repo + err = RepoUpdate(options.Debug) + if err != nil { + return fmt.Errorf("failed to update repo: %w", err) + } + + // Check if release exists + exists, err := releaseExists(options.ChartName, options.Namespace, settings) + if err != nil { + return fmt.Errorf("failed to check if release exists: %w", err) + } + + if exists && options.Upgrade { + return updateChart(options, actionConfig, settings) + } + + return performInstallChart(options, actionConfig, settings) +} + +func updateChart(options installChartOptions, actionConfig *action.Configuration, settings *cli.EnvSettings) error { + client := action.NewUpgrade(actionConfig) + client.Namespace = options.Namespace + + // If version is specified, set it + if options.ChartVersion != "" { + client.Version = options.ChartVersion + } + + // Locate the chart + chartPath, err := client.ChartPathOptions.LocateChart(fmt.Sprintf("%s/%s", options.RepoName, options.ChartName), settings) + if err != nil { + return fmt.Errorf("failed to locate chart: %w", err) + } + + // Load the chart + loadedChart, err := loader.Load(chartPath) + if err != nil { + return fmt.Errorf("failed to load chart from path: %w", err) + } + + // Merge provided values + p := getter.All(settings) + vals, err := options.ChartValues.MergeValues(p) + if err != nil { + return fmt.Errorf("failed to merge values: %w", err) + } + + // Update the chart + _, err = client.Run(options.ChartName, loadedChart, vals) + if err != nil { + return fmt.Errorf("failed to update chart: %w", err) + } + + return nil +} + +func performInstallChart(options installChartOptions, actionConfig *action.Configuration, settings *cli.EnvSettings) error { + client := action.NewInstall(actionConfig) + client.CreateNamespace = true + + setClientOptions(client, options) + + chartPath, err := locateChartPath(client, options, settings) + if err != nil { + return err + } + + chartRequested, err := loader.Load(chartPath) + if err != nil { + return fmt.Errorf("failed to load chart. %s", err) + } + + if valid, err := isChartInstallable(chartRequested); !valid { + return fmt.Errorf("chart %s is not installable. %s", chartRequested.Name(), err) + } + + err = handleDependencies(client, settings, chartRequested, chartPath, getter.All(settings)) + if err != nil { + return err + } + + vals, err := options.ChartValues.MergeValues(getter.All(settings)) + if err != nil { + return fmt.Errorf("failed to merge values: %w", err) + } + + r, err := client.Run(chartRequested, vals) + if err != nil { + return handleInstallError(err, options) + } + + printDebugInfo(r, options) + + return nil +} + +// Checks whether a Helm release with the given name exists in the specified namespace. +func releaseExists(releaseName, namespace string, settings *cli.EnvSettings) (bool, error) { + helmLog := chooseLogFunc(false) + actionConfig := new(action.Configuration) + if err := actionConfig.Init(settings.RESTClientGetter(), namespace, os.Getenv("HELM_DRIVER"), helmLog); err != nil { + return false, err + } + + listAction := action.NewList(actionConfig) + listAction.Deployed = true // Only consider deployed releases + + releases, err := listAction.Run() + if err != nil { + return false, err + } + + for _, rel := range releases { + if rel.Name == releaseName { + return true, nil + } + } + + return false, nil +} + +func setClientOptions(client *action.Install, options installChartOptions) { + client.IsUpgrade = options.Upgrade + client.ReleaseName = options.ChartName + client.Namespace = options.Namespace + + if options.ChartVersion != "" { + client.Version = options.ChartVersion + } + + if client.Version == "" && client.Devel { + client.Version = ">0.0.0-0" + } +} + +func locateChartPath(client *action.Install, options installChartOptions, settings *cli.EnvSettings) (string, error) { + cp, err := client.ChartPathOptions.LocateChart(fmt.Sprintf("%s/%s", options.RepoName, options.ChartName), settings) + if err != nil { + return "", fmt.Errorf("failed to locate chart. %s", err) + } + if options.Debug { + helmLog := chooseLogFunc(options.Debug) + helmLog("CHART PATH: %s\n", cp) + } + return cp, nil +} + +func handleInstallError(err error, options installChartOptions) error { + if err.Error() == "cannot re-use a name that is still in use" && options.Debug { + fmt.Println("warning: chart release name already exists, no action taken!") + return nil + } + return fmt.Errorf("failed to install chart: %w", err) +} + +func printDebugInfo(release *release.Release, options installChartOptions) { + if options.Debug { + fmt.Println(release.Manifest) + fmt.Println(release.Info) + fmt.Println(release.Chart.Values) + } +} + +// isChartInstallable checks if the chart is installable. +func isChartInstallable(ch *chart.Chart) (bool, error) { + switch ch.Metadata.Type { + case "", "application": + return true, nil + } + return false, fmt.Errorf("invalid chart type: %q", ch.Metadata.Type) +} + +func handleDependencies(client *action.Install, + settings *cli.EnvSettings, + ch *chart.Chart, + cp string, + p getter.Providers) error { + + reqs := ch.Metadata.Dependencies + if reqs == nil { + // No dependencies to handle + return nil + } + + if err := action.CheckDependencies(ch, reqs); err != nil { + if client.DependencyUpdate { + return updateDependencies(cp, client, settings, p) + } else { + return fmt.Errorf("chart dependencies are not satisfied: %w", err) + } + } + + return nil +} + +func updateDependencies(cp string, client *action.Install, settings *cli.EnvSettings, p getter.Providers) error { + man := &downloader.Manager{ + Out: os.Stdout, + ChartPath: cp, + Keyring: client.ChartPathOptions.Keyring, + SkipUpdate: false, + Getters: p, + RepositoryConfig: settings.RepositoryConfig, + RepositoryCache: settings.RepositoryCache, + } + if err := man.Update(); err != nil { + return fmt.Errorf("failed to update the chart dependencies: %w", err) + } + return nil +} + +func uninstallChart(kubeconfig, name, namespace string, debug bool) error { + var settings = cli.New() + settings.KubeConfig = kubeconfig + actionConfig := new(action.Configuration) + + var helmLog = noLog + + if debug { + helmLog = debugLog + } + + if err := actionConfig.Init(settings.RESTClientGetter(), + namespace, + os.Getenv("HELM_DRIVER"), + helmLog); err != nil { + return fmt.Errorf("failed to initialize helm action configuration: %w", err) + } + client := action.NewUninstall(actionConfig) + + r, err := client.Run(name) + if err != nil { + return fmt.Errorf("failed to uninstall chart: %w", err) + } + + if debug { + fmt.Println("uninstalled", r.Release.Name) + } + return nil +} diff --git a/helm/helm.go b/helm/helm.go new file mode 100644 index 0000000..4c6441a --- /dev/null +++ b/helm/helm.go @@ -0,0 +1,67 @@ +// Package helm. +/* +Copyright © 2023 zcubbs https://github.com/zcubbs +*/ +package helm + +import ( + "fmt" + "github.com/zcubbs/x/bash" + "strings" +) + +const ( + TmpHelmScript = "/tmp/get_helm.sh" + InstallScriptUrl = "https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3" +) + +func InstallCli(debug bool) error { + if debug { + fmt.Printf("installing helm %s", "curl -fsSL -o "+TmpHelmScript+InstallScriptUrl) + } + + err := bash.ExecuteCmd( + "curl", + debug, + "-fsSL", + "-o", + TmpHelmScript, + "https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3", + ) + if err != nil { + return err + } + + // chmod 700 get_helm.sh + err = bash.Chmod("/tmp/get_helm.sh", 0700, debug) + if err != nil { + return err + } + + // sh ./get_helm.sh + _, err = bash.ExecuteScript( + TmpHelmScript, + debug, + TmpHelmScript, + ) + if err != nil { + return err + } + + return nil +} + +func IsHelmInstalled() (bool, error) { + // helm version + err := bash.ExecuteCmd( + "helm", + false, + "version", + ) + if err != nil && strings.Contains(err.Error(), "executable file not found in $PATH") { + return false, nil + } else if err != nil { + return false, err + } + return true, nil +} diff --git a/helm/log.go b/helm/log.go new file mode 100644 index 0000000..c4b33f0 --- /dev/null +++ b/helm/log.go @@ -0,0 +1,27 @@ +// Package helm. +/* +Copyright © 2023 zcubbs https://github.com/zcubbs +*/ +package helm + +import ( + "fmt" + "log" +) + +func chooseLogFunc(debug bool) func(string, ...interface{}) { + if debug { + return debugLog + } + return noLog +} + +func debugLog(format string, v ...interface{}) { + format = fmt.Sprintf("[debug] %s\n", format) + err := log.Output(2, fmt.Sprintf(format, v...)) + if err != nil { + return + } +} + +func noLog(_ string, _ ...interface{}) {} diff --git a/helm/queries.go b/helm/queries.go new file mode 100644 index 0000000..4469852 --- /dev/null +++ b/helm/queries.go @@ -0,0 +1,27 @@ +// Package helm. +/* +Copyright © 2023 zcubbs https://github.com/zcubbs +*/ +package helm + +import ( + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/kube" + "helm.sh/helm/v3/pkg/release" + "log" +) + +func GetAllReleases(kubeconfig string) ([]*release.Release, error) { + actionConfig := new(action.Configuration) + err := actionConfig.Init(kube.GetConfig(kubeconfig, "", ""), "", "", log.Printf) + if err != nil { + return nil, err + } + + _releases, err := action.NewList(actionConfig).Run() + if err != nil { + return nil, err + } + + return _releases, nil +} diff --git a/helm/repository.go b/helm/repository.go new file mode 100644 index 0000000..2434efe --- /dev/null +++ b/helm/repository.go @@ -0,0 +1,142 @@ +// Package helm. +/* +Copyright © 2023 zcubbs https://github.com/zcubbs +*/ +package helm + +import ( + "context" + "fmt" + "github.com/gofrs/flock" + "github.com/pkg/errors" + "gopkg.in/yaml.v2" + "helm.sh/helm/v3/pkg/cli" + "helm.sh/helm/v3/pkg/getter" + "helm.sh/helm/v3/pkg/repo" + "os" + "path/filepath" + "strings" + "sync" + "time" +) + +// RepoAdd adds repo with given name and url +func RepoAdd(name, url string, debug bool) error { + var settings = cli.New() + repoFile := settings.RepositoryConfig + repoFile = filepath.Clean(repoFile) + + //Ensure the file directory exists as it is required for file locking + err := os.MkdirAll(filepath.Dir(repoFile), os.ModePerm) + if err != nil && !os.IsExist(err) { + return fmt.Errorf("failed to create directory %s. %s", filepath.Dir(repoFile), err) + } + + // Acquire a file lock for process synchronization + fileLock := flock.New(strings.Replace(repoFile, filepath.Ext(repoFile), ".lock", 1)) + lockCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + locked, err := fileLock.TryLockContext(lockCtx, time.Second) + if err == nil && locked { + defer func(fileLock *flock.Flock) { + err := fileLock.Unlock() + if err != nil { + panic(fmt.Errorf("failed to unlock file %s. %s", repoFile, err)) + } + }(fileLock) + } + if err != nil { + return fmt.Errorf("failed to lock file %s. %s", repoFile, err) + } + + b, err := os.ReadFile(repoFile) + if err != nil && !os.IsNotExist(err) { + return fmt.Errorf("failed to read file %s. %s", repoFile, err) + } + + var f repo.File + if err := yaml.Unmarshal(b, &f); err != nil { + return fmt.Errorf("failed to unmarshal file %s. %s", repoFile, err) + } + + if f.Has(name) { + if debug { + fmt.Printf("%q already exists with the same configuration, skipping\n", name) + } + return nil + } + + c := repo.Entry{ + Name: name, + URL: url, + } + + r, err := repo.NewChartRepository(&c, getter.All(settings)) + if err != nil { + return fmt.Errorf("looks like %q is not a valid chart repository or cannot be reached. %s", url, err) + } + + if _, err := r.DownloadIndexFile(); err != nil { + err := errors.Wrapf(err, "looks like %q is not a valid chart repository or cannot be reached", url) + return err + } + + f.Update(&c) + + if err := f.WriteFile(repoFile, 0644); err != nil { + return fmt.Errorf("failed to write file %s. %s", repoFile, err) + } + + if debug { + fmt.Printf("%q has been added to your repositories\n", name) + } + + return nil +} + +// RepoUpdate updates charts for all helm repos +func RepoUpdate(debug bool) error { + var settings = cli.New() + repoFile := settings.RepositoryConfig + + f, err := repo.LoadFile(repoFile) + if os.IsNotExist(errors.Cause(err)) || len(f.Repositories) == 0 { + return errors.New("no repositories found. You must add one before updating") + } + var repos []*repo.ChartRepository + for _, cfg := range f.Repositories { + r, err := repo.NewChartRepository(cfg, getter.All(settings)) + if err != nil { + return errors.Wrapf(err, "looks like %q is not a valid chart repository or cannot be reached", + cfg.URL) + } + repos = append(repos, r) + } + + if debug { + fmt.Printf("Hang tight while we grab the latest from your chart repositories...\n") + } + + var wg sync.WaitGroup + for _, re := range repos { + wg.Add(1) + go func(re *repo.ChartRepository) { + defer wg.Done() + if debug { + if _, err := re.DownloadIndexFile(); err != nil { + fmt.Printf("...Unable to get an update from the %q chart repository (%s):\n\t%s\n", re.Config.Name, re.Config.URL, err) + } else { + fmt.Printf("...Successfully got an update from the %q chart repository\n", re.Config.Name) + } + } + }(re) + } + + wg.Wait() + + if debug { + fmt.Printf("Update Complete. ⎈ Happy Helming!⎈\n") + } + + return nil +} diff --git a/k3s/install.go b/k3s/install.go new file mode 100644 index 0000000..504a7ff --- /dev/null +++ b/k3s/install.go @@ -0,0 +1,155 @@ +// Package k3s +/* +Copyright © 2023 zcubbs https://github.com/zcubbs +*/ +package k3s + +import ( + "fmt" + "github.com/zcubbs/x/bash" + osx "github.com/zcubbs/x/os" + "os" + "text/template" +) + +const InstallScript = "/tmp/k3s-install.sh" +const UninstallScript = "/usr/local/bin/k3s-uninstall.sh" +const ConfigFileLocation = "/etc/rancher/k3s" + +type Config struct { + Disable []string + TlsSan []string + DataDir string + DefaultLocalStoragePath string + WriteKubeconfigMode string + HttpsListenPort string +} + +var configTmpl = `--- +{{- if .Disable }} +disable: +{{- range $val := $.Disable }} + - {{ $val }} +{{- end }} +{{- end }} +{{- if .DefaultLocalStoragePath }} +default-local-storage-path: {{ .DefaultLocalStoragePath }} +{{- end }} +{{- if .HttpsListenPort }} +https-listen-port: {{ .HttpsListenPort }} +{{- end }} +{{- if .TlsSan }} +tls-san: +{{- range $val := $.TlsSan }} + - {{ $val }} +{{- end }} +{{- end }} +{{- if .DataDir }} +data-dir: {{ .DataDir }} +{{- end }} +{{- if .WriteKubeconfigMode }} +write-kubeconfig-mode: {{ .WriteKubeconfigMode }} +{{- end }} +` + +func Install(config Config, debug bool) error { + if debug { + fmt.Printf("%+v\n", config) + } + + // prepare config file + err := osx.CreateDirIfNotExist(ConfigFileLocation) + if err != nil { + return err + } + targetFile := fmt.Sprintf("%s/%s", ConfigFileLocation, "config.yaml") + err = WriteTemplateToFile(configTmpl, config, targetFile) + if err != nil { + return err + } + + //err = PrintFileContents(targetFile) + //if err != nil { + // log.Fatal(err) + //} + + // curl -sfL https://get.k3s.io -o k3s-install.sh + err = bash.ExecuteCmd( + "curl", + debug, + "https://get.k3s.io", + "-o", + InstallScript, + ) + if err != nil { + return fmt.Errorf("error while executing %s \n%v", + "curl https://get.k3s.io -o k3s-install.sh", + err, + ) + } + + // sh ./k3s-install.sh -s - --write-kubeconfig-mode 644 + err = os.Chmod("/tmp/k3s-install.sh", 0700) + if err != nil { + return fmt.Errorf("error while running %s \n%v", + "chmod 0700 ./tmp/k3s-install.sh -s - --write-kubeconfig-mode 644", + err) + } + + ok, err := bash.ExecuteScript( + InstallScript, + debug, + InstallScript, + "-s", + "-", + "--write-kubeconfig-mode=644", + ) + if !ok && err != nil { + return fmt.Errorf("error while running %s \n%v", + "./k3s-install.sh -s - --write-kubeconfig-mode 644", + err) + } + + return nil +} + +func WriteTemplateToFile(templateStr string, config Config, outputFilePath string) error { + // Create a new template and parse the letter into it. + tmpl, err := template.New("myTemplate").Parse(templateStr) + if err != nil { + return err + } + + // Open the output file + f, err := os.OpenFile(outputFilePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + return err + } + defer func(f *os.File) { + err := f.Close() + if err != nil { + panic(err) + } + }(f) + + // Apply the template to the config data and write to the file + err = tmpl.Execute(f, config) + if err != nil { + return err + } + + return nil +} + +func Uninstall(debug bool) error { + _, err := bash.ExecuteScript( + UninstallScript, + debug, + UninstallScript, + ) + if err != nil { + return err + } + + return nil +} diff --git a/k3s/tools.go b/k3s/tools.go new file mode 100644 index 0000000..bdbac3c --- /dev/null +++ b/k3s/tools.go @@ -0,0 +1,59 @@ +// Package k3s. +/* +Copyright © 2023 zcubbs https://github.com/zcubbs +*/ +package k3s + +import ( + "fmt" + "github.com/zcubbs/x/bash" + "golang.org/x/text/cases" + "golang.org/x/text/language" + "runtime" +) + +func InstallK9s(debug bool) error { + if debug { + fmt.Printf("Arch: %s_%s\n", runtime.GOOS, runtime.GOARCH) + } + + // wget -O /tmp/k9s.tar.gz https://github.com/derailed/k9s/releases/download/v0.27.3/k9s_Linux_amd64.tar.gz + err := bash.ExecuteCmd( + "wget", + debug, + "-O", + "/tmp/k9s.tar.gz", + fmt.Sprintf("https://github.com/derailed/k9s/releases/download/v0.27.3/k9s_%s_%s.tar.gz", + cases.Title(language.Und).String(runtime.GOOS), + runtime.GOARCH, + ), + ) + if err != nil { + return err + } + + // tar -xvf k9s.tar.gz + err = bash.ExtractTarGzWithFile("/tmp/k9s.tar.gz", "k9s", "/tmp", debug) + if err != nil { + return err + } + + // mv /tmp/k9s /usr/local/bin/k9s + err = bash.ExecuteCmd( + "mv", + debug, + "/tmp/k9s", + "/usr/local/bin/k9s", + ) + if err != nil { + return err + } + + // chmod +x /usr/local/bin/k9s + err = bash.Chmod("/usr/local/bin/k9s", 0700, debug) + if err != nil { + return fmt.Errorf("chmod /usr/local/bin/k9s: %w", err) + } + + return nil +} diff --git a/kubernetes/apply.go b/kubernetes/apply.go new file mode 100644 index 0000000..e4d286f --- /dev/null +++ b/kubernetes/apply.go @@ -0,0 +1,39 @@ +package kubernetes + +import ( + "fmt" + "github.com/zcubbs/x/bash" + "github.com/zcubbs/x/yaml" + "os" + "time" +) + +func ApplyManifest(manifestTmpl string, data interface{}, debug bool) error { + b, err := yaml.ApplyTmpl(manifestTmpl, data, debug) + if err != nil { + return fmt.Errorf("failed to apply template \n %w", err) + } + + // generate tmp file name + fn := fmt.Sprintf("/tmp/tmpManifest_%s.yaml", + time.Unix(time.Now().Unix(), 0).Format("20060102150405"), + ) + + // write tmp manifest + err = os.WriteFile(fn, b, 0644) + if err != nil { + return fmt.Errorf("failed to write tmp manifest \n %w", err) + } + + err = bash.ExecuteCmd("kubectl", debug, "apply", "-f", fn) + if err != nil { + return fmt.Errorf("failed to apply manifest \n %w", err) + } + + // delete tmp manifest + err = os.Remove(fn) + if err != nil { + return fmt.Errorf("failed to delete tmp manifest \n %w", err) + } + return nil +} diff --git a/kubernetes/client.go b/kubernetes/client.go new file mode 100644 index 0000000..e89bb7d --- /dev/null +++ b/kubernetes/client.go @@ -0,0 +1,20 @@ +package kubernetes + +import ( + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" +) + +func GetClientSet(kubeconfig string) (*kubernetes.Clientset, error) { + config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) + if err != nil { + return nil, err + } + + cs, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, err + } + + return cs, nil +} diff --git a/kubernetes/namespace.go b/kubernetes/namespace.go new file mode 100644 index 0000000..65e6657 --- /dev/null +++ b/kubernetes/namespace.go @@ -0,0 +1,37 @@ +package kubernetes + +import ( + "context" + apiv1 "k8s.io/api/core/v1" + errosv1 "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func CreateNamespace(kubeconfig string, namespace []string) error { + cs, err := GetClientSet(kubeconfig) + if err != nil { + return err + } + + for _, ns := range namespace { + ns := &apiv1.Namespace{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Namespace", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: ns, + Labels: map[string]string{ + "name": ns, + }, + }, + } + + _, err := cs.CoreV1().Namespaces().Create(context.Background(), ns, metav1.CreateOptions{}) + if err != nil && !errosv1.IsAlreadyExists(err) { + return err + } + } + + return nil +} diff --git a/kubernetes/pod.go b/kubernetes/pod.go new file mode 100644 index 0000000..9e1bc2b --- /dev/null +++ b/kubernetes/pod.go @@ -0,0 +1,91 @@ +package kubernetes + +import ( + "context" + "fmt" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "strings" +) + +func RestartPods(kubeconfig, namespace string, podNames []string, debug bool) error { + cs, err := GetClientSet(kubeconfig) + if err != nil { + return err + } + + pods, err := GetPodsInNamespace(kubeconfig, namespace, debug) + if err != nil { + return err + } + deletePolicy := metav1.DeletePropagationForeground + + for _, pod := range pods { + for _, podName := range podNames { + if strings.Contains(pod, podName) { + err := cs.CoreV1(). + Pods(namespace). + Delete(context.TODO(), pod, metav1.DeleteOptions{ + PropagationPolicy: &deletePolicy, + }) + if err != nil { + return fmt.Errorf("failed to delete pod: %s", err) + } + + if debug { + fmt.Printf("Restarted pod: %s\n", pod) + } + } + } + } + return nil +} + +func GetPodsInNamespace(kubeconfig string, namespace string, debug bool) ([]string, error) { + cs, err := GetClientSet(kubeconfig) + if err != nil { + return nil, err + } + + pods, err := cs.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{}) + if err != nil { + return nil, err + } + + var podNames []string + for _, pod := range pods.Items { + podNames = append(podNames, pod.Name) + } + + if debug { + fmt.Printf("Found pods: %v\n", podNames) + } + return podNames, nil +} + +func GetPodsInDeployment(kubeconfig, namespace, deploymentName string, debug bool) ([]string, error) { + cs, err := GetClientSet(kubeconfig) + if err != nil { + return nil, err + } + + deploy, err := cs.AppsV1().Deployments(namespace).Get(context.TODO(), deploymentName, metav1.GetOptions{}) + if err != nil { + return nil, err + } + + labelSelector := metav1.FormatLabelSelector(deploy.Spec.Selector) + pods, err := cs.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: labelSelector}) + if err != nil { + return nil, err + } + + var podNames []string + for _, pod := range pods.Items { + podNames = append(podNames, pod.Name) + } + + if debug { + fmt.Printf("Found pods: %v\n", podNames) + } + return podNames, nil +} diff --git a/kubernetes/secret.go b/kubernetes/secret.go new file mode 100644 index 0000000..e347b27 --- /dev/null +++ b/kubernetes/secret.go @@ -0,0 +1,126 @@ +package kubernetes + +import ( + "context" + "encoding/base64" + "fmt" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "strings" +) + +type ContainerRegistrySecret struct { + Name string + Server string + Username string + Password string + Email string +} + +func CreateContainerRegistrySecret( + ctx context.Context, + kubeconfig string, + secretConfig ContainerRegistrySecret, + namespaces []string, + replace bool, + debug bool) error { + auth := fmt.Sprintf("%s:%s", secretConfig.Username, secretConfig.Password) + encodedAuth := base64.StdEncoding.EncodeToString([]byte(auth)) + + data := map[string][]byte{ + ".dockerconfigjson": []byte(fmt.Sprintf(`{ + "auths": { + "%s": { + "username": "%s", + "password": "%s", + "email": "%s", + "auth": "%s" + } + } + }`, secretConfig.Server, secretConfig.Username, + secretConfig.Password, secretConfig.Email, encodedAuth)), + } + + secret := &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretConfig.Name, + }, + Data: data, + Type: v1.SecretTypeDockerConfigJson, + } + + cs, err := GetClientSet(kubeconfig) + if err != nil { + return err + } + + for _, namespace := range namespaces { + created, err := cs.CoreV1().Secrets(namespace).Create(ctx, secret, metav1.CreateOptions{}) + if err != nil { + if strings.Contains(err.Error(), "already exists") && replace { + _, err := cs.CoreV1().Secrets(namespace).Update(ctx, secret, metav1.UpdateOptions{}) + return err + } + return fmt.Errorf("failed to create secret: %v", err) + } + + if debug { + fmt.Printf("Created secret %s\n", created.String()) + } + } + + return nil +} + +func CreateGenericSecret( + ctx context.Context, + kubeconfig string, + secretConfig v1.Secret, + namespaces []string, + replace bool, + debug bool) error { + cs, err := GetClientSet(kubeconfig) + if err != nil { + return err + } + + for _, namespace := range namespaces { + created, err := cs.CoreV1().Secrets(namespace).Create(ctx, &secretConfig, metav1.CreateOptions{}) + if err != nil { + if strings.Contains(err.Error(), "already exists") && replace { + _, err := cs.CoreV1().Secrets(namespace).Update(ctx, &secretConfig, metav1.UpdateOptions{}) + return err + } + return fmt.Errorf("failed to create secret: %v", err) + } + + if debug { + fmt.Printf("Created secret %s\n", created.String()) + } + } + + return nil +} + +func GetSecret(kubeconfig, namespace, secretName string) (*v1.Secret, error) { + cs, err := GetClientSet(kubeconfig) + if err != nil { + return nil, err + } + secret, err := cs.CoreV1().Secrets(namespace).Get(context.Background(), secretName, metav1.GetOptions{}) + if err != nil { + return nil, err + } + return secret, nil +} + +// GetSecretByName retrieves a Kubernetes Secret by its name. +func GetSecretByName(kubeconfig, namespace, secretName string) (*v1.Secret, error) { + cs, err := GetClientSet(kubeconfig) + if err != nil { + return nil, err + } + return cs.CoreV1(). + Secrets(namespace). + Get(context.TODO(), secretName, metav1.GetOptions{}) +} diff --git a/kubernetes/service.go b/kubernetes/service.go new file mode 100644 index 0000000..b45e192 --- /dev/null +++ b/kubernetes/service.go @@ -0,0 +1,27 @@ +package kubernetes + +import ( + "context" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// GetServiceCIDR returns the service CIDR from the Kubernetes cluster +func GetServiceCIDR(ctx context.Context, kubeconfig string) (string, error) { + cs, err := GetClientSet(kubeconfig) + if err != nil { + return "", err + } + + nodeList, err := cs.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) + if err != nil { + return "", err + } + + for _, annotation := range nodeList.Items[0].Annotations { + if annotation == "kubeadm.alpha.kubernetes.io/cri-socket" { + return annotation, nil + } + } + + return "", nil +} diff --git a/kubernetes/status.go b/kubernetes/status.go new file mode 100644 index 0000000..9d821d6 --- /dev/null +++ b/kubernetes/status.go @@ -0,0 +1,82 @@ +package kubernetes + +import ( + "context" + "fmt" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "time" +) + +// IsDeploymentReady checks if a kubernetes deployment is ready +func IsDeploymentReady(ctx context.Context, + kubeconfig, namespace string, + deploymentNames []string, + debug bool) error { + + cs, err := GetClientSet(kubeconfig) + if err != nil { + return err + } + for _, deploymentName := range deploymentNames { + err := isDeploymentUnitReady(ctx, namespace, deploymentName, cs, debug) + if err != nil { + return err + } + } + + return nil +} + +func isDeploymentUnitReady(ctx context.Context, + namespace, deploymentName string, + clientSet *kubernetes.Clientset, + debug bool) error { + for { + deployment, err := clientSet.AppsV1().Deployments(namespace).Get(ctx, deploymentName, metav1.GetOptions{}) + if err != nil { + return err + } + + if deployment.Status.ReadyReplicas == *deployment.Spec.Replicas { + if debug { + fmt.Printf("Deployment %s is ready\n", deploymentName) + } + break + } else { + if debug { + fmt.Printf("Deployment %s is not ready yet, ready replicas: %v\n", deploymentName, deployment.Status.ReadyReplicas) + } + time.Sleep(1 * time.Second) + } + } + + return nil +} + +// IsClusterReady checks if a kubernetes cluster is ready +func IsClusterReady(ctx context.Context, kubeconfig string) (bool, error) { + cs, err := GetClientSet(kubeconfig) + if err != nil { + return false, err + } + nodes, err := cs.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) + if err != nil { + return false, err + } + + for _, node := range nodes.Items { + isNodeReady := false + for _, condition := range node.Status.Conditions { + if condition.Type == "Ready" && condition.Status == "True" { + isNodeReady = true + } + } + + if !isNodeReady { + return false, nil + } + } + + return true, nil +} diff --git a/traefik/dns_challenge.go b/traefik/dns_challenge.go new file mode 100644 index 0000000..f7df696 --- /dev/null +++ b/traefik/dns_challenge.go @@ -0,0 +1,182 @@ +package traefik + +import ( + "context" + "fmt" + "github.com/zcubbs/go-k8s/kubernetes" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "os" +) + +type DnsProvider string + +/* #nosec */ +const traefikProviderCredentialsSecretName = "traefik-dns-provider-credentials" + +const ( + Cloudflare DnsProvider = "cloudflare" + OVH DnsProvider = "ovh" + Azure DnsProvider = "azure" +) + +const ( + ovhEndpointEnvKey = "OVH_ENDPOINT" + ovhAppKeyEnvKey = "OVH_APPLICATION_KEY" + /* #nosec */ + ovhAppSecretEnvKey = "OVH_APPLICATION_SECRET" + ovhConsumerKeyEnvKey = "OVH_CONSUMER_KEY" + + azureClientIDEnvKey = "AZURE_CLIENT_ID" + /* #nosec */ + azureClientSecretEnvKey = "AZURE_CLIENT_SECRET" + azureResourceGroupKey = "AZURE_RESOURCE_GROUP" + azureSubscriptionIDKey = "AZURE_SUBSCRIPTION_ID" + azureTenantIDKey = "AZURE_TENANT_ID" +) + +func configureDNSChallengeVars(values Values, kubeconfig string, debug bool) error { + if values.DnsProvider == "" { + return fmt.Errorf("dns provider is required") + } + + if values.DnsProvider == string(Cloudflare) { + return configureCloudflare(values, kubeconfig, debug) + } + + if values.DnsProvider == string(OVH) { + return configureOVH(values, kubeconfig, debug) + } + + if values.DnsProvider == string(Azure) { + return configureAzure(values, kubeconfig, debug) + } + + return fmt.Errorf("unknown dns provider: %s", values.DnsProvider) +} + +func configureCloudflare(_ Values, _ string, _ bool) error { + return fmt.Errorf("cloudflare provider not implemented") +} + +func configureAzure(values Values, kubeconfig string, debug bool) error { + // load env vars + azureClientId := os.Getenv(azureClientIDEnvKey) + azureClientSecret := os.Getenv(azureClientSecretEnvKey) + azureResourceGroup := os.Getenv(azureResourceGroupKey) + azureSubscriptionID := os.Getenv(azureSubscriptionIDKey) + azureTenantID := os.Getenv(azureTenantIDKey) + + // validate env vars + if azureClientId == "" { + return fmt.Errorf("azure client id is required") + } + + if azureClientSecret == "" { + return fmt.Errorf("azure client secret is required") + } + + if azureResourceGroup == "" { + return fmt.Errorf("azure resource group is required") + } + + if azureSubscriptionID == "" { + return fmt.Errorf("azure subscription id is required") + } + + if azureTenantID == "" { + return fmt.Errorf("azure tenant id is required") + } + + // create namespace + err := kubernetes.CreateNamespace( + kubeconfig, + []string{traefikNamespace}, + ) + if err != nil { + return fmt.Errorf("failed to create namespace %s \n %w", traefikNamespace, err) + } + + // create secret + return createSecret( + map[string][]byte{ + "AZURE_CLIENT_ID": []byte(azureClientId), + "AZURE_CLIENT_SECRET": []byte(azureClientSecret), + "AZURE_RESOURCE_GROUP": []byte(azureResourceGroup), + "AZURE_SUBSCRIPTION_ID": []byte(azureSubscriptionID), + "AZURE_TENANT_ID": []byte(azureTenantID), + "TZ": []byte(values.DnsTZ), + }, + kubeconfig, + debug, + ) +} + +func configureOVH(values Values, kubeconfig string, debug bool) error { + // load env vars + ovhEndpoint := os.Getenv(ovhEndpointEnvKey) + ovhAppKey := os.Getenv(ovhAppKeyEnvKey) + ovhAppSecret := os.Getenv(ovhAppSecretEnvKey) + ovhConsumerKey := os.Getenv(ovhConsumerKeyEnvKey) + + // validate env vars + if ovhEndpoint == "" { + return fmt.Errorf("ovh endpoint is required") + } + + if ovhAppKey == "" { + return fmt.Errorf("ovh app key is required") + } + + if ovhAppSecret == "" { + return fmt.Errorf("ovh app secret is required") + } + + if ovhConsumerKey == "" { + return fmt.Errorf("ovh consumer key is required") + } + + // create namespace + err := kubernetes.CreateNamespace( + kubeconfig, + []string{traefikNamespace}, + ) + if err != nil { + return fmt.Errorf("failed to create namespace %s \n %w", traefikNamespace, err) + } + + // create secret + return createSecret( + map[string][]byte{ + "OVH_ENDPOINT": []byte(ovhEndpoint), + "OVH_APPLICATION_KEY": []byte(ovhAppKey), + "OVH_APPLICATION_SECRET": []byte(ovhAppSecret), + "OVH_CONSUMER_KEY": []byte(ovhConsumerKey), + "TZ": []byte(values.DnsTZ), + }, + kubeconfig, + debug, + ) +} + +func createSecret(data map[string][]byte, kubeconfig string, debug bool) error { + // create secret + err := kubernetes.CreateGenericSecret( + context.Background(), + kubeconfig, + v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: traefikProviderCredentialsSecretName, + }, + Data: data, + }, + []string{traefikNamespace}, + true, + debug, + ) + if err != nil { + return fmt.Errorf("failed to create secret %s \n %w", traefikProviderCredentialsSecretName, err) + } + + return nil +} diff --git a/traefik/traefik.go b/traefik/traefik.go new file mode 100644 index 0000000..ff2e865 --- /dev/null +++ b/traefik/traefik.go @@ -0,0 +1,249 @@ +package traefik + +import ( + "context" + "fmt" + "github.com/zcubbs/go-k8s/helm" + "github.com/zcubbs/go-k8s/kubernetes" + "github.com/zcubbs/x/yaml" + "os" + "time" +) + +const ( + traefikHelmRepoName = "traefik" + traefikHelmRepoUrl = "https://helm.traefik.io/traefik" + traefikChartName = "traefik" + traefikChartVersion = "" // latest + traefikNamespace = "traefik" + + traefikDnsResolver = "letsencrypt" + + traefikEndpointWeb = "80" + traefikEndpointWebsecure = "443" + + traefikDnsTZ = "Europe/Paris" +) + +func Install(values Values, kubeconfig string, debug bool) error { + if err := validateValues(&values); err != nil { + return err + } + + if values.DnsProvider != "" { + if err := configureDNSChallengeVars(values, kubeconfig, debug); err != nil { + return err + } + } + + valuesPath := getTmpValuesFilePath() + + // create traefik values.yaml from template + configFileContent, err := yaml.ApplyTmpl(traefikValuesTmpl, values, debug) + if err != nil { + return fmt.Errorf("failed to apply template \n %w", err) + } + + // write tmp manifest + err = os.WriteFile(valuesPath, configFileContent, 0600) + if err != nil { + return fmt.Errorf("failed to write traefik values.yaml \n %w", err) + } + + err = helm.Install(helm.Chart{ + Name: traefikChartName, + Repo: traefikHelmRepoName, + URL: traefikHelmRepoUrl, + Version: traefikChartVersion, + Values: nil, + ValuesFiles: []string{valuesPath}, + Namespace: traefikNamespace, + Upgrade: true, + CreateNamespace: true, + }, kubeconfig, debug) + if err != nil { + return err + } + + // wait for traefik deployment to be ready + ctxWithTimeout, cancel := context.WithTimeout(context.Background(), 1*time.Minute) + defer cancel() + err = kubernetes.IsDeploymentReady( + ctxWithTimeout, + kubeconfig, + traefikNamespace, + []string{"traefik"}, + debug, + ) + if err != nil { + return fmt.Errorf("failed to wait for traefik deployment to be ready \n %w", err) + } + + return nil +} + +func Uninstall(kubeconfig string, debug bool) error { + return helm.Uninstall(helm.Chart{ + Name: traefikChartName, + Namespace: traefikNamespace, + }, kubeconfig, debug) +} + +func getTmpValuesFilePath() string { + return os.TempDir() + "/values-" + time.Now().Format("20060102150405") + ".yaml" +} + +func validateValues(values *Values) error { + if values.IngressProvider != "" && values.DnsProvider != "" { + return fmt.Errorf("can't set both ingressProvider and dnsProvider") + } + + if values.DnsResolver == "" { + values.DnsResolver = traefikDnsResolver + } + + if values.EndpointsWeb == "" { + values.EndpointsWeb = traefikEndpointWeb + } + + if values.EndpointsWebsecure == "" { + values.EndpointsWebsecure = traefikEndpointWebsecure + } + + if values.DnsTZ == "" { + values.DnsTZ = traefikDnsTZ + } + + return nil +} + +type Values struct { + AdditionalArguments []string + IngressProvider string + DnsProvider string + DnsResolver string + DnsResolverEmail string + EnableDashboard bool + EnableAccessLog bool + DebugLog bool + EndpointsWeb string + EndpointsWebsecure string + ServersTransportInsecureSkipVerify bool + ForwardedHeaders bool + ForwardedHeadersInsecure bool + ForwardedHeadersTrustedIPs string + ProxyProtocol bool + ProxyProtocolInsecure bool + ProxyProtocolTrustedIPs string + DnsTZ string +} + +var traefikValuesTmpl = ` +globalArguments: + - "--global.checknewversion=false" + - "--global.sendanonymoususage=false" +global: + sendAnonymousUsage: false + checkNewVersion: false + log: + {{- if .DebugLog }} + level: DEBUG + {{- else }} + level: INFO + {{- end }} + accessLogs: + {{- if .EnableAccessLog }} + enabled: true + {{- else }} + enabled: false + {{- end }} +service: + enabled: true + type: LoadBalancer +rbac: + enabled: true +additionalArguments: + {{- range $i, $arg := .AdditionalArguments }} + - "{{ printf "%s" . }}" + {{- end }} + {{- if .ServersTransportInsecureSkipVerify }} + - "--serversTransport.insecureSkipVerify" + {{- end }} + {{- if .ForwardedHeaders }} + {{- if .ForwardedHeadersInsecure }} + - "--entrypoints.websecure.forwardedHeaders.insecure" + {{- end }} + {{- if .ForwardedHeadersTrustedIPs }} + - "--entrypoints.websecure.forwardedHeaders.trustedIPs=127.0.0.1/32,{{ .ForwardedHeadersTrustedIPs }}" + - "--entrypoints.web.forwardedHeaders.trustedIPs=127.0.0.1/32,{{ .ForwardedHeadersTrustedIPs }}" + {{- end }} + {{- end }} + {{- if .ProxyProtocol }} + {{- if .ProxyProtocolInsecure }} + - "--entrypoints.websecure.proxyProtocol.insecure" + {{- end }} + {{- if .ProxyProtocolTrustedIPs }} + - "--entrypoints.websecure.proxyProtocol.trustedIPs=127.0.0.1/32,{{ .ProxyProtocolTrustedIPs }}" + {{- end }} + {{- end }} + {{- if .IngressProvider }} + - "{{ printf "%s=%s" "--providers.kubernetesIngress.ingressClass" .IngressProvider }}" + {{- end }} + {{- if .DnsProvider }} + - "--certificatesresolvers.{{ .DnsResolver }}-staging.acme.dnschallenge=true" + - "--certificatesresolvers.{{ .DnsResolver }}-staging.acme.dnschallenge.provider={{ .DnsProvider }}" + - "--certificatesresolvers.{{ .DnsResolver }}-staging.acme.dnschallenge.delayBeforeCheck=10" + - "--certificatesresolvers.{{ .DnsResolver }}-staging.acme.email={{ .DnsResolverEmail }}" + - "--certificatesresolvers.{{ .DnsResolver }}-staging.acme.storage=/data/acme.json" + - "--certificatesresolvers.{{ .DnsResolver }}-staging.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory" + - "--certificatesresolvers.{{ .DnsResolver }}.acme.dnschallenge=true" + - "--certificatesresolvers.{{ .DnsResolver }}.acme.dnschallenge.provider={{ .DnsProvider }}" + - "--certificatesresolvers.{{ .DnsResolver }}.acme.dnschallenge.delayBeforeCheck=10" + - "--certificatesresolvers.{{ .DnsResolver }}.acme.email={{ .DnsResolverEmail }}" + - "--certificatesresolvers.{{ .DnsResolver }}.acme.storage=/data/acme.json" + - "--certificatesresolvers.{{ .DnsResolver }}.acme.caserver=https://acme-v02.api.letsencrypt.org/directory" + {{- end }} +ports: + websecure: + tls: + enabled: true + certResolver: {{ .DnsResolver }} + +persistence: + enabled: true + accessMode: ReadWriteOnce + size: 128Mi + path: /data + annotations: { } + +ingressRoute: + dashboard: + enabled: true + +logs: + general: + {{- if .DebugLog }} + level: DEBUG + {{- else }} + level: INFO + {{- end }} + access: + enabled: true +pilot: + enabled: false + +deployment: + initContainers: + - name: volume-permissions + image: busybox:1.31.1 + command: ["sh", "-c", "touch /data/acme.json; chmod -Rv 0600 /data/acme.json; cat /data/acme.json"] + volumeMounts: + - name: data + mountPath: /data + +{{- if .DnsProvider }} +envFrom: + - secretRef: + name: traefik-dns-provider-credentials +{{- end }} +`