mirror of https://github.com/helm/helm.git
Merge branch 'main' into stdlib-errors-2
Signed-off-by: Justen Stall <39888103+justenstall@users.noreply.github.com>
This commit is contained in:
commit
280a9ddbdb
|
@ -1,7 +1,24 @@
|
|||
version: 2
|
||||
|
||||
updates:
|
||||
- # Keep dev-v3 branch dependencies up to date, while Helm v3 is within support
|
||||
package-ecosystem: "gomod"
|
||||
target-branch: "dev-v3"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
groups:
|
||||
k8s.io:
|
||||
patterns:
|
||||
- "k8s.io/api"
|
||||
- "k8s.io/apiextensions-apiserver"
|
||||
- "k8s.io/apimachinery"
|
||||
- "k8s.io/apiserver"
|
||||
- "k8s.io/cli-runtime"
|
||||
- "k8s.io/client-go"
|
||||
- "k8s.io/kubectl"
|
||||
- package-ecosystem: "gomod"
|
||||
target-branch: "main"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
@ -16,6 +33,7 @@ updates:
|
|||
- "k8s.io/client-go"
|
||||
- "k8s.io/kubectl"
|
||||
- package-ecosystem: "github-actions"
|
||||
target-branch: "main"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
GOLANG_VERSION=1.24
|
||||
GOLANGCI_LINT_VERSION=v2.0.2
|
|
@ -7,9 +7,10 @@ on:
|
|||
- "release-**"
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- "main"
|
||||
- "dev-v3"
|
||||
|
||||
permissions:
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
|
@ -18,10 +19,12 @@ jobs:
|
|||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
|
||||
- name: Add variables to environment file
|
||||
run: cat ".github/env" >> "$GITHUB_ENV"
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # pin@5.1.0
|
||||
uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # pin@5.4.0
|
||||
with:
|
||||
go-version: '1.22'
|
||||
go-version: '${{ env.GOLANG_VERSION }}'
|
||||
check-latest: true
|
||||
- name: Test source headers are present
|
||||
run: make test-source-headers
|
||||
|
|
|
@ -14,13 +14,14 @@ jobs:
|
|||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
|
||||
|
||||
- name: Add variables to environment file
|
||||
run: cat ".github/env" >> "$GITHUB_ENV"
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # pin@5.1.0
|
||||
uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # pin@5.4.0
|
||||
with:
|
||||
go-version: '1.22'
|
||||
go-version: '${{ env.GOLANG_VERSION }}'
|
||||
check-latest: true
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@971e284b6050e8a5849b72094c50ab08da042db8 #pin@6.1.1
|
||||
uses: golangci/golangci-lint-action@1481404843c368bc19ca9406f87d6e0fc97bdcfd #pin@7.0.0
|
||||
with:
|
||||
version: v1.58
|
||||
version: ${{ env.GOLANGCI_LINT_VERSION }}
|
||||
|
|
|
@ -13,10 +13,12 @@ jobs:
|
|||
name: govulncheck
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Add variables to environment file
|
||||
run: cat ".github/env" >> "$GITHUB_ENV"
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # pin@5.1.0
|
||||
uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # pin@5.4.0
|
||||
with:
|
||||
go-version: '1.22'
|
||||
go-version: '${{ env.GOLANG_VERSION }}'
|
||||
check-latest: true
|
||||
- name: govulncheck
|
||||
uses: golang/govulncheck-action@b625fbe08f3bccbe446d94fbf87fcc875a4f50ee # pin@1.0.4
|
||||
|
|
|
@ -24,14 +24,15 @@ jobs:
|
|||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # pin@5.1.0
|
||||
with:
|
||||
go-version: '1.22.7'
|
||||
- name: Add variables to environment file
|
||||
run: cat ".github/env" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # pin@5.4.0
|
||||
with:
|
||||
go-version: '${{ env.GOLANG_VERSION }}'
|
||||
- name: Run unit tests
|
||||
run: make test-coverage
|
||||
|
||||
- name: Build Helm Binaries
|
||||
run: |
|
||||
set -eu -o pipefail
|
||||
|
@ -80,10 +81,13 @@ jobs:
|
|||
- name: Checkout source code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
|
||||
|
||||
- name: Add variables to environment file
|
||||
run: cat ".github/env" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # pin@5.1.0
|
||||
uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # pin@5.4.0
|
||||
with:
|
||||
go-version: '1.22'
|
||||
go-version: '${{ env.GOLANG_VERSION }}'
|
||||
check-latest: true
|
||||
|
||||
- name: Run unit tests
|
||||
|
|
|
@ -33,7 +33,7 @@ jobs:
|
|||
persist-credentials: false
|
||||
|
||||
- name: "Run analysis"
|
||||
uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
|
||||
uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
|
@ -55,7 +55,7 @@ jobs:
|
|||
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||
# format to the repository Actions tab.
|
||||
- name: "Upload artifact"
|
||||
uses: actions/upload-artifact@97a0fba1372883ab732affbe8f94b823f91727db # v3.pre.node20
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
|
|
|
@ -9,7 +9,7 @@ jobs:
|
|||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0
|
||||
- uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: 'This issue has been marked as stale because it has been open for 90 days with no activity. This thread will be automatically closed in 30 days if no further activity occurs.'
|
||||
|
|
|
@ -12,3 +12,5 @@ bin/
|
|||
vendor/
|
||||
# Ignores charts pulled for dependency build tests
|
||||
cmd/helm/testdata/testcharts/issue-7233/charts/*
|
||||
pkg/cmd/testdata/testcharts/issue-7233/charts/*
|
||||
.pre-commit-config.yaml
|
||||
|
|
|
@ -1,25 +1,63 @@
|
|||
version: "2"
|
||||
run:
|
||||
timeout: 10m
|
||||
|
||||
linters:
|
||||
disable-all: true
|
||||
default: none
|
||||
enable:
|
||||
- dupl
|
||||
- gofmt
|
||||
- goimports
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- misspell
|
||||
- nakedret
|
||||
- revive
|
||||
- unused
|
||||
- staticcheck
|
||||
|
||||
linters-settings:
|
||||
gofmt:
|
||||
simplify: true
|
||||
goimports:
|
||||
local-prefixes: helm.sh/helm/v3
|
||||
dupl:
|
||||
threshold: 400
|
||||
- unused
|
||||
settings:
|
||||
dupl:
|
||||
threshold: 400
|
||||
exclusions:
|
||||
# Helm, and the Go source code itself, sometimes uses these names outside their built-in
|
||||
# functions. As the Go source code has re-used these names it's ok for Helm to do the same.
|
||||
# Linting will look for redefinition of built-in id's but we opt-in to the ones we choose to use.
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- common-false-positives
|
||||
- legacy
|
||||
- std-error-handling
|
||||
rules:
|
||||
- linters:
|
||||
- revive
|
||||
text: 'redefines-builtin-id: redefinition of the built-in function append'
|
||||
- linters:
|
||||
- revive
|
||||
text: 'redefines-builtin-id: redefinition of the built-in function clear'
|
||||
- linters:
|
||||
- revive
|
||||
text: 'redefines-builtin-id: redefinition of the built-in function max'
|
||||
- linters:
|
||||
- revive
|
||||
text: 'redefines-builtin-id: redefinition of the built-in function min'
|
||||
- linters:
|
||||
- revive
|
||||
text: 'redefines-builtin-id: redefinition of the built-in function new'
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
formatters:
|
||||
enable:
|
||||
- gofmt
|
||||
- goimports
|
||||
settings:
|
||||
gofmt:
|
||||
simplify: true
|
||||
goimports:
|
||||
local-prefixes:
|
||||
- helm.sh/helm/v4
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
|
|
26
ADOPTERS.md
26
ADOPTERS.md
|
@ -5,15 +5,21 @@
|
|||
|
||||
# Organizations Using Helm
|
||||
|
||||
- [IBM](https://www.ibm.com)
|
||||
- [Microsoft](https://microsoft.com)
|
||||
- [Octopus Deploy](https://octopus.com/)
|
||||
- [New Relic](https://www.newrelic.com)
|
||||
- [Qovery](https://www.qovery.com/)
|
||||
- [Samsung SDS](https://www.samsungsds.com/)
|
||||
- [Softonic](https://hello.softonic.com/)
|
||||
- [Syself](https://syself.com)
|
||||
- [Ville de Montreal](https://montreal.ca)
|
||||
- [Intercept](https://Intercept.cloud)
|
||||
- [IBM](https://www.ibm.com)
|
||||
- [InfoCert](https://www.infocert.it/)
|
||||
- [Intercept](https://Intercept.cloud)
|
||||
- [Microsoft](https://microsoft.com)
|
||||
- [New Relic](https://www.newrelic.com)
|
||||
- [Octopus Deploy](https://octopus.com/)
|
||||
- [Omnistrate](https://omnistrate.com)
|
||||
- [Oracle](www.oracle.com)
|
||||
- [Percona](https://www.percona.com)
|
||||
- [Qovery](https://www.qovery.com/)
|
||||
- [Samsung SDS](https://www.samsungsds.com/)
|
||||
- [Softonic](https://hello.softonic.com/)
|
||||
- [SyncTune](https://mb-consulting.dev)
|
||||
- [Syself](https://syself.com)
|
||||
- [Ville de Montreal](https://montreal.ca)
|
||||
|
||||
|
||||
_This file is part of the CNCF official documentation for projects._
|
||||
|
|
|
@ -276,12 +276,26 @@ Like any good open source project, we use Pull Requests (PRs) to track code chan
|
|||
or explicitly request another OWNER do that for them.
|
||||
- If the owner of a PR is _not_ listed in `OWNERS`, any core maintainer may merge the PR.
|
||||
|
||||
#### Documentation PRs
|
||||
### Documentation PRs
|
||||
|
||||
Documentation PRs should be made on the docs repo: <https://github.com/helm/helm-www>. Keeping Helm's documentation up to date is highly desirable, and is recommended for all user facing changes. Accurate and helpful documentation is critical for effectively communicating Helm's behavior to a wide audience.
|
||||
|
||||
Small, ad-hoc changes/PRs to Helm which introduce user facing changes, which would benefit from documentation changes, should apply the `docs needed` label. Larger changes associated with a HIP should track docs via that HIP. The `docs needed` label doesn't block PRs, and maintainers/PR reviewers should apply discretion judging in whether the `docs needed` label should be applied.
|
||||
|
||||
### Profiling PRs
|
||||
|
||||
If your contribution requires profiling to check memory and/or CPU usage, you can set `HELM_PPROF_CPU_PROFILE=/path/to/cpu.prof` and/or `HELM_PPROF_MEM_PROFILE=/path/to/mem.prof` environment variables to collect runtime profiling data for analysis. You can use Golang's [pprof](https://github.com/google/pprof/blob/main/doc/README.md) tool to inspect the results.
|
||||
|
||||
Example analysing collected profiling data
|
||||
```
|
||||
HELM_PPROF_CPU_PROFILE=cpu.prof HELM_PPROF_MEM_PROFILE=mem.prof helm show all bitnami/nginx
|
||||
|
||||
# Visualize graphs. You need to have installed graphviz package in your system
|
||||
go tool pprof -http=":8000" cpu.prof
|
||||
|
||||
go tool pprof -http=":8001" mem.prof
|
||||
```
|
||||
|
||||
## The Triager
|
||||
|
||||
Each week, one of the core maintainers will serve as the designated "triager" starting after the
|
||||
|
|
21
KEYS
21
KEYS
|
@ -1037,3 +1037,24 @@ nk38BkgHg3LHjCbCNEVkSK2TMT69A58iwpY9WUQlphsiz4WBpafSPbv/jSlsm7uK
|
|||
TNWtbFGBRpJyEg==
|
||||
=w141
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
pub ed25519 2024-07-09 [SC]
|
||||
7FEC81FACC7FFB2A010ADD13C2D40F4D8196E874
|
||||
uid [ultimate] Robert Sirchia (I like turtles.) <rsirchia@outlook.com>
|
||||
sig 3 C2D40F4D8196E874 2024-07-09 [self-signature]
|
||||
sub cv25519 2024-07-09 [E]
|
||||
sig C2D40F4D8196E874 2024-07-09 [self-signature]
|
||||
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mDMEZo2C6xYJKwYBBAHaRw8BAQdA8kCWaI+FlCabcTw8EVeiMkokyWDalgl/Inbn
|
||||
ACcGN1e0N1JvYmVydCBTaXJjaGlhIChJIGxpa2UgdHVydGxlcy4pIDxyc2lyY2hp
|
||||
YUBvdXRsb29rLmNvbT6IkwQTFgoAOxYhBH/sgfrMf/sqAQrdE8LUD02Bluh0BQJm
|
||||
jYLrAhsDBQsJCAcCAiICBhUKCQgLAgQWAgMBAh4HAheAAAoJEMLUD02Bluh0dyYA
|
||||
/i7RB6m3MXNA8ei7GD8uQVpLfCRgEFsqSS/AzAOu8NGhAQCbw1kWL3AUll7KKtiQ
|
||||
UE96nhCk+HnkQeVkWYS+MZ1tALg4BGaNgusSCisGAQQBl1UBBQEBB0CCA6Au4krL
|
||||
YinQq9aAs29fFeRu/ye3PqQuz5jZ2r1ScAMBCAeIeAQYFgoAIBYhBH/sgfrMf/sq
|
||||
AQrdE8LUD02Bluh0BQJmjYLrAhsMAAoJEMLUD02Bluh0KH4BAMSwEIGkoQl10LN3
|
||||
K6V08VpFmniENmCDHshXYq0gGiTDAP9FsXl2UtmFU5xuYxH4fRKIxgmxJRAFMWI8
|
||||
u3Rdu/s+DQ==
|
||||
=smBO
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
|
|
18
Makefile
18
Makefile
|
@ -44,7 +44,7 @@ BINARY_VERSION ?= ${GIT_TAG}
|
|||
|
||||
# Only set Version if building a tag or VERSION is set
|
||||
ifneq ($(BINARY_VERSION),)
|
||||
LDFLAGS += -X helm.sh/helm/v3/internal/version.version=${BINARY_VERSION}
|
||||
LDFLAGS += -X helm.sh/helm/v4/internal/version.version=${BINARY_VERSION}
|
||||
endif
|
||||
|
||||
VERSION_METADATA = unreleased
|
||||
|
@ -53,9 +53,9 @@ ifneq ($(GIT_TAG),)
|
|||
VERSION_METADATA =
|
||||
endif
|
||||
|
||||
LDFLAGS += -X helm.sh/helm/v3/internal/version.metadata=${VERSION_METADATA}
|
||||
LDFLAGS += -X helm.sh/helm/v3/internal/version.gitCommit=${GIT_COMMIT}
|
||||
LDFLAGS += -X helm.sh/helm/v3/internal/version.gitTreeState=${GIT_DIRTY}
|
||||
LDFLAGS += -X helm.sh/helm/v4/internal/version.metadata=${VERSION_METADATA}
|
||||
LDFLAGS += -X helm.sh/helm/v4/internal/version.gitCommit=${GIT_COMMIT}
|
||||
LDFLAGS += -X helm.sh/helm/v4/internal/version.gitTreeState=${GIT_DIRTY}
|
||||
LDFLAGS += $(EXT_LDFLAGS)
|
||||
|
||||
# Define constants based on the client-go version
|
||||
|
@ -63,10 +63,10 @@ K8S_MODULES_VER=$(subst ., ,$(subst v,,$(shell go list -f '{{.Version}}' -m k8s.
|
|||
K8S_MODULES_MAJOR_VER=$(shell echo $$(($(firstword $(K8S_MODULES_VER)) + 1)))
|
||||
K8S_MODULES_MINOR_VER=$(word 2,$(K8S_MODULES_VER))
|
||||
|
||||
LDFLAGS += -X helm.sh/helm/v3/pkg/lint/rules.k8sVersionMajor=$(K8S_MODULES_MAJOR_VER)
|
||||
LDFLAGS += -X helm.sh/helm/v3/pkg/lint/rules.k8sVersionMinor=$(K8S_MODULES_MINOR_VER)
|
||||
LDFLAGS += -X helm.sh/helm/v3/pkg/chartutil.k8sVersionMajor=$(K8S_MODULES_MAJOR_VER)
|
||||
LDFLAGS += -X helm.sh/helm/v3/pkg/chartutil.k8sVersionMinor=$(K8S_MODULES_MINOR_VER)
|
||||
LDFLAGS += -X helm.sh/helm/v4/pkg/lint/rules.k8sVersionMajor=$(K8S_MODULES_MAJOR_VER)
|
||||
LDFLAGS += -X helm.sh/helm/v4/pkg/lint/rules.k8sVersionMinor=$(K8S_MODULES_MINOR_VER)
|
||||
LDFLAGS += -X helm.sh/helm/v4/pkg/chart/v2/util.k8sVersionMajor=$(K8S_MODULES_MAJOR_VER)
|
||||
LDFLAGS += -X helm.sh/helm/v4/pkg/chart/v2/util.k8sVersionMinor=$(K8S_MODULES_MINOR_VER)
|
||||
|
||||
.PHONY: all
|
||||
all: build
|
||||
|
@ -156,7 +156,7 @@ format: $(GOIMPORTS)
|
|||
# Generate golden files used in unit tests
|
||||
.PHONY: gen-test-golden
|
||||
gen-test-golden:
|
||||
gen-test-golden: PKG = ./cmd/helm ./pkg/action
|
||||
gen-test-golden: PKG = ./pkg/cmd ./pkg/action
|
||||
gen-test-golden: TESTFLAGS = -update
|
||||
gen-test-golden: test-unit
|
||||
|
||||
|
|
10
README.md
10
README.md
|
@ -1,8 +1,8 @@
|
|||
# Helm
|
||||
|
||||
[](https://github.com/helm/helm/actions?workflow=release)
|
||||
[](https://goreportcard.com/report/github.com/helm/helm)
|
||||
[](https://pkg.go.dev/helm.sh/helm/v3)
|
||||
[](https://goreportcard.com/report/helm.sh/helm/v4)
|
||||
[](https://pkg.go.dev/helm.sh/helm/v4)
|
||||
[](https://bestpractices.coreinfrastructure.org/projects/3131)
|
||||
[](https://scorecard.dev/viewer/?uri=github.com/helm/helm)
|
||||
|
||||
|
@ -29,6 +29,11 @@ Think of it like apt/yum/homebrew for Kubernetes.
|
|||
- Charts can be stored on disk, or fetched from remote chart repositories
|
||||
(like Debian or RedHat packages)
|
||||
|
||||
## Helm Development and Stable Versions
|
||||
|
||||
Helm v4 is currently under development on the `main` branch. This is unstable and the APIs within the Go SDK and at the command line are changing.
|
||||
Helm v3 (current stable) is maintained on the `dev-v3` branch. APIs there follow semantic versioning.
|
||||
|
||||
## Install
|
||||
|
||||
Binary downloads of the Helm client can be found on [the Releases page](https://github.com/helm/helm/releases/latest).
|
||||
|
@ -39,6 +44,7 @@ If you want to use a package manager:
|
|||
|
||||
- [Homebrew](https://brew.sh/) users can use `brew install helm`.
|
||||
- [Chocolatey](https://chocolatey.org/) users can use `choco install kubernetes-helm`.
|
||||
- [Winget](https://learn.microsoft.com/en-us/windows/package-manager/) users can use `winget install Helm.Helm`.
|
||||
- [Scoop](https://scoop.sh/) users can use `scoop install helm`.
|
||||
- [Snapcraft](https://snapcraft.io/) users can use `snap install helm --classic`.
|
||||
- [Flox](https://flox.dev) users can use `flox install kubernetes-helm`.
|
||||
|
|
|
@ -14,49 +14,19 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main // import "helm.sh/helm/v3/cmd/helm"
|
||||
package main // import "helm.sh/helm/v4/cmd/helm"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"log/slog"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
// Import to initialize client auth plugins.
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
"helm.sh/helm/v3/pkg/cli"
|
||||
"helm.sh/helm/v3/pkg/kube"
|
||||
kubefake "helm.sh/helm/v3/pkg/kube/fake"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"helm.sh/helm/v3/pkg/storage/driver"
|
||||
helmcmd "helm.sh/helm/v4/pkg/cmd"
|
||||
"helm.sh/helm/v4/pkg/kube"
|
||||
)
|
||||
|
||||
var settings = cli.New()
|
||||
|
||||
func init() {
|
||||
log.SetFlags(log.Lshortfile)
|
||||
}
|
||||
|
||||
func debug(format string, v ...interface{}) {
|
||||
if settings.Debug {
|
||||
timeNow := time.Now().String()
|
||||
format = fmt.Sprintf("%s [debug] %s\n", timeNow, format)
|
||||
log.Output(2, fmt.Sprintf(format, v...))
|
||||
}
|
||||
}
|
||||
|
||||
func warning(format string, v ...interface{}) {
|
||||
format = fmt.Sprintf("WARNING: %s\n", format)
|
||||
fmt.Fprintf(os.Stderr, format, v...)
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Setting the name of the app for managedFields in the Kubernetes client.
|
||||
// It is set here to the full name of "helm" so that renaming of helm to
|
||||
|
@ -64,69 +34,19 @@ func main() {
|
|||
// manager as picked up by the automated name detection.
|
||||
kube.ManagedFieldsManager = "helm"
|
||||
|
||||
actionConfig := new(action.Configuration)
|
||||
cmd, err := newRootCmd(actionConfig, os.Stdout, os.Args[1:])
|
||||
cmd, err := helmcmd.NewRootCmd(os.Stdout, os.Args[1:])
|
||||
if err != nil {
|
||||
warning("%+v", err)
|
||||
slog.Warn("command failed", slog.Any("error", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// run when each command's execute method is called
|
||||
cobra.OnInitialize(func() {
|
||||
helmDriver := os.Getenv("HELM_DRIVER")
|
||||
if err := actionConfig.Init(settings.RESTClientGetter(), settings.Namespace(), helmDriver, debug); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if helmDriver == "memory" {
|
||||
loadReleasesInMemory(actionConfig)
|
||||
}
|
||||
})
|
||||
|
||||
if err := cmd.Execute(); err != nil {
|
||||
debug("%+v", err)
|
||||
slog.Debug("error", slog.Any("error", err))
|
||||
switch e := err.(type) {
|
||||
case pluginError:
|
||||
os.Exit(e.code)
|
||||
case helmcmd.PluginError:
|
||||
os.Exit(e.Code)
|
||||
default:
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This function loads releases into the memory storage if the
|
||||
// environment variable is properly set.
|
||||
func loadReleasesInMemory(actionConfig *action.Configuration) {
|
||||
filePaths := strings.Split(os.Getenv("HELM_MEMORY_DRIVER_DATA"), ":")
|
||||
if len(filePaths) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
store := actionConfig.Releases
|
||||
mem, ok := store.Driver.(*driver.Memory)
|
||||
if !ok {
|
||||
// For an unexpected reason we are not dealing with the memory storage driver.
|
||||
return
|
||||
}
|
||||
|
||||
actionConfig.KubeClient = &kubefake.PrintingKubeClient{Out: io.Discard}
|
||||
|
||||
for _, path := range filePaths {
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
log.Fatal("Unable to read memory driver data", err)
|
||||
}
|
||||
|
||||
releases := []*release.Release{}
|
||||
if err := yaml.Unmarshal(b, &releases); err != nil {
|
||||
log.Fatal("Unable to unmarshal memory driver data: ", err)
|
||||
}
|
||||
|
||||
for _, rel := range releases {
|
||||
if err := store.Create(rel); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Must reset namespace to the proper one
|
||||
mem.SetNamespace(settings.Namespace())
|
||||
}
|
||||
|
|
|
@ -18,153 +18,12 @@ package main
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
shellwords "github.com/mattn/go-shellwords"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"helm.sh/helm/v3/internal/test"
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/cli"
|
||||
kubefake "helm.sh/helm/v3/pkg/kube/fake"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"helm.sh/helm/v3/pkg/storage"
|
||||
"helm.sh/helm/v3/pkg/storage/driver"
|
||||
"helm.sh/helm/v3/pkg/time"
|
||||
)
|
||||
|
||||
func testTimestamper() time.Time { return time.Unix(242085845, 0).UTC() }
|
||||
|
||||
func init() {
|
||||
action.Timestamper = testTimestamper
|
||||
}
|
||||
|
||||
func runTestCmd(t *testing.T, tests []cmdTestCase) {
|
||||
t.Helper()
|
||||
for _, tt := range tests {
|
||||
for i := 0; i <= tt.repeat; i++ {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
defer resetEnv()()
|
||||
|
||||
storage := storageFixture()
|
||||
for _, rel := range tt.rels {
|
||||
if err := storage.Create(rel); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
t.Logf("running cmd (attempt %d): %s", i+1, tt.cmd)
|
||||
_, out, err := executeActionCommandC(storage, tt.cmd)
|
||||
if tt.wantError && err == nil {
|
||||
t.Errorf("expected error, got success with the following output:\n%s", out)
|
||||
}
|
||||
if !tt.wantError && err != nil {
|
||||
t.Errorf("expected no error, got: '%v'", err)
|
||||
}
|
||||
if tt.golden != "" {
|
||||
test.AssertGoldenString(t, out, tt.golden)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func storageFixture() *storage.Storage {
|
||||
return storage.Init(driver.NewMemory())
|
||||
}
|
||||
|
||||
func executeActionCommandC(store *storage.Storage, cmd string) (*cobra.Command, string, error) {
|
||||
return executeActionCommandStdinC(store, nil, cmd)
|
||||
}
|
||||
|
||||
func executeActionCommandStdinC(store *storage.Storage, in *os.File, cmd string) (*cobra.Command, string, error) {
|
||||
args, err := shellwords.Parse(cmd)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
actionConfig := &action.Configuration{
|
||||
Releases: store,
|
||||
KubeClient: &kubefake.PrintingKubeClient{Out: io.Discard},
|
||||
Capabilities: chartutil.DefaultCapabilities,
|
||||
Log: func(_ string, _ ...interface{}) {},
|
||||
}
|
||||
|
||||
root, err := newRootCmd(actionConfig, buf, args)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
root.SetOut(buf)
|
||||
root.SetErr(buf)
|
||||
root.SetArgs(args)
|
||||
|
||||
oldStdin := os.Stdin
|
||||
if in != nil {
|
||||
root.SetIn(in)
|
||||
os.Stdin = in
|
||||
}
|
||||
|
||||
if mem, ok := store.Driver.(*driver.Memory); ok {
|
||||
mem.SetNamespace(settings.Namespace())
|
||||
}
|
||||
c, err := root.ExecuteC()
|
||||
|
||||
result := buf.String()
|
||||
|
||||
os.Stdin = oldStdin
|
||||
|
||||
return c, result, err
|
||||
}
|
||||
|
||||
// cmdTestCase describes a test case that works with releases.
|
||||
type cmdTestCase struct {
|
||||
name string
|
||||
cmd string
|
||||
golden string
|
||||
wantError bool
|
||||
// Rels are the available releases at the start of the test.
|
||||
rels []*release.Release
|
||||
// Number of repeats (in case a feature was previously flaky and the test checks
|
||||
// it's now stably producing identical results). 0 means test is run exactly once.
|
||||
repeat int
|
||||
}
|
||||
|
||||
func executeActionCommand(cmd string) (*cobra.Command, string, error) {
|
||||
return executeActionCommandC(storageFixture(), cmd)
|
||||
}
|
||||
|
||||
func resetEnv() func() {
|
||||
origEnv := os.Environ()
|
||||
return func() {
|
||||
os.Clearenv()
|
||||
for _, pair := range origEnv {
|
||||
kv := strings.SplitN(pair, "=", 2)
|
||||
os.Setenv(kv[0], kv[1])
|
||||
}
|
||||
settings = cli.New()
|
||||
}
|
||||
}
|
||||
|
||||
func testChdir(t *testing.T, dir string) func() {
|
||||
t.Helper()
|
||||
old, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.Chdir(dir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return func() { os.Chdir(old) }
|
||||
}
|
||||
|
||||
func TestPluginExitCode(t *testing.T) {
|
||||
if os.Getenv("RUN_MAIN_FOR_TESTING") == "1" {
|
||||
os.Args = []string{"helm", "exitwith", "2"}
|
||||
|
@ -190,10 +49,8 @@ func TestPluginExitCode(t *testing.T) {
|
|||
"RUN_MAIN_FOR_TESTING=1",
|
||||
// See pkg/cli/environment.go for which envvars can be used for configuring these passes
|
||||
// and also see plugin_test.go for how a plugin env can be set up.
|
||||
// We just does the same setup as plugin_test.go via envvars
|
||||
"HELM_PLUGINS=testdata/helmhome/helm/plugins",
|
||||
"HELM_REPOSITORY_CONFIG=testdata/helmhome/helm/repositories.yaml",
|
||||
"HELM_REPOSITORY_CACHE=testdata/helmhome/helm/repository",
|
||||
// This mimics the "exitwith" test case in TestLoadPlugins using envvars
|
||||
"HELM_PLUGINS=../../pkg/cmd/testdata/helmhome/helm/plugins",
|
||||
)
|
||||
stdout := &bytes.Buffer{}
|
||||
stderr := &bytes.Buffer{}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
{"name":"thomas-guide","chart":"foo","version":"0.1.0-beta.1","appVersion":"1.0","namespace":"default","revision":1,"status":"deployed","deployedAt":"1977-09-02T22:04:05Z"}
|
|
@ -1,8 +0,0 @@
|
|||
appVersion: "1.0"
|
||||
chart: foo
|
||||
deployedAt: "1977-09-02T22:04:05Z"
|
||||
name: thomas-guide
|
||||
namespace: default
|
||||
revision: 1
|
||||
status: deployed
|
||||
version: 0.1.0-beta.1
|
|
@ -1 +0,0 @@
|
|||
version.BuildInfo{Version:"v3.16", GitCommit:"", GitTreeState:"", GoVersion:""}
|
|
@ -1 +0,0 @@
|
|||
version.BuildInfo{Version:"v3.16", GitCommit:"", GitTreeState:"", GoVersion:""}
|
|
@ -1 +0,0 @@
|
|||
v3.16
|
|
@ -1 +0,0 @@
|
|||
Version: v3.16
|
|
@ -1 +0,0 @@
|
|||
version.BuildInfo{Version:"v3.16", GitCommit:"", GitTreeState:"", GoVersion:""}
|
185
go.mod
185
go.mod
|
@ -1,20 +1,20 @@
|
|||
module helm.sh/helm/v3
|
||||
module helm.sh/helm/v4
|
||||
|
||||
go 1.22.0
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24
|
||||
github.com/BurntSushi/toml v1.4.0
|
||||
github.com/BurntSushi/toml v1.5.0
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2
|
||||
github.com/Masterminds/semver/v3 v3.3.0
|
||||
github.com/Masterminds/sprig/v3 v3.3.0
|
||||
github.com/Masterminds/squirrel v1.5.4
|
||||
github.com/Masterminds/vcs v1.13.3
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
|
||||
github.com/containerd/containerd v1.7.23
|
||||
github.com/cyphar/filepath-securejoin v0.3.4
|
||||
github.com/distribution/distribution/v3 v3.0.0-rc.1
|
||||
github.com/evanphx/json-patch v5.9.0+incompatible
|
||||
github.com/cyphar/filepath-securejoin v0.4.1
|
||||
github.com/distribution/distribution/v3 v3.0.0
|
||||
github.com/evanphx/json-patch v5.9.11+incompatible
|
||||
github.com/fluxcd/cli-utils v0.36.0-flux.12
|
||||
github.com/foxcpp/go-mockdns v1.1.0
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/gofrs/flock v0.12.1
|
||||
|
@ -24,34 +24,35 @@ require (
|
|||
github.com/lib/pq v1.10.9
|
||||
github.com/mattn/go-shellwords v1.0.12
|
||||
github.com/mitchellh/copystructure v1.2.0
|
||||
github.com/moby/term v0.5.0
|
||||
github.com/opencontainers/image-spec v1.1.0
|
||||
github.com/moby/term v0.5.2
|
||||
github.com/opencontainers/image-spec v1.1.1
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/rubenv/sql-migrate v1.7.0
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/xeipuuv/gojsonschema v1.2.0
|
||||
golang.org/x/crypto v0.29.0
|
||||
golang.org/x/term v0.26.0
|
||||
golang.org/x/text v0.20.0
|
||||
k8s.io/api v0.31.2
|
||||
k8s.io/apiextensions-apiserver v0.31.2
|
||||
k8s.io/apimachinery v0.31.2
|
||||
k8s.io/apiserver v0.31.2
|
||||
k8s.io/cli-runtime v0.31.2
|
||||
k8s.io/client-go v0.31.2
|
||||
github.com/rubenv/sql-migrate v1.8.0
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/spf13/pflag v1.0.6
|
||||
github.com/stretchr/testify v1.10.0
|
||||
golang.org/x/crypto v0.37.0
|
||||
golang.org/x/term v0.31.0
|
||||
golang.org/x/text v0.24.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/api v0.32.3
|
||||
k8s.io/apiextensions-apiserver v0.32.3
|
||||
k8s.io/apimachinery v0.32.3
|
||||
k8s.io/apiserver v0.32.3
|
||||
k8s.io/cli-runtime v0.32.3
|
||||
k8s.io/client-go v0.32.3
|
||||
k8s.io/klog/v2 v2.130.1
|
||||
k8s.io/kubectl v0.31.2
|
||||
oras.land/oras-go v1.2.5
|
||||
k8s.io/kubectl v0.32.3
|
||||
oras.land/oras-go/v2 v2.5.0
|
||||
sigs.k8s.io/controller-runtime v0.20.4
|
||||
sigs.k8s.io/yaml v1.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.1 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
|
||||
github.com/MakeNowJust/heredoc v1.0.0 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
|
@ -60,131 +61,123 @@ require (
|
|||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/chai2010/gettext-go v1.0.2 // indirect
|
||||
github.com/containerd/errdefs v0.3.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/platforms v0.2.1 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/docker/cli v25.0.1+incompatible // indirect
|
||||
github.com/docker/distribution v2.8.3+incompatible // indirect
|
||||
github.com/docker/docker v25.0.6+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.8.2 // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
|
||||
github.com/docker/go-metrics v0.0.1 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.12.1 // indirect
|
||||
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
|
||||
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
|
||||
github.com/fatih/color v1.13.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/go-errors/errors v1.4.2 // 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.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.22.4 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/google/gnostic-models v0.6.9 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/handlers v1.5.2 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/golang-lru/arc/v2 v2.0.5 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.5 // indirect
|
||||
github.com/huandu/xstrings v1.5.0 // indirect
|
||||
github.com/imdario/mergo v0.3.16 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.17.9 // indirect
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
|
||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
|
||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mailru/easyjson v0.9.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/miekg/dns v1.1.57 // 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.4.0 // indirect
|
||||
github.com/moby/spdystream 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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
|
||||
github.com/onsi/gomega v1.36.2 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_golang v1.20.1 // indirect
|
||||
github.com/prometheus/client_golang v1.20.5 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.55.0 // indirect
|
||||
github.com/prometheus/common v0.62.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 // indirect
|
||||
github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 // indirect
|
||||
github.com/redis/go-redis/v9 v9.1.0 // indirect
|
||||
github.com/redis/go-redis/v9 v9.7.3 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/shopspring/decimal v1.4.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/spf13/cast v1.7.0 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xlab/treeprint v1.2.0 // indirect
|
||||
go.opentelemetry.io/contrib/bridges/prometheus v0.54.0 // indirect
|
||||
go.opentelemetry.io/contrib/exporters/autoexport v0.54.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
|
||||
go.opentelemetry.io/otel v1.29.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.5.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.29.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.29.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.29.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.29.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.51.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.5.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.29.0 // indirect
|
||||
go.opentelemetry.io/otel/log v0.5.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.29.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.29.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/log v0.5.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.29.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.29.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/bridges/prometheus v0.57.0 // indirect
|
||||
go.opentelemetry.io/contrib/exporters/autoexport v0.57.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 // indirect
|
||||
go.opentelemetry.io/otel v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.54.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.8.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.32.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.32.0 // indirect
|
||||
go.opentelemetry.io/otel/log v0.8.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.32.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/log v0.8.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.32.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.34.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
||||
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/net v0.29.0 // indirect
|
||||
golang.org/x/oauth2 v0.23.0 // indirect
|
||||
golang.org/x/sync v0.9.0 // indirect
|
||||
golang.org/x/sys v0.27.0 // indirect
|
||||
golang.org/x/time v0.6.0 // indirect
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
|
||||
google.golang.org/grpc v1.66.2 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
golang.org/x/mod v0.22.0 // indirect
|
||||
golang.org/x/net v0.38.0 // indirect
|
||||
golang.org/x/oauth2 v0.28.0 // indirect
|
||||
golang.org/x/sync v0.13.0 // indirect
|
||||
golang.org/x/sys v0.32.0 // indirect
|
||||
golang.org/x/time v0.9.0 // indirect
|
||||
golang.org/x/tools v0.29.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // indirect
|
||||
google.golang.org/grpc v1.68.0 // indirect
|
||||
google.golang.org/protobuf v1.36.4 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/component-base v0.31.2 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||
sigs.k8s.io/kustomize/api v0.17.2 // indirect
|
||||
sigs.k8s.io/kustomize/kyaml v0.17.1 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
|
||||
k8s.io/component-base v0.32.3 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 // indirect
|
||||
k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
||||
sigs.k8s.io/kustomize/api v0.18.0 // indirect
|
||||
sigs.k8s.io/kustomize/kyaml v0.19.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect
|
||||
)
|
||||
|
|
473
go.sum
473
go.sum
|
@ -1,15 +1,13 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
||||
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
||||
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
|
||||
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
|
||||
|
@ -24,10 +22,6 @@ github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8
|
|||
github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
|
||||
github.com/Masterminds/vcs v1.13.3 h1:IIA2aBdXvfbIM+yl/eTnL4hb1XwdpvuQLglAix1gweE=
|
||||
github.com/Masterminds/vcs v1.13.3/go.mod h1:TiE7xuEjl1N4j016moRd6vezp6e6Lz23gypeXfzXeW8=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/Microsoft/hcsshim v0.11.7 h1:vl/nj3Bar/CvJSYo7gIQPyRWc9f3c6IeSNavBTSZNZQ=
|
||||
github.com/Microsoft/hcsshim v0.11.7/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA09d4bExKcU=
|
||||
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=
|
||||
|
@ -43,89 +37,66 @@ github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2y
|
|||
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/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w=
|
||||
github.com/bsm/ginkgo/v2 v2.9.5 h1:rtVBYPs3+TC5iLUVOis1B9tjLTup7Cj5IfzosKtvTJ0=
|
||||
github.com/bsm/ginkgo/v2 v2.9.5/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||
github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||
github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk=
|
||||
github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
|
||||
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
|
||||
github.com/containerd/containerd v1.7.23 h1:H2CClyUkmpKAGlhQp95g2WXHfLYc7whAuvZGBNYOOwQ=
|
||||
github.com/containerd/containerd v1.7.23/go.mod h1:7QUzfURqZWCZV7RLNEn1XjUCQLEf0bkaK4GjUaZehxw=
|
||||
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/containerd/errdefs v0.3.0 h1:FSZgGOeK4yuT/+DnF07/Olde/q4KBoMsaamhXxIMDp4=
|
||||
github.com/containerd/errdefs v0.3.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
|
||||
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
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.3.4 h1:VBWugsJh2ZxJmLFSM06/0qzQyiQX2Qs0ViKrUAcqdZ8=
|
||||
github.com/cyphar/filepath-securejoin v0.3.4/go.mod h1:8s/MCNJREmFK0H02MF6Ihv1nakJe4L/w3WZLHNkvlYM=
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
|
||||
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/distribution/distribution/v3 v3.0.0-rc.1 h1:6M4ewmPBUhF7wtQ8URLOQ1W/PQuVKiD1u8ymwLDUGqQ=
|
||||
github.com/distribution/distribution/v3 v3.0.0-rc.1/go.mod h1:tFjaPDeHCrLg28e4feBIy27cP+qmrc/mvkl6MFIfVi4=
|
||||
github.com/distribution/distribution/v3 v3.0.0 h1:q4R8wemdRQDClzoNNStftB2ZAfqOiN6UX90KJc4HjyM=
|
||||
github.com/distribution/distribution/v3 v3.0.0/go.mod h1:tRNuFoZsUdyRVegq8xGNeds4KLjwLCRin/tTo6i1DhU=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/cli v25.0.1+incompatible h1:mFpqnrS6Hsm3v1k7Wa/BO23oz0k121MTbTO1lpcGSkU=
|
||||
github.com/docker/cli v25.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
|
||||
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v25.0.6+incompatible h1:5cPwbwriIcsua2REJe8HqQV+6WlWc1byg2QSXzBxBGg=
|
||||
github.com/docker/docker v25.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
|
||||
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
|
||||
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
|
||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||
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/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4=
|
||||
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls=
|
||||
github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM=
|
||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
|
||||
github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU=
|
||||
github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8=
|
||||
github.com/evanphx/json-patch v5.9.11+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU=
|
||||
github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM=
|
||||
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.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fluxcd/cli-utils v0.36.0-flux.12 h1:8cD6SmaKa/lGo0KCu0XWiGrXJMLMBQwSsnoP0cG+Gjw=
|
||||
github.com/fluxcd/cli-utils v0.36.0-flux.12/go.mod h1:Nb/zMqsJAzjz4/HIsEc2LTqxC6eC0rV26t4hkJT/F9o=
|
||||
github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7DlmewI=
|
||||
github.com/foxcpp/go-mockdns v1.1.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||
github.com/go-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=
|
||||
|
@ -136,13 +107,14 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
|||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
|
||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||
github.com/go-openapi/swag v0.22.3/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-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
|
||||
github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
|
||||
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
|
||||
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
|
||||
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
|
@ -156,39 +128,24 @@ github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeH
|
|||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
||||
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
||||
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
|
||||
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
|
||||
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM=
|
||||
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
|
||||
github.com/google/pprof v0.0.0-20250128161936-077ca0a936bf h1:BvBLUD2hkvLI3dJTJMiopAq8/wp43AAZKTP7qdpptbU=
|
||||
github.com/google/pprof v0.0.0-20250128161936-077ca0a936bf/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
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.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
|
@ -197,14 +154,14 @@ github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyE
|
|||
github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY=
|
||||
github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I=
|
||||
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/grpc-ecosystem/grpc-gateway/v2 v2.23.0 h1:ad0vkEBuk23VJzZR9nkLVG0YAoN9coASF1GusYX6AlU=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0/go.mod h1:igFoXX2ELCW06bol23DWPB5BEWfZISOzSP5K2sbLea0=
|
||||
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=
|
||||
|
@ -216,8 +173,6 @@ github.com/hashicorp/golang-lru/v2 v2.0.5 h1:wW7h1TG88eUIJ2i69gaE3uNVtEPIagzhGvH
|
|||
github.com/hashicorp/golang-lru/v2 v2.0.5/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
|
||||
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
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.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
|
||||
|
@ -232,15 +187,12 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
|
|||
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/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
|
||||
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
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/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
|
@ -253,8 +205,8 @@ 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/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
|
||||
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
|
@ -278,16 +230,10 @@ github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQ
|
|||
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
|
||||
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.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8=
|
||||
github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
|
||||
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/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
|
||||
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
|
||||
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/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU=
|
||||
github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
|
||||
github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
|
||||
github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
|
||||
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=
|
||||
|
@ -302,14 +248,14 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m
|
|||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||
github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
|
||||
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
|
||||
github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
|
||||
github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
|
||||
github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
|
||||
github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
|
||||
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
|
||||
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
|
||||
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 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||
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=
|
||||
|
@ -325,17 +271,16 @@ github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjz
|
|||
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.20.1 h1:IMJXHOD6eARkQpxo8KkhgEVFlBNm+nkrFUyGlIu7Na8=
|
||||
github.com/prometheus/client_golang v1.20.1/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
|
||||
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
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.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
|
||||
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
|
||||
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
|
||||
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
||||
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=
|
||||
|
@ -346,14 +291,16 @@ github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5/go.mod h1:fyalQWdtzDBECAQFBJu
|
|||
github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 h1:EfpWLLCyXw8PSM2/XNJLjI3Pb27yVE+gIAfeqp8LUCc=
|
||||
github.com/redis/go-redis/extra/redisotel/v9 v9.0.5/go.mod h1:WZjPDy7VNzn77AAfnAfVjZNvfJTYfPetfZk5yoSTLaQ=
|
||||
github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk=
|
||||
github.com/redis/go-redis/v9 v9.1.0 h1:137FnGdk+EQdCbye1FW+qOEcY5S+SpY9T0NiuqvtfMY=
|
||||
github.com/redis/go-redis/v9 v9.1.0/go.mod h1:urWj3He21Dj5k4TK1y59xH8Uj6ATueP8AH1cY3lZl4c=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/rubenv/sql-migrate v1.7.0 h1:HtQq1xyTN2ISmQDggnh0c9U3JlP8apWh8YO2jzlXpTI=
|
||||
github.com/rubenv/sql-migrate v1.7.0/go.mod h1:S4wtDEG1CKn+0ShpTtzWhFpHHI5PvCUtiGI+C+Z2THE=
|
||||
github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
|
||||
github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/rubenv/sql-migrate v1.8.0 h1:dXnYiJk9k3wetp7GfQbKJcPHjVJL6YK19tKj8t2Ns0o=
|
||||
github.com/rubenv/sql-migrate v1.8.0/go.mod h1:F2bGFBwCU+pnmbtNYDeKvSuvL6lBVtXDXUUv5t+u1qw=
|
||||
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/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw=
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
|
||||
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
|
||||
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||
|
@ -363,86 +310,79 @@ 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.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
|
||||
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/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/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
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.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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
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=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/contrib/bridges/prometheus v0.54.0 h1:WWL67oxtknNVMb70lJXxXruf8UyK/a9hmIE1XO3Uedg=
|
||||
go.opentelemetry.io/contrib/bridges/prometheus v0.54.0/go.mod h1:LqNcnXmyULp8ertk4hUTVtSUvKXj4h1Mx7gUCSSr/q0=
|
||||
go.opentelemetry.io/contrib/exporters/autoexport v0.54.0 h1:dTmcmVm4J54IRPGm5oVjLci1uYat4UDea84E2tyBaAk=
|
||||
go.opentelemetry.io/contrib/exporters/autoexport v0.54.0/go.mod h1:zPp5Fwpq2Hc7xMtVttg6GhZMcfTESjVbY9ONw2o/Dc4=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
|
||||
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
|
||||
go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.5.0 h1:4d++HQ+Ihdl+53zSjtsCUFDmNMju2FC9qFkUlTxPLqo=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.5.0/go.mod h1:mQX5dTO3Mh5ZF7bPKDkt5c/7C41u/SiDr9XgTpzXXn8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.29.0 h1:k6fQVDQexDE+3jG2SfCQjnHS7OamcP73YMoxEVq5B6k=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.29.0/go.mod h1:t4BrYLHU450Zo9fnydWlIuswB1bm7rM8havDpWOJeDo=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.29.0 h1:xvhQxJ/C9+RTnAj5DpTg7LSM1vbbMTiXt7e9hsfqHNw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.29.0/go.mod h1:Fcvs2Bz1jkDM+Wf5/ozBGmi3tQ/c9zPKLnsipnfhGAo=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0 h1:dIIDULZJpgdiHz5tXrTgKIMLkus6jEFa7x5SOKcyR7E=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0/go.mod h1:jlRVBe7+Z1wyxFSUs48L6OBQZ5JwH2Hg/Vbl+t9rAgI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.29.0 h1:nSiV3s7wiCam610XcLbYOmMfJxB9gO4uK3Xgv5gmTgg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.29.0/go.mod h1:hKn/e/Nmd19/x1gvIHwtOwVWM+VhuITSWip3JUDghj0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.29.0 h1:JAv0Jwtl01UFiyWZEMiJZBiTlv5A50zNs8lsthXqIio=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.29.0/go.mod h1:QNKLmUEAq2QUbPQUfvw4fmv0bgbK7UlOSFCnXyfvSNc=
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.51.0 h1:G7uexXb/K3T+T9fNLCCKncweEtNEBMTO+46hKX5EdKw=
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.51.0/go.mod h1:v0mFe5Kk7woIh938mrZBJBmENYquyA0IICrlYm4Y0t4=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.5.0 h1:ThVXnEsdwNcxdBO+r96ci1xbF+PgNjwlk457VNuJODo=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.5.0/go.mod h1:rHWcSmC4q2h3gje/yOq6sAOaq8+UHxN/Ru3BbmDXOfY=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 h1:WDdP9acbMYjbKIyJUhTvtzj601sVJOqgWdUxSdR/Ysc=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0/go.mod h1:BLbf7zbNIONBLPwvFnwNHGj4zge8uTCM/UPIVW1Mq2I=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.29.0 h1:X3ZjNp36/WlkSYx0ul2jw4PtbNEDDeLskw3VPsrpYM0=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.29.0/go.mod h1:2uL/xnOXh0CHOBFCWXz5u1A4GXLiW+0IQIzVbeOEQ0U=
|
||||
go.opentelemetry.io/otel/log v0.5.0 h1:x1Pr6Y3gnXgl1iFBwtGy1W/mnzENoK0w0ZoaeOI3i30=
|
||||
go.opentelemetry.io/otel/log v0.5.0/go.mod h1:NU/ozXeGuOR5/mjCRXYbTC00NFJ3NYuraV/7O78F0rE=
|
||||
go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc=
|
||||
go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
|
||||
go.opentelemetry.io/otel/sdk v1.29.0 h1:vkqKjk7gwhS8VaWb0POZKmIEDimRCMsopNYnriHyryo=
|
||||
go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok=
|
||||
go.opentelemetry.io/otel/sdk/log v0.5.0 h1:A+9lSjlZGxkQOr7QSBJcuyyYBw79CufQ69saiJLey7o=
|
||||
go.opentelemetry.io/otel/sdk/log v0.5.0/go.mod h1:zjxIW7sw1IHolZL2KlSAtrUi8JHttoeiQy43Yl3WuVQ=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.29.0 h1:K2CfmJohnRgvZ9UAj2/FhIf/okdWcNdBwe1m8xFXiSY=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.29.0/go.mod h1:6zZLdCl2fkauYoZIOn/soQIDSWFmNSRcICarHfuhNJQ=
|
||||
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
|
||||
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/bridges/prometheus v0.57.0 h1:UW0+QyeyBVhn+COBec3nGhfnFe5lwB0ic1JBVjzhk0w=
|
||||
go.opentelemetry.io/contrib/bridges/prometheus v0.57.0/go.mod h1:ppciCHRLsyCio54qbzQv0E4Jyth/fLWDTJYfvWpcSVk=
|
||||
go.opentelemetry.io/contrib/exporters/autoexport v0.57.0 h1:jmTVJ86dP60C01K3slFQa2NQ/Aoi7zA+wy7vMOKD9H4=
|
||||
go.opentelemetry.io/contrib/exporters/autoexport v0.57.0/go.mod h1:EJBheUMttD/lABFyLXhce47Wr6DPWYReCzaZiXadH7g=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 h1:DheMAlT6POBP+gh8RUH19EOTnQIor5QE0uSRPtzCpSw=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0/go.mod h1:wZcGmeVO9nzP67aYSLDqXNWK87EZWhi7JWj1v7ZXf94=
|
||||
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
|
||||
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 h1:WzNab7hOOLzdDF/EoWCt4glhrbMPVMOO5JYTmpz36Ls=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0/go.mod h1:hKvJwTzJdp90Vh7p6q/9PAOd55dI6WA6sWj62a/JvSs=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0 h1:S+LdBGiQXtJdowoJoQPEtI52syEP/JYBUpjO49EQhV8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0/go.mod h1:5KXybFvPGds3QinJWQT7pmXf+TN5YIa7CNYObWRkj50=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0 h1:j7ZSD+5yn+lo3sGV69nW04rRR0jhYnBwjuX3r0HvnK0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0/go.mod h1:WXbYJTUaZXAbYd8lbgGuvih0yuCfOFC5RJoYnoLcGz8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0 h1:t/Qur3vKSkUCcDVaSumWF2PKHt85pc7fRvFuoVT8qFU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0/go.mod h1:Rl61tySSdcOJWoEgYZVtmnKdA0GeKrSqkHC1t+91CH8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 h1:IJFEoHiytixx8cMiVAO+GmHR6Frwu+u5Ur8njpFO6Ac=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0/go.mod h1:3rHrKNtLIoS0oZwkY2vxi+oJcwFRWdtUyRII+so45p8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0 h1:9kV11HXBHZAvuPUZxmMWrH8hZn/6UnHX4K0mu36vNsU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0/go.mod h1:JyA0FHXe22E1NeNiHmVp7kFHglnexDQ7uRWDiiJ1hKQ=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0 h1:cMyu9O88joYEaI47CnQkxO1XZdpoTF9fEnW2duIddhw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0/go.mod h1:6Am3rn7P9TVVeXYG+wtcGE7IE1tsQ+bP3AuWcKt/gOI=
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.54.0 h1:rFwzp68QMgtzu9PgP3jm9XaMICI6TsofWWPcBDKwlsU=
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.54.0/go.mod h1:QyjcV9qDP6VeK5qPyKETvNjmaaEc7+gqjh4SS0ZYzDU=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.8.0 h1:CHXNXwfKWfzS65yrlB2PVds1IBZcdsX8Vepy9of0iRU=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.8.0/go.mod h1:zKU4zUgKiaRxrdovSS2amdM5gOc59slmo/zJwGX+YBg=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.32.0 h1:SZmDnHcgp3zwlPBS2JX2urGYe/jBKEIT6ZedHRUyCz8=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.32.0/go.mod h1:fdWW0HtZJ7+jNpTKUR0GpMEDP69nR8YBJQxNiVCE3jk=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.32.0 h1:cC2yDI3IQd0Udsux7Qmq8ToKAx1XCilTQECZ0KDZyTw=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.32.0/go.mod h1:2PD5Ex6z8CFzDbTdOlwyNIUywRr1DN0ospafJM1wJ+s=
|
||||
go.opentelemetry.io/otel/log v0.8.0 h1:egZ8vV5atrUWUbnSsHn6vB8R21G2wrKqNiDt3iWertk=
|
||||
go.opentelemetry.io/otel/log v0.8.0/go.mod h1:M9qvDdUTRCopJcGRKg57+JSQ9LgLBrwwfC32epk5NX8=
|
||||
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
|
||||
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
|
||||
go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
|
||||
go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
|
||||
go.opentelemetry.io/otel/sdk/log v0.8.0 h1:zg7GUYXqxk1jnGF/dTdLPrK06xJdrXgqgFLnI4Crxvs=
|
||||
go.opentelemetry.io/otel/sdk/log v0.8.0/go.mod h1:50iXr0UVwQrYS45KbruFrEt4LvAdCaWWgIrsN3ZQggo=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
|
||||
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
|
||||
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
||||
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY=
|
||||
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
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=
|
||||
|
@ -451,25 +391,17 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
|
|||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
|
||||
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
||||
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
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.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-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=
|
||||
|
@ -482,12 +414,10 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
|||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
|
||||
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
|
||||
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
|
||||
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
|
||||
golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
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=
|
||||
|
@ -498,9 +428,8 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
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=
|
||||
|
@ -522,18 +451,17 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
|
||||
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
|
||||
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
|
||||
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
|
||||
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
|
||||
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=
|
||||
|
@ -541,15 +469,11 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
|||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
|
||||
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
|
||||
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
||||
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
||||
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
|
@ -557,36 +481,20 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
|||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
|
||||
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
|
||||
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.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 h1:hjSy6tcFQZ171igDaN5QHOw2n6vx40juYbC/x67CEhc=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo=
|
||||
google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 h1:M0KvPgPmDZHPlbRbaNU1APr28TvwvvdUPlSv7PUvy8g=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:dguCy7UOdZhTvLzDyt15+rOrawrpM4q7DD9dQ1P11P4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 h1:XVhgTWWV3kGQlwJHR3upFWZeTsei6Oks1apkZSeonIE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
||||
google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0=
|
||||
google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA=
|
||||
google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
|
||||
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
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=
|
||||
|
@ -596,47 +504,44 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWM
|
|||
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.8/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=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0=
|
||||
k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk=
|
||||
k8s.io/apiextensions-apiserver v0.31.2 h1:W8EwUb8+WXBLu56ser5IudT2cOho0gAKeTOnywBLxd0=
|
||||
k8s.io/apiextensions-apiserver v0.31.2/go.mod h1:i+Geh+nGCJEGiCGR3MlBDkS7koHIIKWVfWeRFiOsUcM=
|
||||
k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw=
|
||||
k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
|
||||
k8s.io/apiserver v0.31.2 h1:VUzOEUGRCDi6kX1OyQ801m4A7AUPglpsmGvdsekmcI4=
|
||||
k8s.io/apiserver v0.31.2/go.mod h1:o3nKZR7lPlJqkU5I3Ove+Zx3JuoFjQobGX1Gctw6XuE=
|
||||
k8s.io/cli-runtime v0.31.2 h1:7FQt4C4Xnqx8V1GJqymInK0FFsoC+fAZtbLqgXYVOLQ=
|
||||
k8s.io/cli-runtime v0.31.2/go.mod h1:XROyicf+G7rQ6FQJMbeDV9jqxzkWXTYD6Uxd15noe0Q=
|
||||
k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc=
|
||||
k8s.io/client-go v0.31.2/go.mod h1:NPa74jSVR/+eez2dFsEIHNa+3o09vtNaWwWwb1qSxSs=
|
||||
k8s.io/component-base v0.31.2 h1:Z1J1LIaC0AV+nzcPRFqfK09af6bZ4D1nAOpWsy9owlA=
|
||||
k8s.io/component-base v0.31.2/go.mod h1:9PeyyFN/drHjtJZMCTkSpQJS3U9OXORnHQqMLDz0sUQ=
|
||||
k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls=
|
||||
k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k=
|
||||
k8s.io/apiextensions-apiserver v0.32.3 h1:4D8vy+9GWerlErCwVIbcQjsWunF9SUGNu7O7hiQTyPY=
|
||||
k8s.io/apiextensions-apiserver v0.32.3/go.mod h1:8YwcvVRMVzw0r1Stc7XfGAzB/SIVLunqApySV5V7Dss=
|
||||
k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U=
|
||||
k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
|
||||
k8s.io/apiserver v0.32.3 h1:kOw2KBuHOA+wetX1MkmrxgBr648ksz653j26ESuWNY8=
|
||||
k8s.io/apiserver v0.32.3/go.mod h1:q1x9B8E/WzShF49wh3ADOh6muSfpmFL0I2t+TG0Zdgc=
|
||||
k8s.io/cli-runtime v0.32.3 h1:khLF2ivU2T6Q77H97atx3REY9tXiA3OLOjWJxUrdvss=
|
||||
k8s.io/cli-runtime v0.32.3/go.mod h1:vZT6dZq7mZAca53rwUfdFSZjdtLyfF61mkf/8q+Xjak=
|
||||
k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU=
|
||||
k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY=
|
||||
k8s.io/component-base v0.32.3 h1:98WJvvMs3QZ2LYHBzvltFSeJjEx7t5+8s71P7M74u8k=
|
||||
k8s.io/component-base v0.32.3/go.mod h1:LWi9cR+yPAv7cu2X9rZanTiFKB2kHA+JjmhkKjCZRpI=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
|
||||
k8s.io/kubectl v0.31.2 h1:gTxbvRkMBwvTSAlobiTVqsH6S8Aa1aGyBcu5xYLsn8M=
|
||||
k8s.io/kubectl v0.31.2/go.mod h1:EyASYVU6PY+032RrTh5ahtSOMgoDRIux9V1JLKtG5xM=
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
oras.land/oras-go v1.2.5 h1:XpYuAwAb0DfQsunIyMfeET92emK8km3W4yEzZvUbsTo=
|
||||
oras.land/oras-go v1.2.5/go.mod h1:PuAwRShRZCsZb7g8Ar3jKKQR/2A/qN+pkYxIOd/FAoo=
|
||||
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.17.2 h1:E7/Fjk7V5fboiuijoZHgs4aHuexi5Y2loXlVOAVAG5g=
|
||||
sigs.k8s.io/kustomize/api v0.17.2/go.mod h1:UWTz9Ct+MvoeQsHcJ5e+vziRRkwimm3HytpZgIYqye0=
|
||||
sigs.k8s.io/kustomize/kyaml v0.17.1 h1:TnxYQxFXzbmNG6gOINgGWQt09GghzgTP6mIurOgrLCQ=
|
||||
sigs.k8s.io/kustomize/kyaml v0.17.1/go.mod h1:9V0mCjIEYjlXuCdYsSXvyoy2BTsLESH7TlGV81S282U=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
|
||||
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 h1:hcha5B1kVACrLujCKLbr8XWMxCxzQx42DY8QKYJrDLg=
|
||||
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7/go.mod h1:GewRfANuJ70iYzvn+i4lezLDAFzvjxZYK1gn1lWcfas=
|
||||
k8s.io/kubectl v0.32.3 h1:VMi584rbboso+yjfv0d8uBHwwxbC438LKq+dXd5tOAI=
|
||||
k8s.io/kubectl v0.32.3/go.mod h1:6Euv2aso5GKzo/UVMacV6C7miuyevpfI91SvBvV9Zdg=
|
||||
k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0=
|
||||
k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
oras.land/oras-go/v2 v2.5.0 h1:o8Me9kLY74Vp5uw07QXPiitjsw7qNXi8Twd+19Zf02c=
|
||||
oras.land/oras-go/v2 v2.5.0/go.mod h1:z4eisnLP530vwIOUOJeBIj0aGI0L1C3d53atvCBqZHg=
|
||||
sigs.k8s.io/controller-runtime v0.20.4 h1:X3c+Odnxz+iPTRobG4tp092+CvBU9UK0t/bRf+n0DGU=
|
||||
sigs.k8s.io/controller-runtime v0.20.4/go.mod h1:xg2XB0K5ShQzAgsoujxuKN4LNXR2LfwwHsPj7Iaw+XY=
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
|
||||
sigs.k8s.io/kustomize/api v0.18.0 h1:hTzp67k+3NEVInwz5BHyzc9rGxIauoXferXyjv5lWPo=
|
||||
sigs.k8s.io/kustomize/api v0.18.0/go.mod h1:f8isXnX+8b+SGLHQ6yO4JG1rdkZlvhaCf/uZbLVMb0U=
|
||||
sigs.k8s.io/kustomize/kyaml v0.19.0 h1:RFge5qsO1uHhwJsu3ipV7RNolC7Uozc0jUBC/61XSlA=
|
||||
sigs.k8s.io/kustomize/kyaml v0.19.0/go.mod h1:FeKD5jEOH+FbZPpqUghBP8mrLjJ3+zD3/rf9NNu1cwY=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 h1:nbCitCK2hfnhyiKo6uf2HxUPTCodY6Qaf85SbDIaMBk=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.5.0/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
|
|
|
@ -21,7 +21,7 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"helm.sh/helm/v3/internal/third_party/dep/fs"
|
||||
"helm.sh/helm/v4/internal/third_party/dep/fs"
|
||||
)
|
||||
|
||||
// AtomicWriteFile atomically (as atomic as os.Rename allows) writes a file to a
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package logging
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"os"
|
||||
)
|
||||
|
||||
// DebugEnabledFunc is a function type that determines if debug logging is enabled
|
||||
// We use a function because we want to check the setting at log time, not when the logger is created
|
||||
type DebugEnabledFunc func() bool
|
||||
|
||||
// DebugCheckHandler checks settings.Debug at log time
|
||||
type DebugCheckHandler struct {
|
||||
handler slog.Handler
|
||||
debugEnabled DebugEnabledFunc
|
||||
}
|
||||
|
||||
// Enabled implements slog.Handler.Enabled
|
||||
func (h *DebugCheckHandler) Enabled(_ context.Context, level slog.Level) bool {
|
||||
if level == slog.LevelDebug {
|
||||
return h.debugEnabled()
|
||||
}
|
||||
return true // Always log other levels
|
||||
}
|
||||
|
||||
// Handle implements slog.Handler.Handle
|
||||
func (h *DebugCheckHandler) Handle(ctx context.Context, r slog.Record) error {
|
||||
return h.handler.Handle(ctx, r)
|
||||
}
|
||||
|
||||
// WithAttrs implements slog.Handler.WithAttrs
|
||||
func (h *DebugCheckHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
|
||||
return &DebugCheckHandler{
|
||||
handler: h.handler.WithAttrs(attrs),
|
||||
debugEnabled: h.debugEnabled,
|
||||
}
|
||||
}
|
||||
|
||||
// WithGroup implements slog.Handler.WithGroup
|
||||
func (h *DebugCheckHandler) WithGroup(name string) slog.Handler {
|
||||
return &DebugCheckHandler{
|
||||
handler: h.handler.WithGroup(name),
|
||||
debugEnabled: h.debugEnabled,
|
||||
}
|
||||
}
|
||||
|
||||
// NewLogger creates a new logger with dynamic debug checking
|
||||
func NewLogger(debugEnabled DebugEnabledFunc) *slog.Logger {
|
||||
// Create base handler that removes timestamps
|
||||
baseHandler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
|
||||
// Always use LevelDebug here to allow all messages through
|
||||
// Our custom handler will do the filtering
|
||||
Level: slog.LevelDebug,
|
||||
ReplaceAttr: func(_ []string, a slog.Attr) slog.Attr {
|
||||
// Remove the time attribute
|
||||
if a.Key == slog.TimeKey {
|
||||
return slog.Attr{}
|
||||
}
|
||||
return a
|
||||
},
|
||||
})
|
||||
|
||||
// Wrap with our dynamic debug-checking handler
|
||||
dynamicHandler := &DebugCheckHandler{
|
||||
handler: baseHandler,
|
||||
debugEnabled: debugEnabled,
|
||||
}
|
||||
|
||||
return slog.New(dynamicHandler)
|
||||
}
|
|
@ -29,9 +29,6 @@ type Client struct {
|
|||
|
||||
// The base URL for requests
|
||||
BaseURL string
|
||||
|
||||
// The internal logger to use
|
||||
Log func(string, ...interface{})
|
||||
}
|
||||
|
||||
// New creates a new client
|
||||
|
@ -44,12 +41,9 @@ func New(u string) (*Client, error) {
|
|||
|
||||
return &Client{
|
||||
BaseURL: u,
|
||||
Log: nopLogger,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var nopLogger = func(_ string, _ ...interface{}) {}
|
||||
|
||||
// Validate if the base URL for monocular is valid.
|
||||
func validate(u string) error {
|
||||
|
||||
|
|
|
@ -24,8 +24,8 @@ import (
|
|||
"path"
|
||||
"time"
|
||||
|
||||
"helm.sh/helm/v3/internal/version"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v4/internal/version"
|
||||
chart "helm.sh/helm/v4/pkg/chart/v2"
|
||||
)
|
||||
|
||||
// SearchPath is the url path to the search API in monocular.
|
||||
|
|
|
@ -28,12 +28,12 @@ import (
|
|||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chart/loader"
|
||||
"helm.sh/helm/v3/pkg/helmpath"
|
||||
"helm.sh/helm/v3/pkg/provenance"
|
||||
"helm.sh/helm/v3/pkg/registry"
|
||||
"helm.sh/helm/v3/pkg/repo"
|
||||
chart "helm.sh/helm/v4/pkg/chart/v2"
|
||||
"helm.sh/helm/v4/pkg/chart/v2/loader"
|
||||
"helm.sh/helm/v4/pkg/helmpath"
|
||||
"helm.sh/helm/v4/pkg/provenance"
|
||||
"helm.sh/helm/v4/pkg/registry"
|
||||
"helm.sh/helm/v4/pkg/repo"
|
||||
)
|
||||
|
||||
// Resolver resolves dependencies from semantic version ranges to a particular version.
|
||||
|
|
|
@ -19,8 +19,8 @@ import (
|
|||
"runtime"
|
||||
"testing"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/registry"
|
||||
chart "helm.sh/helm/v4/pkg/chart/v2"
|
||||
"helm.sh/helm/v4/pkg/registry"
|
||||
)
|
||||
|
||||
func TestResolve(t *testing.T) {
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
Copyright The Helm Authors.
|
||||
This file was initially copied and modified from
|
||||
https://github.com/fluxcd/kustomize-controller/blob/main/internal/statusreaders/job.go
|
||||
Copyright 2022 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package statusreaders
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
"github.com/fluxcd/cli-utils/pkg/kstatus/polling/engine"
|
||||
"github.com/fluxcd/cli-utils/pkg/kstatus/polling/event"
|
||||
"github.com/fluxcd/cli-utils/pkg/kstatus/polling/statusreaders"
|
||||
"github.com/fluxcd/cli-utils/pkg/kstatus/status"
|
||||
"github.com/fluxcd/cli-utils/pkg/object"
|
||||
)
|
||||
|
||||
type customJobStatusReader struct {
|
||||
genericStatusReader engine.StatusReader
|
||||
}
|
||||
|
||||
func NewCustomJobStatusReader(mapper meta.RESTMapper) engine.StatusReader {
|
||||
genericStatusReader := statusreaders.NewGenericStatusReader(mapper, jobConditions)
|
||||
return &customJobStatusReader{
|
||||
genericStatusReader: genericStatusReader,
|
||||
}
|
||||
}
|
||||
|
||||
func (j *customJobStatusReader) Supports(gk schema.GroupKind) bool {
|
||||
return gk == batchv1.SchemeGroupVersion.WithKind("Job").GroupKind()
|
||||
}
|
||||
|
||||
func (j *customJobStatusReader) ReadStatus(ctx context.Context, reader engine.ClusterReader, resource object.ObjMetadata) (*event.ResourceStatus, error) {
|
||||
return j.genericStatusReader.ReadStatus(ctx, reader, resource)
|
||||
}
|
||||
|
||||
func (j *customJobStatusReader) ReadStatusForObject(ctx context.Context, reader engine.ClusterReader, resource *unstructured.Unstructured) (*event.ResourceStatus, error) {
|
||||
return j.genericStatusReader.ReadStatusForObject(ctx, reader, resource)
|
||||
}
|
||||
|
||||
// Ref: https://github.com/kubernetes-sigs/cli-utils/blob/v0.29.4/pkg/kstatus/status/core.go
|
||||
// Modified to return Current status only when the Job has completed as opposed to when it's in progress.
|
||||
func jobConditions(u *unstructured.Unstructured) (*status.Result, error) {
|
||||
obj := u.UnstructuredContent()
|
||||
|
||||
parallelism := status.GetIntField(obj, ".spec.parallelism", 1)
|
||||
completions := status.GetIntField(obj, ".spec.completions", parallelism)
|
||||
succeeded := status.GetIntField(obj, ".status.succeeded", 0)
|
||||
failed := status.GetIntField(obj, ".status.failed", 0)
|
||||
|
||||
// Conditions
|
||||
// https://github.com/kubernetes/kubernetes/blob/master/pkg/controller/job/utils.go#L24
|
||||
objc, err := status.GetObjectWithConditions(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, c := range objc.Status.Conditions {
|
||||
switch c.Type {
|
||||
case "Complete":
|
||||
if c.Status == corev1.ConditionTrue {
|
||||
message := fmt.Sprintf("Job Completed. succeeded: %d/%d", succeeded, completions)
|
||||
return &status.Result{
|
||||
Status: status.CurrentStatus,
|
||||
Message: message,
|
||||
Conditions: []status.Condition{},
|
||||
}, nil
|
||||
}
|
||||
case "Failed":
|
||||
message := fmt.Sprintf("Job Failed. failed: %d/%d", failed, completions)
|
||||
if c.Status == corev1.ConditionTrue {
|
||||
return &status.Result{
|
||||
Status: status.FailedStatus,
|
||||
Message: message,
|
||||
Conditions: []status.Condition{
|
||||
{
|
||||
Type: status.ConditionStalled,
|
||||
Status: corev1.ConditionTrue,
|
||||
Reason: "JobFailed",
|
||||
Message: message,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
message := "Job in progress"
|
||||
return &status.Result{
|
||||
Status: status.InProgressStatus,
|
||||
Message: message,
|
||||
Conditions: []status.Condition{
|
||||
{
|
||||
Type: status.ConditionReconciling,
|
||||
Status: corev1.ConditionTrue,
|
||||
Reason: "JobInProgress",
|
||||
Message: message,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
Copyright The Helm Authors.
|
||||
This file was initially copied and modified from
|
||||
https://github.com/fluxcd/kustomize-controller/blob/main/internal/statusreaders/job_test.go
|
||||
Copyright 2022 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package statusreaders
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
"github.com/fluxcd/cli-utils/pkg/kstatus/status"
|
||||
)
|
||||
|
||||
func toUnstructured(t *testing.T, obj runtime.Object) (*unstructured.Unstructured, error) {
|
||||
t.Helper()
|
||||
// If the incoming object is already unstructured, perform a deep copy first
|
||||
// otherwise DefaultUnstructuredConverter ends up returning the inner map without
|
||||
// making a copy.
|
||||
if _, ok := obj.(runtime.Unstructured); ok {
|
||||
obj = obj.DeepCopyObject()
|
||||
}
|
||||
rawMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &unstructured.Unstructured{Object: rawMap}, nil
|
||||
}
|
||||
|
||||
func TestJobConditions(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
job *batchv1.Job
|
||||
expectedStatus status.Status
|
||||
}{
|
||||
{
|
||||
name: "job without Complete condition returns InProgress status",
|
||||
job: &batchv1.Job{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "job-no-condition",
|
||||
},
|
||||
Spec: batchv1.JobSpec{},
|
||||
Status: batchv1.JobStatus{},
|
||||
},
|
||||
expectedStatus: status.InProgressStatus,
|
||||
},
|
||||
{
|
||||
name: "job with Complete condition as True returns Current status",
|
||||
job: &batchv1.Job{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "job-complete",
|
||||
},
|
||||
Spec: batchv1.JobSpec{},
|
||||
Status: batchv1.JobStatus{
|
||||
Conditions: []batchv1.JobCondition{
|
||||
{
|
||||
Type: batchv1.JobComplete,
|
||||
Status: corev1.ConditionTrue,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedStatus: status.CurrentStatus,
|
||||
},
|
||||
{
|
||||
name: "job with Failed condition as True returns Failed status",
|
||||
job: &batchv1.Job{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "job-failed",
|
||||
},
|
||||
Spec: batchv1.JobSpec{},
|
||||
Status: batchv1.JobStatus{
|
||||
Conditions: []batchv1.JobCondition{
|
||||
{
|
||||
Type: batchv1.JobFailed,
|
||||
Status: corev1.ConditionTrue,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedStatus: status.FailedStatus,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
us, err := toUnstructured(t, tc.job)
|
||||
assert.NoError(t, err)
|
||||
result, err := jobConditions(us)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tc.expectedStatus, result.Status)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package statusreaders
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
"github.com/fluxcd/cli-utils/pkg/kstatus/polling/engine"
|
||||
"github.com/fluxcd/cli-utils/pkg/kstatus/polling/event"
|
||||
"github.com/fluxcd/cli-utils/pkg/kstatus/polling/statusreaders"
|
||||
"github.com/fluxcd/cli-utils/pkg/kstatus/status"
|
||||
"github.com/fluxcd/cli-utils/pkg/object"
|
||||
)
|
||||
|
||||
type customPodStatusReader struct {
|
||||
genericStatusReader engine.StatusReader
|
||||
}
|
||||
|
||||
func NewCustomPodStatusReader(mapper meta.RESTMapper) engine.StatusReader {
|
||||
genericStatusReader := statusreaders.NewGenericStatusReader(mapper, podConditions)
|
||||
return &customPodStatusReader{
|
||||
genericStatusReader: genericStatusReader,
|
||||
}
|
||||
}
|
||||
|
||||
func (j *customPodStatusReader) Supports(gk schema.GroupKind) bool {
|
||||
return gk == corev1.SchemeGroupVersion.WithKind("Pod").GroupKind()
|
||||
}
|
||||
|
||||
func (j *customPodStatusReader) ReadStatus(ctx context.Context, reader engine.ClusterReader, resource object.ObjMetadata) (*event.ResourceStatus, error) {
|
||||
return j.genericStatusReader.ReadStatus(ctx, reader, resource)
|
||||
}
|
||||
|
||||
func (j *customPodStatusReader) ReadStatusForObject(ctx context.Context, reader engine.ClusterReader, resource *unstructured.Unstructured) (*event.ResourceStatus, error) {
|
||||
return j.genericStatusReader.ReadStatusForObject(ctx, reader, resource)
|
||||
}
|
||||
|
||||
func podConditions(u *unstructured.Unstructured) (*status.Result, error) {
|
||||
obj := u.UnstructuredContent()
|
||||
phase := status.GetStringField(obj, ".status.phase", "")
|
||||
switch corev1.PodPhase(phase) {
|
||||
case corev1.PodSucceeded:
|
||||
message := fmt.Sprintf("pod %s succeeded", u.GetName())
|
||||
return &status.Result{
|
||||
Status: status.CurrentStatus,
|
||||
Message: message,
|
||||
Conditions: []status.Condition{
|
||||
{
|
||||
Type: status.ConditionStalled,
|
||||
Status: corev1.ConditionTrue,
|
||||
Message: message,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
case corev1.PodFailed:
|
||||
message := fmt.Sprintf("pod %s failed", u.GetName())
|
||||
return &status.Result{
|
||||
Status: status.FailedStatus,
|
||||
Message: message,
|
||||
Conditions: []status.Condition{
|
||||
{
|
||||
Type: status.ConditionStalled,
|
||||
Status: corev1.ConditionTrue,
|
||||
Reason: "PodFailed",
|
||||
Message: message,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
message := "Pod in progress"
|
||||
return &status.Result{
|
||||
Status: status.InProgressStatus,
|
||||
Message: message,
|
||||
Conditions: []status.Condition{
|
||||
{
|
||||
Type: status.ConditionReconciling,
|
||||
Status: corev1.ConditionTrue,
|
||||
Reason: "PodInProgress",
|
||||
Message: message,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package statusreaders
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/fluxcd/cli-utils/pkg/kstatus/status"
|
||||
)
|
||||
|
||||
func TestPodConditions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
pod *v1.Pod
|
||||
expectedStatus status.Status
|
||||
}{
|
||||
{
|
||||
name: "pod without status returns in progress",
|
||||
pod: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "pod-no-status"},
|
||||
Spec: v1.PodSpec{},
|
||||
Status: v1.PodStatus{},
|
||||
},
|
||||
expectedStatus: status.InProgressStatus,
|
||||
},
|
||||
{
|
||||
name: "pod succeeded returns current status",
|
||||
pod: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "pod-succeeded"},
|
||||
Spec: v1.PodSpec{},
|
||||
Status: v1.PodStatus{
|
||||
Phase: v1.PodSucceeded,
|
||||
},
|
||||
},
|
||||
expectedStatus: status.CurrentStatus,
|
||||
},
|
||||
{
|
||||
name: "pod failed returns failed status",
|
||||
pod: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "pod-failed"},
|
||||
Spec: v1.PodSpec{},
|
||||
Status: v1.PodStatus{
|
||||
Phase: v1.PodFailed,
|
||||
},
|
||||
},
|
||||
expectedStatus: status.FailedStatus,
|
||||
},
|
||||
{
|
||||
name: "pod pending returns in progress status",
|
||||
pod: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "pod-pending"},
|
||||
Spec: v1.PodSpec{},
|
||||
Status: v1.PodStatus{
|
||||
Phase: v1.PodPending,
|
||||
},
|
||||
},
|
||||
expectedStatus: status.InProgressStatus,
|
||||
},
|
||||
{
|
||||
name: "pod running returns in progress status",
|
||||
pod: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "pod-running"},
|
||||
Spec: v1.PodSpec{},
|
||||
Status: v1.PodStatus{
|
||||
Phase: v1.PodRunning,
|
||||
},
|
||||
},
|
||||
expectedStatus: status.InProgressStatus,
|
||||
},
|
||||
{
|
||||
name: "pod with unknown phase returns in progress status",
|
||||
pod: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "pod-unknown"},
|
||||
Spec: v1.PodSpec{},
|
||||
Status: v1.PodStatus{
|
||||
Phase: v1.PodUnknown,
|
||||
},
|
||||
},
|
||||
expectedStatus: status.InProgressStatus,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
us, err := toUnstructured(t, tc.pod)
|
||||
assert.NoError(t, err)
|
||||
result, err := podConditions(us)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tc.expectedStatus, result.Status)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -22,7 +22,7 @@ package sympath
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
|
@ -70,8 +70,8 @@ func symwalk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("error evaluating symlink %s: %w", path, err)
|
||||
}
|
||||
// This log message is to highlight a symlink that is being used within a chart, symlinks can be used for nefarious reasons.
|
||||
log.Printf("found symbolic link in path: %s resolves to %s. Contents of linked file included and used", path, resolved)
|
||||
//This log message is to highlight a symlink that is being used within a chart, symlinks can be used for nefarious reasons.
|
||||
slog.Info("found symbolic link in path. Contents of linked file included and used", "path", path, "resolved", resolved)
|
||||
if info, err = os.Lstat(resolved); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -108,12 +108,12 @@ func checkMarks(t *testing.T, report bool) {
|
|||
}
|
||||
|
||||
// Assumes that each node name is unique. Good enough for a test.
|
||||
// If clear is true, any incoming error is cleared before return. The errors
|
||||
// are always accumulated, though.
|
||||
func mark(info os.FileInfo, err error, errors *[]error, clear bool) error {
|
||||
// If clearIncomingError is true, any incoming error is cleared before
|
||||
// return. The errors are always accumulated, though.
|
||||
func mark(info os.FileInfo, err error, errors *[]error, clearIncomingError bool) error {
|
||||
if err != nil {
|
||||
*errors = append(*errors, err)
|
||||
if clear {
|
||||
if clearIncomingError {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
|
@ -130,9 +130,8 @@ func mark(info os.FileInfo, err error, errors *[]error, clear bool) error {
|
|||
func TestWalk(t *testing.T) {
|
||||
makeTree(t)
|
||||
errors := make([]error, 0, 10)
|
||||
clear := true
|
||||
markFn := func(_ string, info os.FileInfo, err error) error {
|
||||
return mark(info, err, &errors, clear)
|
||||
return mark(info, err, &errors, true)
|
||||
}
|
||||
// Expect no errors.
|
||||
err := Walk(tree.name, markFn)
|
||||
|
|
|
@ -21,8 +21,8 @@ import (
|
|||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"helm.sh/helm/v3/pkg/helmpath"
|
||||
"helm.sh/helm/v3/pkg/helmpath/xdg"
|
||||
"helm.sh/helm/v4/pkg/helmpath"
|
||||
"helm.sh/helm/v4/pkg/helmpath/xdg"
|
||||
)
|
||||
|
||||
// HelmHome sets up a Helm Home in a temp dir.
|
||||
|
|
|
@ -91,5 +91,5 @@ func update(filename string, in []byte) error {
|
|||
}
|
||||
|
||||
func normalize(in []byte) []byte {
|
||||
return bytes.Replace(in, []byte("\r\n"), []byte("\n"), -1)
|
||||
return bytes.ReplaceAll(in, []byte("\r\n"), []byte("\n"))
|
||||
}
|
||||
|
|
|
@ -177,28 +177,28 @@ func copyFile(src, dst string) (err error) {
|
|||
|
||||
in, err := os.Open(src)
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
defer in.Close()
|
||||
|
||||
out, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = io.Copy(out, in); err != nil {
|
||||
out.Close()
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
// Check for write errors on Close
|
||||
if err = out.Close(); err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
si, err := os.Stat(src)
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
// Temporary fix for Go < 1.9
|
||||
|
@ -210,7 +210,7 @@ func copyFile(src, dst string) (err error) {
|
|||
}
|
||||
err = os.Chmod(dst, si.Mode())
|
||||
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
// cloneSymlink will create a new symlink that points to the resolved path of sl.
|
||||
|
|
|
@ -33,17 +33,11 @@ package fs
|
|||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
mu sync.Mutex
|
||||
)
|
||||
|
||||
func TestRenameWithFallback(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
|
@ -360,19 +354,6 @@ func TestCopyFile(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func cleanUpDir(dir string) {
|
||||
// NOTE(mattn): It seems that sometimes git.exe is not dead
|
||||
// when cleanUpDir() is called. But we do not know any way to wait for it.
|
||||
if runtime.GOOS == "windows" {
|
||||
mu.Lock()
|
||||
exec.Command(`taskkill`, `/F`, `/IM`, `git.exe`).Run()
|
||||
mu.Unlock()
|
||||
}
|
||||
if dir != "" {
|
||||
os.RemoveAll(dir)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopyFileSymlink(t *testing.T) {
|
||||
tempdir := t.TempDir()
|
||||
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package tlsutil
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
)
|
||||
|
||||
// Options represents configurable options used to create client and server TLS configurations.
|
||||
type Options struct {
|
||||
CaCertFile string
|
||||
// If either the KeyFile or CertFile is empty, ClientConfig() will not load them.
|
||||
KeyFile string
|
||||
CertFile string
|
||||
// Client-only options
|
||||
InsecureSkipVerify bool
|
||||
}
|
||||
|
||||
// ClientConfig returns a TLS configuration for use by a Helm client.
|
||||
func ClientConfig(opts Options) (cfg *tls.Config, err error) {
|
||||
var cert *tls.Certificate
|
||||
var pool *x509.CertPool
|
||||
|
||||
if opts.CertFile != "" || opts.KeyFile != "" {
|
||||
if cert, err = CertFromFilePair(opts.CertFile, opts.KeyFile); err != nil {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return nil, fmt.Errorf("could not load x509 key pair (cert: %q, key: %q): %w", opts.CertFile, opts.KeyFile, err)
|
||||
}
|
||||
return nil, fmt.Errorf("could not read x509 key pair (cert: %q, key: %q): %w", opts.CertFile, opts.KeyFile, err)
|
||||
}
|
||||
}
|
||||
if !opts.InsecureSkipVerify && opts.CaCertFile != "" {
|
||||
if pool, err = CertPoolFromFile(opts.CaCertFile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
cfg = &tls.Config{InsecureSkipVerify: opts.InsecureSkipVerify, Certificates: []tls.Certificate{*cert}, RootCAs: pool}
|
||||
return cfg, nil
|
||||
}
|
|
@ -21,57 +21,102 @@ import (
|
|||
"crypto/x509"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"errors"
|
||||
)
|
||||
|
||||
// NewClientTLS returns tls.Config appropriate for client auth.
|
||||
func NewClientTLS(certFile, keyFile, caFile string, insecureSkipTLSverify bool) (*tls.Config, error) {
|
||||
type TLSConfigOptions struct {
|
||||
insecureSkipTLSverify bool
|
||||
certPEMBlock, keyPEMBlock []byte
|
||||
caPEMBlock []byte
|
||||
}
|
||||
|
||||
type TLSConfigOption func(options *TLSConfigOptions) error
|
||||
|
||||
func WithInsecureSkipVerify(insecureSkipTLSverify bool) TLSConfigOption {
|
||||
return func(options *TLSConfigOptions) error {
|
||||
options.insecureSkipTLSverify = insecureSkipTLSverify
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithCertKeyPairFiles(certFile, keyFile string) TLSConfigOption {
|
||||
return func(options *TLSConfigOptions) error {
|
||||
if certFile == "" && keyFile == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
certPEMBlock, err := os.ReadFile(certFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to read cert file: %q: %w", certFile, err)
|
||||
}
|
||||
|
||||
keyPEMBlock, err := os.ReadFile(keyFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to read key file: %q: %w", keyFile, err)
|
||||
}
|
||||
|
||||
options.certPEMBlock = certPEMBlock
|
||||
options.keyPEMBlock = keyPEMBlock
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithCAFile(caFile string) TLSConfigOption {
|
||||
return func(options *TLSConfigOptions) error {
|
||||
if caFile == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
caPEMBlock, err := os.ReadFile(caFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't read CA file: %q: %w", caFile, err)
|
||||
}
|
||||
|
||||
options.caPEMBlock = caPEMBlock
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func NewTLSConfig(options ...TLSConfigOption) (*tls.Config, error) {
|
||||
to := TLSConfigOptions{}
|
||||
|
||||
errs := []error{}
|
||||
for _, option := range options {
|
||||
err := option(&to)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return nil, errors.Join(errs...)
|
||||
}
|
||||
|
||||
config := tls.Config{
|
||||
InsecureSkipVerify: insecureSkipTLSverify,
|
||||
InsecureSkipVerify: to.insecureSkipTLSverify,
|
||||
}
|
||||
|
||||
if certFile != "" && keyFile != "" {
|
||||
cert, err := CertFromFilePair(certFile, keyFile)
|
||||
if len(to.certPEMBlock) > 0 && len(to.keyPEMBlock) > 0 {
|
||||
cert, err := tls.X509KeyPair(to.certPEMBlock, to.keyPEMBlock)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("unable to load cert from key pair: %w", err)
|
||||
}
|
||||
config.Certificates = []tls.Certificate{*cert}
|
||||
|
||||
config.Certificates = []tls.Certificate{cert}
|
||||
}
|
||||
|
||||
if caFile != "" {
|
||||
cp, err := CertPoolFromFile(caFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if len(to.caPEMBlock) > 0 {
|
||||
cp := x509.NewCertPool()
|
||||
if !cp.AppendCertsFromPEM(to.caPEMBlock) {
|
||||
return nil, fmt.Errorf("failed to append certificates from pem block")
|
||||
}
|
||||
|
||||
config.RootCAs = cp
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
// CertPoolFromFile returns an x509.CertPool containing the certificates
|
||||
// in the given PEM-encoded file.
|
||||
// Returns an error if the file could not be read, a certificate could not
|
||||
// be parsed, or if the file does not contain any certificates
|
||||
func CertPoolFromFile(filename string) (*x509.CertPool, error) {
|
||||
b, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't read CA file: %v", filename)
|
||||
}
|
||||
cp := x509.NewCertPool()
|
||||
if !cp.AppendCertsFromPEM(b) {
|
||||
return nil, fmt.Errorf("failed to append certificates from file: %s", filename)
|
||||
}
|
||||
return cp, nil
|
||||
}
|
||||
|
||||
// CertFromFilePair returns a tls.Certificate containing the
|
||||
// certificates public/private key pair from a pair of given PEM-encoded files.
|
||||
// Returns an error if the file could not be read, a certificate could not
|
||||
// be parsed, or if the file does not contain any certificates
|
||||
func CertFromFilePair(certFile, keyFile string) (*tls.Certificate, error) {
|
||||
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't load key pair from cert %s and key %s: %w", certFile, keyFile, err)
|
||||
}
|
||||
return &cert, err
|
||||
}
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package tlsutil
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const tlsTestDir = "../../testdata"
|
||||
|
||||
const (
|
||||
testCaCertFile = "rootca.crt"
|
||||
testCertFile = "crt.pem"
|
||||
testKeyFile = "key.pem"
|
||||
)
|
||||
|
||||
func testfile(t *testing.T, file string) (path string) {
|
||||
var err error
|
||||
if path, err = filepath.Abs(filepath.Join(tlsTestDir, file)); err != nil {
|
||||
t.Fatalf("error getting absolute path to test file %q: %v", file, err)
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func TestNewTLSConfig(t *testing.T) {
|
||||
certFile := testfile(t, testCertFile)
|
||||
keyFile := testfile(t, testKeyFile)
|
||||
caCertFile := testfile(t, testCaCertFile)
|
||||
insecureSkipTLSverify := false
|
||||
|
||||
{
|
||||
cfg, err := NewTLSConfig(
|
||||
WithInsecureSkipVerify(insecureSkipTLSverify),
|
||||
WithCertKeyPairFiles(certFile, keyFile),
|
||||
WithCAFile(caCertFile),
|
||||
)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if got := len(cfg.Certificates); got != 1 {
|
||||
t.Fatalf("expecting 1 client certificates, got %d", got)
|
||||
}
|
||||
if cfg.InsecureSkipVerify {
|
||||
t.Fatalf("insecure skip verify mismatch, expecting false")
|
||||
}
|
||||
if cfg.RootCAs == nil {
|
||||
t.Fatalf("mismatch tls RootCAs, expecting non-nil")
|
||||
}
|
||||
}
|
||||
{
|
||||
cfg, err := NewTLSConfig(
|
||||
WithInsecureSkipVerify(insecureSkipTLSverify),
|
||||
WithCAFile(caCertFile),
|
||||
)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if got := len(cfg.Certificates); got != 0 {
|
||||
t.Fatalf("expecting 0 client certificates, got %d", got)
|
||||
}
|
||||
if cfg.InsecureSkipVerify {
|
||||
t.Fatalf("insecure skip verify mismatch, expecting false")
|
||||
}
|
||||
if cfg.RootCAs == nil {
|
||||
t.Fatalf("mismatch tls RootCAs, expecting non-nil")
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
cfg, err := NewTLSConfig(
|
||||
WithInsecureSkipVerify(insecureSkipTLSverify),
|
||||
WithCertKeyPairFiles(certFile, keyFile),
|
||||
)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if got := len(cfg.Certificates); got != 1 {
|
||||
t.Fatalf("expecting 1 client certificates, got %d", got)
|
||||
}
|
||||
if cfg.InsecureSkipVerify {
|
||||
t.Fatalf("insecure skip verify mismatch, expecting false")
|
||||
}
|
||||
if cfg.RootCAs != nil {
|
||||
t.Fatalf("mismatch tls RootCAs, expecting nil")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,114 +0,0 @@
|
|||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package tlsutil
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const tlsTestDir = "../../testdata"
|
||||
|
||||
const (
|
||||
testCaCertFile = "rootca.crt"
|
||||
testCertFile = "crt.pem"
|
||||
testKeyFile = "key.pem"
|
||||
)
|
||||
|
||||
func TestClientConfig(t *testing.T) {
|
||||
opts := Options{
|
||||
CaCertFile: testfile(t, testCaCertFile),
|
||||
CertFile: testfile(t, testCertFile),
|
||||
KeyFile: testfile(t, testKeyFile),
|
||||
InsecureSkipVerify: false,
|
||||
}
|
||||
|
||||
cfg, err := ClientConfig(opts)
|
||||
if err != nil {
|
||||
t.Fatalf("error building tls client config: %v", err)
|
||||
}
|
||||
|
||||
if got := len(cfg.Certificates); got != 1 {
|
||||
t.Fatalf("expecting 1 client certificates, got %d", got)
|
||||
}
|
||||
if cfg.InsecureSkipVerify {
|
||||
t.Fatalf("insecure skip verify mismatch, expecting false")
|
||||
}
|
||||
if cfg.RootCAs == nil {
|
||||
t.Fatalf("mismatch tls RootCAs, expecting non-nil")
|
||||
}
|
||||
}
|
||||
|
||||
func testfile(t *testing.T, file string) (path string) {
|
||||
var err error
|
||||
if path, err = filepath.Abs(filepath.Join(tlsTestDir, file)); err != nil {
|
||||
t.Fatalf("error getting absolute path to test file %q: %v", file, err)
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func TestNewClientTLS(t *testing.T) {
|
||||
certFile := testfile(t, testCertFile)
|
||||
keyFile := testfile(t, testKeyFile)
|
||||
caCertFile := testfile(t, testCaCertFile)
|
||||
insecureSkipTLSverify := false
|
||||
|
||||
cfg, err := NewClientTLS(certFile, keyFile, caCertFile, insecureSkipTLSverify)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if got := len(cfg.Certificates); got != 1 {
|
||||
t.Fatalf("expecting 1 client certificates, got %d", got)
|
||||
}
|
||||
if cfg.InsecureSkipVerify {
|
||||
t.Fatalf("insecure skip verify mismatch, expecting false")
|
||||
}
|
||||
if cfg.RootCAs == nil {
|
||||
t.Fatalf("mismatch tls RootCAs, expecting non-nil")
|
||||
}
|
||||
|
||||
cfg, err = NewClientTLS("", "", caCertFile, insecureSkipTLSverify)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if got := len(cfg.Certificates); got != 0 {
|
||||
t.Fatalf("expecting 0 client certificates, got %d", got)
|
||||
}
|
||||
if cfg.InsecureSkipVerify {
|
||||
t.Fatalf("insecure skip verify mismatch, expecting false")
|
||||
}
|
||||
if cfg.RootCAs == nil {
|
||||
t.Fatalf("mismatch tls RootCAs, expecting non-nil")
|
||||
}
|
||||
|
||||
cfg, err = NewClientTLS(certFile, keyFile, "", insecureSkipTLSverify)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if got := len(cfg.Certificates); got != 1 {
|
||||
t.Fatalf("expecting 1 client certificates, got %d", got)
|
||||
}
|
||||
if cfg.InsecureSkipVerify {
|
||||
t.Fatalf("insecure skip verify mismatch, expecting false")
|
||||
}
|
||||
if cfg.RootCAs != nil {
|
||||
t.Fatalf("mismatch tls RootCAs, expecting nil")
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package version // import "helm.sh/helm/v3/internal/version"
|
||||
package version // import "helm.sh/helm/v4/internal/version"
|
||||
|
||||
import (
|
||||
"flag"
|
||||
|
@ -29,7 +29,7 @@ var (
|
|||
//
|
||||
// Increment major number for new feature additions and behavioral changes.
|
||||
// Increment minor number for bug fixes and performance enhancements.
|
||||
version = "v3.16"
|
||||
version = "v4.0"
|
||||
|
||||
// metadata is extra build time data
|
||||
metadata = ""
|
||||
|
|
|
@ -20,11 +20,13 @@ import (
|
|||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
|
@ -32,17 +34,17 @@ import (
|
|||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/engine"
|
||||
"helm.sh/helm/v3/pkg/kube"
|
||||
"helm.sh/helm/v3/pkg/postrender"
|
||||
"helm.sh/helm/v3/pkg/registry"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"helm.sh/helm/v3/pkg/releaseutil"
|
||||
"helm.sh/helm/v3/pkg/storage"
|
||||
"helm.sh/helm/v3/pkg/storage/driver"
|
||||
"helm.sh/helm/v3/pkg/time"
|
||||
chart "helm.sh/helm/v4/pkg/chart/v2"
|
||||
chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
|
||||
"helm.sh/helm/v4/pkg/engine"
|
||||
"helm.sh/helm/v4/pkg/kube"
|
||||
"helm.sh/helm/v4/pkg/postrender"
|
||||
"helm.sh/helm/v4/pkg/registry"
|
||||
releaseutil "helm.sh/helm/v4/pkg/release/util"
|
||||
release "helm.sh/helm/v4/pkg/release/v1"
|
||||
"helm.sh/helm/v4/pkg/storage"
|
||||
"helm.sh/helm/v4/pkg/storage/driver"
|
||||
"helm.sh/helm/v4/pkg/time"
|
||||
)
|
||||
|
||||
// Timestamper is a function capable of producing a timestamp.Timestamper.
|
||||
|
@ -62,21 +64,6 @@ var (
|
|||
errPending = errors.New("another operation (install/upgrade/rollback) is in progress")
|
||||
)
|
||||
|
||||
// ValidName is a regular expression for resource names.
|
||||
//
|
||||
// DEPRECATED: This will be removed in Helm 4, and is no longer used here. See
|
||||
// pkg/lint/rules.validateMetadataNameFunc for the replacement.
|
||||
//
|
||||
// According to the Kubernetes help text, the regular expression it uses is:
|
||||
//
|
||||
// [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*
|
||||
//
|
||||
// This follows the above regular expression (but requires a full string match, not partial).
|
||||
//
|
||||
// The Kubernetes documentation is here, though it is not entirely correct:
|
||||
// https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
var ValidName = regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`)
|
||||
|
||||
// Configuration injects the dependencies that all actions share.
|
||||
type Configuration struct {
|
||||
// RESTClientGetter is an interface that loads Kubernetes clients.
|
||||
|
@ -94,7 +81,11 @@ type Configuration struct {
|
|||
// Capabilities describes the capabilities of the Kubernetes cluster.
|
||||
Capabilities *chartutil.Capabilities
|
||||
|
||||
Log func(string, ...interface{})
|
||||
// CustomTemplateFuncs is defined by users to provide custom template funcs
|
||||
CustomTemplateFuncs template.FuncMap
|
||||
|
||||
// HookOutputFunc called with container name and returns and expects writer that will receive the log output.
|
||||
HookOutputFunc func(namespace, pod, container string) io.Writer
|
||||
}
|
||||
|
||||
// renderResources renders the templates in a chart
|
||||
|
@ -122,7 +113,7 @@ func (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Valu
|
|||
var err2 error
|
||||
|
||||
// A `helm template` should not talk to the remote cluster. However, commands with the flag
|
||||
//`--dry-run` with the value of `false`, `none`, or `server` should try to interact with the cluster.
|
||||
// `--dry-run` with the value of `false`, `none`, or `server` should try to interact with the cluster.
|
||||
// It may break in interesting and exotic ways because other data (e.g. discovery) is mocked.
|
||||
if interactWithRemote && cfg.RESTClientGetter != nil {
|
||||
restConfig, err := cfg.RESTClientGetter.ToRESTConfig()
|
||||
|
@ -131,10 +122,14 @@ func (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Valu
|
|||
}
|
||||
e := engine.New(restConfig)
|
||||
e.EnableDNS = enableDNS
|
||||
e.CustomTemplateFuncs = cfg.CustomTemplateFuncs
|
||||
|
||||
files, err2 = e.Render(ch, values)
|
||||
} else {
|
||||
var e engine.Engine
|
||||
e.EnableDNS = enableDNS
|
||||
e.CustomTemplateFuncs = cfg.CustomTemplateFuncs
|
||||
|
||||
files, err2 = e.Render(ch, values)
|
||||
}
|
||||
|
||||
|
@ -239,9 +234,6 @@ type RESTClientGetter interface {
|
|||
ToRESTMapper() (meta.RESTMapper, error)
|
||||
}
|
||||
|
||||
// DebugLog sets the logger that writes debug strings
|
||||
type DebugLog func(format string, v ...interface{})
|
||||
|
||||
// capabilities builds a Capabilities from discovery information.
|
||||
func (cfg *Configuration) getCapabilities() (*chartutil.Capabilities, error) {
|
||||
if cfg.Capabilities != nil {
|
||||
|
@ -265,8 +257,8 @@ func (cfg *Configuration) getCapabilities() (*chartutil.Capabilities, error) {
|
|||
apiVersions, err := GetVersionSet(dc)
|
||||
if err != nil {
|
||||
if discovery.IsGroupDiscoveryFailedError(err) {
|
||||
cfg.Log("WARNING: The Kubernetes server has an orphaned API service. Server reports: %s", err)
|
||||
cfg.Log("WARNING: To fix this, kubectl delete apiservice <service-name>")
|
||||
slog.Warn("the kubernetes server has an orphaned API service", slog.Any("error", err))
|
||||
slog.Warn("to fix this, kubectl delete apiservice <service-name>")
|
||||
} else {
|
||||
return nil, fmt.Errorf("could not get apiVersions from Kubernetes: %w", err)
|
||||
}
|
||||
|
@ -365,14 +357,13 @@ func GetVersionSet(client discovery.ServerResourcesInterface) (chartutil.Version
|
|||
// recordRelease with an update operation in case reuse has been set.
|
||||
func (cfg *Configuration) recordRelease(r *release.Release) {
|
||||
if err := cfg.Releases.Update(r); err != nil {
|
||||
cfg.Log("warning: Failed to update release %s: %s", r.Name, err)
|
||||
slog.Warn("failed to update release", "name", r.Name, "revision", r.Version, slog.Any("error", err))
|
||||
}
|
||||
}
|
||||
|
||||
// Init initializes the action configuration
|
||||
func (cfg *Configuration) Init(getter genericclioptions.RESTClientGetter, namespace, helmDriver string, log DebugLog) error {
|
||||
func (cfg *Configuration) Init(getter genericclioptions.RESTClientGetter, namespace, helmDriver string) error {
|
||||
kc := kube.New(getter)
|
||||
kc.Log = log
|
||||
|
||||
lazyClient := &lazyClient{
|
||||
namespace: namespace,
|
||||
|
@ -383,11 +374,9 @@ func (cfg *Configuration) Init(getter genericclioptions.RESTClientGetter, namesp
|
|||
switch helmDriver {
|
||||
case "secret", "secrets", "":
|
||||
d := driver.NewSecrets(newSecretClient(lazyClient))
|
||||
d.Log = log
|
||||
store = storage.Init(d)
|
||||
case "configmap", "configmaps":
|
||||
d := driver.NewConfigMaps(newConfigMapClient(lazyClient))
|
||||
d.Log = log
|
||||
store = storage.Init(d)
|
||||
case "memory":
|
||||
var d *driver.Memory
|
||||
|
@ -407,7 +396,6 @@ func (cfg *Configuration) Init(getter genericclioptions.RESTClientGetter, namesp
|
|||
case "sql":
|
||||
d, err := driver.NewSQL(
|
||||
os.Getenv("HELM_DRIVER_SQL_CONNECTION_STRING"),
|
||||
log,
|
||||
namespace,
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -421,7 +409,12 @@ func (cfg *Configuration) Init(getter genericclioptions.RESTClientGetter, namesp
|
|||
cfg.RESTClientGetter = getter
|
||||
cfg.KubeClient = kc
|
||||
cfg.Releases = store
|
||||
cfg.Log = log
|
||||
cfg.HookOutputFunc = func(_, _, _ string) io.Writer { return io.Discard }
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetHookOutputFunc sets the HookOutputFunc on the Configuration.
|
||||
func (cfg *Configuration) SetHookOutputFunc(hookOutputFunc func(_, _, _ string) io.Writer) {
|
||||
cfg.HookOutputFunc = hookOutputFunc
|
||||
}
|
||||
|
|
|
@ -19,26 +19,38 @@ import (
|
|||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
fakeclientset "k8s.io/client-go/kubernetes/fake"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
kubefake "helm.sh/helm/v3/pkg/kube/fake"
|
||||
"helm.sh/helm/v3/pkg/registry"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"helm.sh/helm/v3/pkg/storage"
|
||||
"helm.sh/helm/v3/pkg/storage/driver"
|
||||
"helm.sh/helm/v3/pkg/time"
|
||||
"helm.sh/helm/v4/internal/logging"
|
||||
chart "helm.sh/helm/v4/pkg/chart/v2"
|
||||
chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
|
||||
"helm.sh/helm/v4/pkg/kube"
|
||||
kubefake "helm.sh/helm/v4/pkg/kube/fake"
|
||||
"helm.sh/helm/v4/pkg/registry"
|
||||
release "helm.sh/helm/v4/pkg/release/v1"
|
||||
"helm.sh/helm/v4/pkg/storage"
|
||||
"helm.sh/helm/v4/pkg/storage/driver"
|
||||
"helm.sh/helm/v4/pkg/time"
|
||||
)
|
||||
|
||||
var verbose = flag.Bool("test.log", false, "enable test logging")
|
||||
var verbose = flag.Bool("test.log", false, "enable test logging (debug by default)")
|
||||
|
||||
func actionConfigFixture(t *testing.T) *Configuration {
|
||||
return actionConfigFixtureWithDummyResources(t, nil)
|
||||
}
|
||||
|
||||
func actionConfigFixtureWithDummyResources(t *testing.T, dummyResources kube.ResourceList) *Configuration {
|
||||
t.Helper()
|
||||
|
||||
logger := logging.NewLogger(func() bool {
|
||||
return *verbose
|
||||
})
|
||||
slog.SetDefault(logger)
|
||||
|
||||
registryClient, err := registry.NewClient()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -46,15 +58,9 @@ func actionConfigFixture(t *testing.T) *Configuration {
|
|||
|
||||
return &Configuration{
|
||||
Releases: storage.Init(driver.NewMemory()),
|
||||
KubeClient: &kubefake.FailingKubeClient{PrintingKubeClient: kubefake.PrintingKubeClient{Out: io.Discard}},
|
||||
KubeClient: &kubefake.FailingKubeClient{PrintingKubeClient: kubefake.PrintingKubeClient{Out: io.Discard}, DummyResources: dummyResources},
|
||||
Capabilities: chartutil.DefaultCapabilities,
|
||||
RegistryClient: registryClient,
|
||||
Log: func(format string, v ...interface{}) {
|
||||
t.Helper()
|
||||
if *verbose {
|
||||
t.Logf(format, v...)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,6 +117,14 @@ type chartOptions struct {
|
|||
type chartOption func(*chartOptions)
|
||||
|
||||
func buildChart(opts ...chartOption) *chart.Chart {
|
||||
defaultTemplates := []*chart.File{
|
||||
{Name: "templates/hello", Data: []byte("hello: world")},
|
||||
{Name: "templates/hooks", Data: []byte(manifestWithHook)},
|
||||
}
|
||||
return buildChartWithTemplates(defaultTemplates, opts...)
|
||||
}
|
||||
|
||||
func buildChartWithTemplates(templates []*chart.File, opts ...chartOption) *chart.Chart {
|
||||
c := &chartOptions{
|
||||
Chart: &chart.Chart{
|
||||
// TODO: This should be more complete.
|
||||
|
@ -119,18 +133,13 @@ func buildChart(opts ...chartOption) *chart.Chart {
|
|||
Name: "hello",
|
||||
Version: "0.1.0",
|
||||
},
|
||||
// This adds a basic template and hooks.
|
||||
Templates: []*chart.File{
|
||||
{Name: "templates/hello", Data: []byte("hello: world")},
|
||||
{Name: "templates/hooks", Data: []byte(manifestWithHook)},
|
||||
},
|
||||
Templates: templates,
|
||||
},
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(c)
|
||||
}
|
||||
|
||||
return c.Chart
|
||||
}
|
||||
|
||||
|
@ -331,7 +340,7 @@ func TestConfiguration_Init(t *testing.T) {
|
|||
t.Run(tt.name, func(t *testing.T) {
|
||||
cfg := &Configuration{}
|
||||
|
||||
actualErr := cfg.Init(nil, "default", tt.helmDriver, nil)
|
||||
actualErr := cfg.Init(nil, "default", tt.helmDriver)
|
||||
if tt.expectErr {
|
||||
assert.Error(t, actualErr)
|
||||
assert.Contains(t, actualErr.Error(), tt.errMsg)
|
||||
|
@ -344,7 +353,7 @@ func TestConfiguration_Init(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGetVersionSet(t *testing.T) {
|
||||
client := fakeclientset.NewSimpleClientset()
|
||||
client := fakeclientset.NewClientset()
|
||||
|
||||
vs, err := GetVersionSet(client.Discovery())
|
||||
if err != nil {
|
||||
|
|
|
@ -26,18 +26,25 @@ import (
|
|||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/gosuri/uitable"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chart/loader"
|
||||
chart "helm.sh/helm/v4/pkg/chart/v2"
|
||||
"helm.sh/helm/v4/pkg/chart/v2/loader"
|
||||
)
|
||||
|
||||
// Dependency is the action for building a given chart's dependency tree.
|
||||
//
|
||||
// It provides the implementation of 'helm dependency' and its respective subcommands.
|
||||
type Dependency struct {
|
||||
Verify bool
|
||||
Keyring string
|
||||
SkipRefresh bool
|
||||
ColumnWidth uint
|
||||
Verify bool
|
||||
Keyring string
|
||||
SkipRefresh bool
|
||||
ColumnWidth uint
|
||||
Username string
|
||||
Password string
|
||||
CertFile string
|
||||
KeyFile string
|
||||
CaFile string
|
||||
InsecureSkipTLSverify bool
|
||||
PlainHTTP bool
|
||||
}
|
||||
|
||||
// NewDependency creates a new Dependency object with the given configuration.
|
||||
|
|
|
@ -24,9 +24,9 @@ import (
|
|||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"helm.sh/helm/v3/internal/test"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v4/internal/test"
|
||||
chart "helm.sh/helm/v4/pkg/chart/v2"
|
||||
chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
|
||||
)
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
|
|
|
@ -17,7 +17,7 @@ limitations under the License.
|
|||
package action
|
||||
|
||||
import (
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
release "helm.sh/helm/v4/pkg/release/v1"
|
||||
)
|
||||
|
||||
// Get is the action for checking a given release's information.
|
||||
|
|
|
@ -16,7 +16,13 @@ limitations under the License.
|
|||
|
||||
package action
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
chart "helm.sh/helm/v4/pkg/chart/v2"
|
||||
)
|
||||
|
||||
// GetMetadata is the action for checking a given release's metadata.
|
||||
//
|
||||
|
@ -28,14 +34,16 @@ type GetMetadata struct {
|
|||
}
|
||||
|
||||
type Metadata struct {
|
||||
Name string `json:"name" yaml:"name"`
|
||||
Chart string `json:"chart" yaml:"chart"`
|
||||
Version string `json:"version" yaml:"version"`
|
||||
AppVersion string `json:"appVersion" yaml:"appVersion"`
|
||||
Namespace string `json:"namespace" yaml:"namespace"`
|
||||
Revision int `json:"revision" yaml:"revision"`
|
||||
Status string `json:"status" yaml:"status"`
|
||||
DeployedAt string `json:"deployedAt" yaml:"deployedAt"`
|
||||
Name string `json:"name" yaml:"name"`
|
||||
Chart string `json:"chart" yaml:"chart"`
|
||||
Version string `json:"version" yaml:"version"`
|
||||
AppVersion string `json:"appVersion" yaml:"appVersion"`
|
||||
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
|
||||
Dependencies []*chart.Dependency `json:"dependencies,omitempty" yaml:"dependencies,omitempty"`
|
||||
Namespace string `json:"namespace" yaml:"namespace"`
|
||||
Revision int `json:"revision" yaml:"revision"`
|
||||
Status string `json:"status" yaml:"status"`
|
||||
DeployedAt string `json:"deployedAt" yaml:"deployedAt"`
|
||||
}
|
||||
|
||||
// NewGetMetadata creates a new GetMetadata object with the given configuration.
|
||||
|
@ -57,13 +65,26 @@ func (g *GetMetadata) Run(name string) (*Metadata, error) {
|
|||
}
|
||||
|
||||
return &Metadata{
|
||||
Name: rel.Name,
|
||||
Chart: rel.Chart.Metadata.Name,
|
||||
Version: rel.Chart.Metadata.Version,
|
||||
AppVersion: rel.Chart.Metadata.AppVersion,
|
||||
Namespace: rel.Namespace,
|
||||
Revision: rel.Version,
|
||||
Status: rel.Info.Status.String(),
|
||||
DeployedAt: rel.Info.LastDeployed.Format(time.RFC3339),
|
||||
Name: rel.Name,
|
||||
Chart: rel.Chart.Metadata.Name,
|
||||
Version: rel.Chart.Metadata.Version,
|
||||
AppVersion: rel.Chart.Metadata.AppVersion,
|
||||
Dependencies: rel.Chart.Metadata.Dependencies,
|
||||
Annotations: rel.Chart.Metadata.Annotations,
|
||||
Namespace: rel.Namespace,
|
||||
Revision: rel.Version,
|
||||
Status: rel.Info.Status.String(),
|
||||
DeployedAt: rel.Info.LastDeployed.Format(time.RFC3339),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// FormattedDepNames formats metadata.dependencies names into a comma-separated list.
|
||||
func (m *Metadata) FormattedDepNames() string {
|
||||
depsNames := make([]string, 0, len(m.Dependencies))
|
||||
for _, dep := range m.Dependencies {
|
||||
depsNames = append(depsNames, dep.Name)
|
||||
}
|
||||
sort.StringSlice(depsNames).Sort()
|
||||
|
||||
return strings.Join(depsNames, ",")
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ limitations under the License.
|
|||
package action
|
||||
|
||||
import (
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
|
||||
)
|
||||
|
||||
// GetValues is the action for checking a given release's values.
|
||||
|
|
|
@ -17,10 +17,12 @@ limitations under the License.
|
|||
package action
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
|
||||
"fmt"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
|
||||
release "helm.sh/helm/v4/pkg/release/v1"
|
||||
)
|
||||
|
||||
// History is the action for checking the release's ledger.
|
||||
|
@ -53,6 +55,6 @@ func (h *History) Run(name string) ([]*release.Release, error) {
|
|||
return nil, fmt.Errorf("release name is invalid: %s", name)
|
||||
}
|
||||
|
||||
h.cfg.Log("getting history for release %s", name)
|
||||
slog.Debug("getting history for release", "release", name)
|
||||
return h.cfg.Releases.History(name)
|
||||
}
|
||||
|
|
|
@ -18,16 +18,23 @@ package action
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"slices"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"helm.sh/helm/v3/pkg/kube"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
helmtime "helm.sh/helm/v3/pkg/time"
|
||||
"github.com/pkg/errors"
|
||||
"helm.sh/helm/v4/pkg/kube"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
release "helm.sh/helm/v4/pkg/release/v1"
|
||||
helmtime "helm.sh/helm/v4/pkg/time"
|
||||
)
|
||||
|
||||
// execHook executes all of the hooks for the given hook event.
|
||||
func (cfg *Configuration) execHook(rl *release.Release, hook release.HookEvent, timeout time.Duration) error {
|
||||
func (cfg *Configuration) execHook(rl *release.Release, hook release.HookEvent, waitStrategy kube.WaitStrategy, timeout time.Duration) error {
|
||||
executingHooks := []*release.Hook{}
|
||||
|
||||
for _, h := range rl.Hooks {
|
||||
|
@ -41,9 +48,9 @@ func (cfg *Configuration) execHook(rl *release.Release, hook release.HookEvent,
|
|||
// hooke are pre-ordered by kind, so keep order stable
|
||||
sort.Stable(hookByWeight(executingHooks))
|
||||
|
||||
for _, h := range executingHooks {
|
||||
for i, h := range executingHooks {
|
||||
// Set default delete policy to before-hook-creation
|
||||
if h.DeletePolicies == nil || len(h.DeletePolicies) == 0 {
|
||||
if len(h.DeletePolicies) == 0 {
|
||||
// TODO(jlegrone): Only apply before-hook-creation delete policy to run to completion
|
||||
// resources. For all other resource types update in place if a
|
||||
// resource with the same name already exists and is owned by the
|
||||
|
@ -51,7 +58,7 @@ func (cfg *Configuration) execHook(rl *release.Release, hook release.HookEvent,
|
|||
h.DeletePolicies = []release.HookDeletePolicy{release.HookBeforeHookCreation}
|
||||
}
|
||||
|
||||
if err := cfg.deleteHookByPolicy(h, release.HookBeforeHookCreation, timeout); err != nil {
|
||||
if err := cfg.deleteHookByPolicy(h, release.HookBeforeHookCreation, waitStrategy, timeout); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -79,28 +86,49 @@ func (cfg *Configuration) execHook(rl *release.Release, hook release.HookEvent,
|
|||
return fmt.Errorf("warning: Hook %s %s failed: %w", hook, h.Path, err)
|
||||
}
|
||||
|
||||
waiter, err := cfg.KubeClient.GetWaiter(waitStrategy)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to get waiter")
|
||||
}
|
||||
// Watch hook resources until they have completed
|
||||
err = cfg.KubeClient.WatchUntilReady(resources, timeout)
|
||||
err = waiter.WatchUntilReady(resources, timeout)
|
||||
// Note the time of success/failure
|
||||
h.LastRun.CompletedAt = helmtime.Now()
|
||||
// Mark hook as succeeded or failed
|
||||
if err != nil {
|
||||
h.LastRun.Phase = release.HookPhaseFailed
|
||||
// If a hook is failed, check the annotation of the hook to determine if we should copy the logs client side
|
||||
if errOutputting := cfg.outputLogsByPolicy(h, rl.Namespace, release.HookOutputOnFailed); errOutputting != nil {
|
||||
// We log the error here as we want to propagate the hook failure upwards to the release object.
|
||||
log.Printf("error outputting logs for hook failure: %v", errOutputting)
|
||||
}
|
||||
// If a hook is failed, check the annotation of the hook to determine whether the hook should be deleted
|
||||
// under failed condition. If so, then clear the corresponding resource object in the hook
|
||||
if err := cfg.deleteHookByPolicy(h, release.HookFailed, timeout); err != nil {
|
||||
if errDeleting := cfg.deleteHookByPolicy(h, release.HookFailed, waitStrategy, timeout); errDeleting != nil {
|
||||
// We log the error here as we want to propagate the hook failure upwards to the release object.
|
||||
log.Printf("error deleting the hook resource on hook failure: %v", errDeleting)
|
||||
}
|
||||
|
||||
// If a hook is failed, check the annotation of the previous successful hooks to determine whether the hooks
|
||||
// should be deleted under succeeded condition.
|
||||
if err := cfg.deleteHooksByPolicy(executingHooks[0:i], release.HookSucceeded, waitStrategy, timeout); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
h.LastRun.Phase = release.HookPhaseSucceeded
|
||||
}
|
||||
|
||||
// If all hooks are successful, check the annotation of each hook to determine whether the hook should be deleted
|
||||
// under succeeded condition. If so, then clear the corresponding resource object in each hook
|
||||
// or output should be logged under succeeded condition. If so, then clear the corresponding resource object in each hook
|
||||
for i := len(executingHooks) - 1; i >= 0; i-- {
|
||||
h := executingHooks[i]
|
||||
if err := cfg.deleteHookByPolicy(h, release.HookSucceeded, timeout); err != nil {
|
||||
if err := cfg.outputLogsByPolicy(h, rl.Namespace, release.HookOutputOnSucceeded); err != nil {
|
||||
// We log here as we still want to attempt hook resource deletion even if output logging fails.
|
||||
log.Printf("error outputting logs for hook failure: %v", err)
|
||||
}
|
||||
if err := cfg.deleteHookByPolicy(h, release.HookSucceeded, waitStrategy, timeout); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -121,7 +149,7 @@ func (x hookByWeight) Less(i, j int) bool {
|
|||
}
|
||||
|
||||
// deleteHookByPolicy deletes a hook if the hook policy instructs it to
|
||||
func (cfg *Configuration) deleteHookByPolicy(h *release.Hook, policy release.HookDeletePolicy, timeout time.Duration) error {
|
||||
func (cfg *Configuration) deleteHookByPolicy(h *release.Hook, policy release.HookDeletePolicy, waitStrategy kube.WaitStrategy, timeout time.Duration) error {
|
||||
// Never delete CustomResourceDefinitions; this could cause lots of
|
||||
// cascading garbage collection.
|
||||
if h.Kind == "CustomResourceDefinition" {
|
||||
|
@ -137,16 +165,28 @@ func (cfg *Configuration) deleteHookByPolicy(h *release.Hook, policy release.Hoo
|
|||
return joinErrors(errs, "; ")
|
||||
}
|
||||
|
||||
// wait for resources until they are deleted to avoid conflicts
|
||||
if kubeClient, ok := cfg.KubeClient.(kube.InterfaceExt); ok {
|
||||
if err := kubeClient.WaitForDelete(resources, timeout); err != nil {
|
||||
return err
|
||||
}
|
||||
waiter, err := cfg.KubeClient.GetWaiter(waitStrategy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := waiter.WaitForDelete(resources, timeout); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// deleteHooksByPolicy deletes all hooks if the hook policy instructs it to
|
||||
func (cfg *Configuration) deleteHooksByPolicy(hooks []*release.Hook, policy release.HookDeletePolicy, waitStrategy kube.WaitStrategy, timeout time.Duration) error {
|
||||
for _, h := range hooks {
|
||||
if err := cfg.deleteHookByPolicy(h, policy, waitStrategy, timeout); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// hookHasDeletePolicy determines whether the defined hook deletion policy matches the hook deletion polices
|
||||
// supported by helm. If so, mark the hook as one should be deleted.
|
||||
func hookHasDeletePolicy(h *release.Hook, policy release.HookDeletePolicy) bool {
|
||||
|
@ -157,3 +197,57 @@ func hookHasDeletePolicy(h *release.Hook, policy release.HookDeletePolicy) bool
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// outputLogsByPolicy outputs a pods logs if the hook policy instructs it to
|
||||
func (cfg *Configuration) outputLogsByPolicy(h *release.Hook, releaseNamespace string, policy release.HookOutputLogPolicy) error {
|
||||
if !hookHasOutputLogPolicy(h, policy) {
|
||||
return nil
|
||||
}
|
||||
namespace, err := cfg.deriveNamespace(h, releaseNamespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch h.Kind {
|
||||
case "Job":
|
||||
return cfg.outputContainerLogsForListOptions(namespace, metav1.ListOptions{LabelSelector: fmt.Sprintf("job-name=%s", h.Name)})
|
||||
case "Pod":
|
||||
return cfg.outputContainerLogsForListOptions(namespace, metav1.ListOptions{FieldSelector: fmt.Sprintf("metadata.name=%s", h.Name)})
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg *Configuration) outputContainerLogsForListOptions(namespace string, listOptions metav1.ListOptions) error {
|
||||
// TODO Helm 4: Remove this check when GetPodList and OutputContainerLogsForPodList are moved from InterfaceLogs to Interface
|
||||
if kubeClient, ok := cfg.KubeClient.(kube.InterfaceLogs); ok {
|
||||
podList, err := kubeClient.GetPodList(namespace, listOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = kubeClient.OutputContainerLogsForPodList(podList, namespace, cfg.HookOutputFunc)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cfg *Configuration) deriveNamespace(h *release.Hook, namespace string) (string, error) {
|
||||
tmp := struct {
|
||||
Metadata struct {
|
||||
Namespace string
|
||||
}
|
||||
}{}
|
||||
err := yaml.Unmarshal([]byte(h.Manifest), &tmp)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "unable to parse metadata.namespace from kubernetes manifest for output logs hook %s", h.Path)
|
||||
}
|
||||
if tmp.Metadata.Namespace == "" {
|
||||
return namespace, nil
|
||||
}
|
||||
return tmp.Metadata.Namespace, nil
|
||||
}
|
||||
|
||||
// hookHasOutputLogPolicy determines whether the defined hook output log policy matches the hook output log policies
|
||||
// supported by helm.
|
||||
func hookHasOutputLogPolicy(h *release.Hook, policy release.HookOutputLogPolicy) bool {
|
||||
return slices.Contains(h.OutputLogPolicies, policy)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,401 @@
|
|||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package action
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/util/yaml"
|
||||
"k8s.io/cli-runtime/pkg/resource"
|
||||
|
||||
chart "helm.sh/helm/v4/pkg/chart/v2"
|
||||
chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
|
||||
"helm.sh/helm/v4/pkg/kube"
|
||||
kubefake "helm.sh/helm/v4/pkg/kube/fake"
|
||||
release "helm.sh/helm/v4/pkg/release/v1"
|
||||
"helm.sh/helm/v4/pkg/storage"
|
||||
"helm.sh/helm/v4/pkg/storage/driver"
|
||||
)
|
||||
|
||||
func podManifestWithOutputLogs(hookDefinitions []release.HookOutputLogPolicy) string {
|
||||
hookDefinitionString := convertHooksToCommaSeparated(hookDefinitions)
|
||||
return fmt.Sprintf(`kind: Pod
|
||||
metadata:
|
||||
name: finding-sharky,
|
||||
annotations:
|
||||
"helm.sh/hook": pre-install
|
||||
"helm.sh/hook-output-log-policy": %s
|
||||
spec:
|
||||
containers:
|
||||
- name: sharky-test
|
||||
image: fake-image
|
||||
cmd: fake-command`, hookDefinitionString)
|
||||
}
|
||||
|
||||
func podManifestWithOutputLogWithNamespace(hookDefinitions []release.HookOutputLogPolicy) string {
|
||||
hookDefinitionString := convertHooksToCommaSeparated(hookDefinitions)
|
||||
return fmt.Sprintf(`kind: Pod
|
||||
metadata:
|
||||
name: finding-george
|
||||
namespace: sneaky-namespace
|
||||
annotations:
|
||||
"helm.sh/hook": pre-install
|
||||
"helm.sh/hook-output-log-policy": %s
|
||||
spec:
|
||||
containers:
|
||||
- name: george-test
|
||||
image: fake-image
|
||||
cmd: fake-command`, hookDefinitionString)
|
||||
}
|
||||
|
||||
func jobManifestWithOutputLog(hookDefinitions []release.HookOutputLogPolicy) string {
|
||||
hookDefinitionString := convertHooksToCommaSeparated(hookDefinitions)
|
||||
return fmt.Sprintf(`kind: Job
|
||||
apiVersion: batch/v1
|
||||
metadata:
|
||||
name: losing-religion
|
||||
annotations:
|
||||
"helm.sh/hook": pre-install
|
||||
"helm.sh/hook-output-log-policy": %s
|
||||
spec:
|
||||
completions: 1
|
||||
parallelism: 1
|
||||
activeDeadlineSeconds: 30
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: religion-container
|
||||
image: religion-image
|
||||
cmd: religion-command`, hookDefinitionString)
|
||||
}
|
||||
|
||||
func jobManifestWithOutputLogWithNamespace(hookDefinitions []release.HookOutputLogPolicy) string {
|
||||
hookDefinitionString := convertHooksToCommaSeparated(hookDefinitions)
|
||||
return fmt.Sprintf(`kind: Job
|
||||
apiVersion: batch/v1
|
||||
metadata:
|
||||
name: losing-religion
|
||||
namespace: rem-namespace
|
||||
annotations:
|
||||
"helm.sh/hook": pre-install
|
||||
"helm.sh/hook-output-log-policy": %s
|
||||
spec:
|
||||
completions: 1
|
||||
parallelism: 1
|
||||
activeDeadlineSeconds: 30
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: religion-container
|
||||
image: religion-image
|
||||
cmd: religion-command`, hookDefinitionString)
|
||||
}
|
||||
|
||||
func convertHooksToCommaSeparated(hookDefinitions []release.HookOutputLogPolicy) string {
|
||||
var commaSeparated string
|
||||
for i, policy := range hookDefinitions {
|
||||
if i+1 == len(hookDefinitions) {
|
||||
commaSeparated += policy.String()
|
||||
} else {
|
||||
commaSeparated += policy.String() + ","
|
||||
}
|
||||
}
|
||||
return commaSeparated
|
||||
}
|
||||
|
||||
func TestInstallRelease_HookOutputLogsOnFailure(t *testing.T) {
|
||||
// Should output on failure with expected namespace if hook-failed is set
|
||||
runInstallForHooksWithFailure(t, podManifestWithOutputLogs([]release.HookOutputLogPolicy{release.HookOutputOnFailed}), "spaced", true)
|
||||
runInstallForHooksWithFailure(t, podManifestWithOutputLogWithNamespace([]release.HookOutputLogPolicy{release.HookOutputOnFailed}), "sneaky-namespace", true)
|
||||
runInstallForHooksWithFailure(t, jobManifestWithOutputLog([]release.HookOutputLogPolicy{release.HookOutputOnFailed}), "spaced", true)
|
||||
runInstallForHooksWithFailure(t, jobManifestWithOutputLogWithNamespace([]release.HookOutputLogPolicy{release.HookOutputOnFailed}), "rem-namespace", true)
|
||||
|
||||
// Should not output on failure with expected namespace if hook-succeed is set
|
||||
runInstallForHooksWithFailure(t, podManifestWithOutputLogs([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded}), "", false)
|
||||
runInstallForHooksWithFailure(t, podManifestWithOutputLogWithNamespace([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded}), "", false)
|
||||
runInstallForHooksWithFailure(t, jobManifestWithOutputLog([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded}), "", false)
|
||||
runInstallForHooksWithFailure(t, jobManifestWithOutputLogWithNamespace([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded}), "", false)
|
||||
}
|
||||
|
||||
func TestInstallRelease_HookOutputLogsOnSuccess(t *testing.T) {
|
||||
// Should output on success with expected namespace if hook-succeeded is set
|
||||
runInstallForHooksWithSuccess(t, podManifestWithOutputLogs([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded}), "spaced", true)
|
||||
runInstallForHooksWithSuccess(t, podManifestWithOutputLogWithNamespace([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded}), "sneaky-namespace", true)
|
||||
runInstallForHooksWithSuccess(t, jobManifestWithOutputLog([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded}), "spaced", true)
|
||||
runInstallForHooksWithSuccess(t, jobManifestWithOutputLogWithNamespace([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded}), "rem-namespace", true)
|
||||
|
||||
// Should not output on success if hook-failed is set
|
||||
runInstallForHooksWithSuccess(t, podManifestWithOutputLogs([]release.HookOutputLogPolicy{release.HookOutputOnFailed}), "", false)
|
||||
runInstallForHooksWithSuccess(t, podManifestWithOutputLogWithNamespace([]release.HookOutputLogPolicy{release.HookOutputOnFailed}), "", false)
|
||||
runInstallForHooksWithSuccess(t, jobManifestWithOutputLog([]release.HookOutputLogPolicy{release.HookOutputOnFailed}), "", false)
|
||||
runInstallForHooksWithSuccess(t, jobManifestWithOutputLogWithNamespace([]release.HookOutputLogPolicy{release.HookOutputOnFailed}), "", false)
|
||||
}
|
||||
|
||||
func TestInstallRelease_HooksOutputLogsOnSuccessAndFailure(t *testing.T) {
|
||||
// Should output on success with expected namespace if hook-succeeded and hook-failed is set
|
||||
runInstallForHooksWithSuccess(t, podManifestWithOutputLogs([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded, release.HookOutputOnFailed}), "spaced", true)
|
||||
runInstallForHooksWithSuccess(t, podManifestWithOutputLogWithNamespace([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded, release.HookOutputOnFailed}), "sneaky-namespace", true)
|
||||
runInstallForHooksWithSuccess(t, jobManifestWithOutputLog([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded, release.HookOutputOnFailed}), "spaced", true)
|
||||
runInstallForHooksWithSuccess(t, jobManifestWithOutputLogWithNamespace([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded, release.HookOutputOnFailed}), "rem-namespace", true)
|
||||
|
||||
// Should output on failure if hook-succeeded and hook-failed is set
|
||||
runInstallForHooksWithFailure(t, podManifestWithOutputLogs([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded, release.HookOutputOnFailed}), "spaced", true)
|
||||
runInstallForHooksWithFailure(t, podManifestWithOutputLogWithNamespace([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded, release.HookOutputOnFailed}), "sneaky-namespace", true)
|
||||
runInstallForHooksWithFailure(t, jobManifestWithOutputLog([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded, release.HookOutputOnFailed}), "spaced", true)
|
||||
runInstallForHooksWithFailure(t, jobManifestWithOutputLogWithNamespace([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded, release.HookOutputOnFailed}), "rem-namespace", true)
|
||||
}
|
||||
|
||||
func runInstallForHooksWithSuccess(t *testing.T, manifest, expectedNamespace string, shouldOutput bool) {
|
||||
var expectedOutput string
|
||||
if shouldOutput {
|
||||
expectedOutput = fmt.Sprintf("attempted to output logs for namespace: %s", expectedNamespace)
|
||||
}
|
||||
is := assert.New(t)
|
||||
instAction := installAction(t)
|
||||
instAction.ReleaseName = "failed-hooks"
|
||||
outBuffer := &bytes.Buffer{}
|
||||
instAction.cfg.KubeClient = &kubefake.PrintingKubeClient{Out: io.Discard, LogOutput: outBuffer}
|
||||
|
||||
templates := []*chart.File{
|
||||
{Name: "templates/hello", Data: []byte("hello: world")},
|
||||
{Name: "templates/hooks", Data: []byte(manifest)},
|
||||
}
|
||||
vals := map[string]interface{}{}
|
||||
|
||||
res, err := instAction.Run(buildChartWithTemplates(templates), vals)
|
||||
is.NoError(err)
|
||||
is.Equal(expectedOutput, outBuffer.String())
|
||||
is.Equal(release.StatusDeployed, res.Info.Status)
|
||||
}
|
||||
|
||||
func runInstallForHooksWithFailure(t *testing.T, manifest, expectedNamespace string, shouldOutput bool) {
|
||||
var expectedOutput string
|
||||
if shouldOutput {
|
||||
expectedOutput = fmt.Sprintf("attempted to output logs for namespace: %s", expectedNamespace)
|
||||
}
|
||||
is := assert.New(t)
|
||||
instAction := installAction(t)
|
||||
instAction.ReleaseName = "failed-hooks"
|
||||
failingClient := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
|
||||
failingClient.WatchUntilReadyError = fmt.Errorf("failed watch")
|
||||
instAction.cfg.KubeClient = failingClient
|
||||
outBuffer := &bytes.Buffer{}
|
||||
failingClient.PrintingKubeClient = kubefake.PrintingKubeClient{Out: io.Discard, LogOutput: outBuffer}
|
||||
|
||||
templates := []*chart.File{
|
||||
{Name: "templates/hello", Data: []byte("hello: world")},
|
||||
{Name: "templates/hooks", Data: []byte(manifest)},
|
||||
}
|
||||
vals := map[string]interface{}{}
|
||||
|
||||
res, err := instAction.Run(buildChartWithTemplates(templates), vals)
|
||||
is.Error(err)
|
||||
is.Contains(res.Info.Description, "failed pre-install")
|
||||
is.Equal(expectedOutput, outBuffer.String())
|
||||
is.Equal(release.StatusFailed, res.Info.Status)
|
||||
}
|
||||
|
||||
type HookFailedError struct{}
|
||||
|
||||
func (e *HookFailedError) Error() string {
|
||||
return "Hook failed!"
|
||||
}
|
||||
|
||||
type HookFailingKubeClient struct {
|
||||
kubefake.PrintingKubeClient
|
||||
failOn resource.Info
|
||||
deleteRecord []resource.Info
|
||||
}
|
||||
|
||||
type HookFailingKubeWaiter struct {
|
||||
*kubefake.PrintingKubeWaiter
|
||||
failOn resource.Info
|
||||
}
|
||||
|
||||
func (*HookFailingKubeClient) Build(reader io.Reader, _ bool) (kube.ResourceList, error) {
|
||||
configMap := &v1.ConfigMap{}
|
||||
|
||||
err := yaml.NewYAMLOrJSONDecoder(reader, 1000).Decode(configMap)
|
||||
|
||||
if err != nil {
|
||||
return kube.ResourceList{}, err
|
||||
}
|
||||
|
||||
return kube.ResourceList{{
|
||||
Name: configMap.Name,
|
||||
Namespace: configMap.Namespace,
|
||||
}}, nil
|
||||
}
|
||||
|
||||
func (h *HookFailingKubeWaiter) WatchUntilReady(resources kube.ResourceList, _ time.Duration) error {
|
||||
for _, res := range resources {
|
||||
if res.Name == h.failOn.Name && res.Namespace == h.failOn.Namespace {
|
||||
return &HookFailedError{}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *HookFailingKubeClient) Delete(resources kube.ResourceList) (*kube.Result, []error) {
|
||||
for _, res := range resources {
|
||||
h.deleteRecord = append(h.deleteRecord, resource.Info{
|
||||
Name: res.Name,
|
||||
Namespace: res.Namespace,
|
||||
})
|
||||
}
|
||||
|
||||
return h.PrintingKubeClient.Delete(resources)
|
||||
}
|
||||
|
||||
func (h *HookFailingKubeClient) GetWaiter(strategy kube.WaitStrategy) (kube.Waiter, error) {
|
||||
waiter, _ := h.PrintingKubeClient.GetWaiter(strategy)
|
||||
return &HookFailingKubeWaiter{
|
||||
PrintingKubeWaiter: waiter.(*kubefake.PrintingKubeWaiter),
|
||||
failOn: h.failOn,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func TestHooksCleanUp(t *testing.T) {
|
||||
hookEvent := release.HookPreInstall
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
inputRelease release.Release
|
||||
failOn resource.Info
|
||||
expectedDeleteRecord []resource.Info
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
"Deletion hook runs for previously successful hook on failure of a heavier weight hook",
|
||||
release.Release{
|
||||
Name: "test-release",
|
||||
Namespace: "test",
|
||||
Hooks: []*release.Hook{
|
||||
{
|
||||
Name: "hook-1",
|
||||
Kind: "ConfigMap",
|
||||
Path: "templates/service_account.yaml",
|
||||
Manifest: `apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: build-config-1
|
||||
namespace: test
|
||||
data:
|
||||
foo: bar
|
||||
`,
|
||||
Weight: -5,
|
||||
Events: []release.HookEvent{
|
||||
hookEvent,
|
||||
},
|
||||
DeletePolicies: []release.HookDeletePolicy{
|
||||
release.HookBeforeHookCreation,
|
||||
release.HookSucceeded,
|
||||
release.HookFailed,
|
||||
},
|
||||
LastRun: release.HookExecution{
|
||||
Phase: release.HookPhaseSucceeded,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "hook-2",
|
||||
Kind: "ConfigMap",
|
||||
Path: "templates/job.yaml",
|
||||
Manifest: `apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: build-config-2
|
||||
namespace: test
|
||||
data:
|
||||
foo: bar
|
||||
`,
|
||||
Weight: 0,
|
||||
Events: []release.HookEvent{
|
||||
hookEvent,
|
||||
},
|
||||
DeletePolicies: []release.HookDeletePolicy{
|
||||
release.HookBeforeHookCreation,
|
||||
release.HookSucceeded,
|
||||
release.HookFailed,
|
||||
},
|
||||
LastRun: release.HookExecution{
|
||||
Phase: release.HookPhaseFailed,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, resource.Info{
|
||||
Name: "build-config-2",
|
||||
Namespace: "test",
|
||||
}, []resource.Info{
|
||||
{
|
||||
// This should be in the record for `before-hook-creation`
|
||||
Name: "build-config-1",
|
||||
Namespace: "test",
|
||||
},
|
||||
{
|
||||
// This should be in the record for `before-hook-creation`
|
||||
Name: "build-config-2",
|
||||
Namespace: "test",
|
||||
},
|
||||
{
|
||||
// This should be in the record for cleaning up (the failure first)
|
||||
Name: "build-config-2",
|
||||
Namespace: "test",
|
||||
},
|
||||
{
|
||||
// This should be in the record for cleaning up (then the previously successful)
|
||||
Name: "build-config-1",
|
||||
Namespace: "test",
|
||||
},
|
||||
}, true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
kubeClient := &HookFailingKubeClient{
|
||||
kubefake.PrintingKubeClient{Out: io.Discard}, tc.failOn, []resource.Info{},
|
||||
}
|
||||
|
||||
configuration := &Configuration{
|
||||
Releases: storage.Init(driver.NewMemory()),
|
||||
KubeClient: kubeClient,
|
||||
Capabilities: chartutil.DefaultCapabilities,
|
||||
}
|
||||
|
||||
err := configuration.execHook(&tc.inputRelease, hookEvent, kube.StatusWatcherStrategy, 600)
|
||||
|
||||
if !reflect.DeepEqual(kubeClient.deleteRecord, tc.expectedDeleteRecord) {
|
||||
t.Fatalf("Got unexpected delete record, expected: %#v, but got: %#v", kubeClient.deleteRecord, tc.expectedDeleteRecord)
|
||||
}
|
||||
|
||||
if err != nil && !tc.expectError {
|
||||
t.Fatalf("Got an unexpected error.")
|
||||
}
|
||||
|
||||
if err == nil && tc.expectError {
|
||||
t.Fatalf("Expected and error but did not get it.")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log/slog"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
|
@ -40,20 +41,20 @@ import (
|
|||
"k8s.io/cli-runtime/pkg/resource"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/cli"
|
||||
"helm.sh/helm/v3/pkg/downloader"
|
||||
"helm.sh/helm/v3/pkg/getter"
|
||||
"helm.sh/helm/v3/pkg/kube"
|
||||
kubefake "helm.sh/helm/v3/pkg/kube/fake"
|
||||
"helm.sh/helm/v3/pkg/postrender"
|
||||
"helm.sh/helm/v3/pkg/registry"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"helm.sh/helm/v3/pkg/releaseutil"
|
||||
"helm.sh/helm/v3/pkg/repo"
|
||||
"helm.sh/helm/v3/pkg/storage"
|
||||
"helm.sh/helm/v3/pkg/storage/driver"
|
||||
chart "helm.sh/helm/v4/pkg/chart/v2"
|
||||
chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
|
||||
"helm.sh/helm/v4/pkg/cli"
|
||||
"helm.sh/helm/v4/pkg/downloader"
|
||||
"helm.sh/helm/v4/pkg/getter"
|
||||
"helm.sh/helm/v4/pkg/kube"
|
||||
kubefake "helm.sh/helm/v4/pkg/kube/fake"
|
||||
"helm.sh/helm/v4/pkg/postrender"
|
||||
"helm.sh/helm/v4/pkg/registry"
|
||||
releaseutil "helm.sh/helm/v4/pkg/release/util"
|
||||
release "helm.sh/helm/v4/pkg/release/v1"
|
||||
"helm.sh/helm/v4/pkg/repo"
|
||||
"helm.sh/helm/v4/pkg/storage"
|
||||
"helm.sh/helm/v4/pkg/storage/driver"
|
||||
)
|
||||
|
||||
// notesFileSuffix that we want to treat special. It goes through the templating engine
|
||||
|
@ -80,7 +81,7 @@ type Install struct {
|
|||
HideSecret bool
|
||||
DisableHooks bool
|
||||
Replace bool
|
||||
Wait bool
|
||||
WaitStrategy kube.WaitStrategy
|
||||
WaitForJobs bool
|
||||
Devel bool
|
||||
DependencyUpdate bool
|
||||
|
@ -143,19 +144,19 @@ func NewInstall(cfg *Configuration) *Install {
|
|||
in := &Install{
|
||||
cfg: cfg,
|
||||
}
|
||||
in.ChartPathOptions.registryClient = cfg.RegistryClient
|
||||
in.registryClient = cfg.RegistryClient
|
||||
|
||||
return in
|
||||
}
|
||||
|
||||
// SetRegistryClient sets the registry client for the install action
|
||||
func (i *Install) SetRegistryClient(registryClient *registry.Client) {
|
||||
i.ChartPathOptions.registryClient = registryClient
|
||||
i.registryClient = registryClient
|
||||
}
|
||||
|
||||
// GetRegistryClient get the registry client.
|
||||
func (i *Install) GetRegistryClient() *registry.Client {
|
||||
return i.ChartPathOptions.registryClient
|
||||
return i.registryClient
|
||||
}
|
||||
|
||||
func (i *Install) installCRDs(crds []chart.CRD) error {
|
||||
|
@ -173,7 +174,7 @@ func (i *Install) installCRDs(crds []chart.CRD) error {
|
|||
// If the error is CRD already exists, continue.
|
||||
if apierrors.IsAlreadyExists(err) {
|
||||
crdName := res[0].Name
|
||||
i.cfg.Log("CRD %s is already present. Skipping.", crdName)
|
||||
slog.Debug("CRD is already present. Skipping", "crd", crdName)
|
||||
continue
|
||||
}
|
||||
return fmt.Errorf("failed to install CRD %s: %w", obj.Name, err)
|
||||
|
@ -181,8 +182,12 @@ func (i *Install) installCRDs(crds []chart.CRD) error {
|
|||
totalItems = append(totalItems, res...)
|
||||
}
|
||||
if len(totalItems) > 0 {
|
||||
waiter, err := i.cfg.KubeClient.GetWaiter(i.WaitStrategy)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to get waiter")
|
||||
}
|
||||
// Give time for the CRD to be recognized.
|
||||
if err := i.cfg.KubeClient.Wait(totalItems, 60*time.Second); err != nil {
|
||||
if err := waiter.Wait(totalItems, 60*time.Second); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -197,7 +202,7 @@ func (i *Install) installCRDs(crds []chart.CRD) error {
|
|||
return err
|
||||
}
|
||||
|
||||
i.cfg.Log("Clearing discovery cache")
|
||||
slog.Debug("clearing discovery cache")
|
||||
discoveryClient.Invalidate()
|
||||
|
||||
_, _ = discoveryClient.ServerGroups()
|
||||
|
@ -210,7 +215,7 @@ func (i *Install) installCRDs(crds []chart.CRD) error {
|
|||
return err
|
||||
}
|
||||
if resettable, ok := restMapper.(meta.ResettableRESTMapper); ok {
|
||||
i.cfg.Log("Clearing REST mapper cache")
|
||||
slog.Debug("clearing REST mapper cache")
|
||||
resettable.Reset()
|
||||
}
|
||||
}
|
||||
|
@ -234,21 +239,25 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma
|
|||
// Check reachability of cluster unless in client-only mode (e.g. `helm template` without `--validate`)
|
||||
if !i.ClientOnly {
|
||||
if err := i.cfg.KubeClient.IsReachable(); err != nil {
|
||||
return nil, err
|
||||
slog.Error(fmt.Sprintf("cluster reachability check failed: %v", err))
|
||||
return nil, errors.Wrap(err, "cluster reachability check failed")
|
||||
}
|
||||
}
|
||||
|
||||
// HideSecret must be used with dry run. Otherwise, return an error.
|
||||
if !i.isDryRun() && i.HideSecret {
|
||||
slog.Error("hiding Kubernetes secrets requires a dry-run mode")
|
||||
return nil, errors.New("Hiding Kubernetes secrets requires a dry-run mode")
|
||||
}
|
||||
|
||||
if err := i.availableName(); err != nil {
|
||||
return nil, err
|
||||
slog.Error("release name check failed", slog.Any("error", err))
|
||||
return nil, errors.Wrap(err, "release name check failed")
|
||||
}
|
||||
|
||||
if err := chartutil.ProcessDependenciesWithMerge(chrt, vals); err != nil {
|
||||
return nil, err
|
||||
if err := chartutil.ProcessDependencies(chrt, vals); err != nil {
|
||||
slog.Error("chart dependencies processing failed", slog.Any("error", err))
|
||||
return nil, errors.Wrap(err, "chart dependencies processing failed")
|
||||
}
|
||||
|
||||
var interactWithRemote bool
|
||||
|
@ -261,7 +270,7 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma
|
|||
if crds := chrt.CRDObjects(); !i.ClientOnly && !i.SkipCRDs && len(crds) > 0 {
|
||||
// On dry run, bail here
|
||||
if i.isDryRun() {
|
||||
i.cfg.Log("WARNING: This chart or one of its subcharts contains CRDs. Rendering may fail or contain inaccuracies.")
|
||||
slog.Warn("This chart or one of its subcharts contains CRDs. Rendering may fail or contain inaccuracies.")
|
||||
} else if err := i.installCRDs(crds); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -281,12 +290,14 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma
|
|||
mem.SetNamespace(i.Namespace)
|
||||
i.cfg.Releases = storage.Init(mem)
|
||||
} else if !i.ClientOnly && len(i.APIVersions) > 0 {
|
||||
i.cfg.Log("API Version list given outside of client only mode, this list will be ignored")
|
||||
slog.Debug("API Version list given outside of client only mode, this list will be ignored")
|
||||
}
|
||||
|
||||
// Make sure if Atomic is set, that wait is set as well. This makes it so
|
||||
// the user doesn't have to specify both
|
||||
i.Wait = i.Wait || i.Atomic
|
||||
if i.WaitStrategy == kube.HookOnlyStrategy && i.Atomic {
|
||||
i.WaitStrategy = kube.StatusWatcherStrategy
|
||||
}
|
||||
|
||||
caps, err := i.cfg.getCapabilities()
|
||||
if err != nil {
|
||||
|
@ -445,7 +456,7 @@ func (i *Install) performInstall(rel *release.Release, toBeAdopted kube.Resource
|
|||
var err error
|
||||
// pre-install hooks
|
||||
if !i.DisableHooks {
|
||||
if err := i.cfg.execHook(rel, release.HookPreInstall, i.Timeout); err != nil {
|
||||
if err := i.cfg.execHook(rel, release.HookPreInstall, i.WaitStrategy, i.Timeout); err != nil {
|
||||
return rel, fmt.Errorf("failed pre-install: %s", err)
|
||||
}
|
||||
}
|
||||
|
@ -456,25 +467,32 @@ func (i *Install) performInstall(rel *release.Release, toBeAdopted kube.Resource
|
|||
if len(toBeAdopted) == 0 && len(resources) > 0 {
|
||||
_, err = i.cfg.KubeClient.Create(resources)
|
||||
} else if len(resources) > 0 {
|
||||
_, err = i.cfg.KubeClient.Update(toBeAdopted, resources, i.Force)
|
||||
if i.TakeOwnership {
|
||||
_, err = i.cfg.KubeClient.(kube.InterfaceThreeWayMerge).UpdateThreeWayMerge(toBeAdopted, resources, i.Force)
|
||||
} else {
|
||||
_, err = i.cfg.KubeClient.Update(toBeAdopted, resources, i.Force)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return rel, err
|
||||
}
|
||||
|
||||
if i.Wait {
|
||||
if i.WaitForJobs {
|
||||
err = i.cfg.KubeClient.WaitWithJobs(resources, i.Timeout)
|
||||
} else {
|
||||
err = i.cfg.KubeClient.Wait(resources, i.Timeout)
|
||||
}
|
||||
if err != nil {
|
||||
return rel, err
|
||||
}
|
||||
waiter, err := i.cfg.KubeClient.GetWaiter(i.WaitStrategy)
|
||||
if err != nil {
|
||||
return rel, fmt.Errorf("failed to get waiter: %w", err)
|
||||
}
|
||||
|
||||
if i.WaitForJobs {
|
||||
err = waiter.WaitWithJobs(resources, i.Timeout)
|
||||
} else {
|
||||
err = waiter.Wait(resources, i.Timeout)
|
||||
}
|
||||
if err != nil {
|
||||
return rel, err
|
||||
}
|
||||
|
||||
if !i.DisableHooks {
|
||||
if err := i.cfg.execHook(rel, release.HookPostInstall, i.Timeout); err != nil {
|
||||
if err := i.cfg.execHook(rel, release.HookPostInstall, i.WaitStrategy, i.Timeout); err != nil {
|
||||
return rel, fmt.Errorf("failed post-install: %s", err)
|
||||
}
|
||||
}
|
||||
|
@ -493,7 +511,7 @@ func (i *Install) performInstall(rel *release.Release, toBeAdopted kube.Resource
|
|||
// One possible strategy would be to do a timed retry to see if we can get
|
||||
// this stored in the future.
|
||||
if err := i.recordRelease(rel); err != nil {
|
||||
i.cfg.Log("failed to record the release: %s", err)
|
||||
slog.Error("failed to record the release", slog.Any("error", err))
|
||||
}
|
||||
|
||||
return rel, nil
|
||||
|
@ -502,7 +520,7 @@ func (i *Install) performInstall(rel *release.Release, toBeAdopted kube.Resource
|
|||
func (i *Install) failRelease(rel *release.Release, err error) (*release.Release, error) {
|
||||
rel.SetStatus(release.StatusFailed, fmt.Sprintf("Release %q failed: %s", i.ReleaseName, err.Error()))
|
||||
if i.Atomic {
|
||||
i.cfg.Log("Install failed and atomic is set, uninstalling release")
|
||||
slog.Debug("install failed, uninstalling release", "release", i.ReleaseName)
|
||||
uninstall := NewUninstall(i.cfg)
|
||||
uninstall.DisableHooks = i.DisableHooks
|
||||
uninstall.KeepHistory = false
|
||||
|
@ -599,8 +617,8 @@ func (i *Install) replaceRelease(rel *release.Release) error {
|
|||
return i.recordRelease(last)
|
||||
}
|
||||
|
||||
// write the <data> to <output-dir>/<name>. <append> controls if the file is created or content will be appended
|
||||
func writeToFile(outputDir string, name string, data string, append bool) error {
|
||||
// write the <data> to <output-dir>/<name>. <appendData> controls if the file is created or content will be appended
|
||||
func writeToFile(outputDir string, name string, data string, appendData bool) error {
|
||||
outfileName := strings.Join([]string{outputDir, name}, string(filepath.Separator))
|
||||
|
||||
err := ensureDirectoryForFile(outfileName)
|
||||
|
@ -608,14 +626,15 @@ func writeToFile(outputDir string, name string, data string, append bool) error
|
|||
return err
|
||||
}
|
||||
|
||||
f, err := createOrOpenFile(outfileName, append)
|
||||
f, err := createOrOpenFile(outfileName, appendData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
_, err = f.WriteString(fmt.Sprintf("---\n# Source: %s\n%s\n", name, data))
|
||||
_, err = fmt.Fprintf(f, "---\n# Source: %s\n%s\n", name, data)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -624,8 +643,8 @@ func writeToFile(outputDir string, name string, data string, append bool) error
|
|||
return nil
|
||||
}
|
||||
|
||||
func createOrOpenFile(filename string, append bool) (*os.File, error) {
|
||||
if append {
|
||||
func createOrOpenFile(filename string, appendData bool) (*os.File, error) {
|
||||
if appendData {
|
||||
return os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0600)
|
||||
}
|
||||
return os.Create(filename)
|
||||
|
@ -770,6 +789,7 @@ func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) (
|
|||
getter.WithTLSClientConfig(c.CertFile, c.KeyFile, c.CaFile),
|
||||
getter.WithInsecureSkipVerifyTLS(c.InsecureSkipTLSverify),
|
||||
getter.WithPlainHTTP(c.PlainHTTP),
|
||||
getter.WithBasicAuth(c.Username, c.Password),
|
||||
},
|
||||
RepositoryConfig: settings.RepositoryConfig,
|
||||
RepositoryCache: settings.RepositoryCache,
|
||||
|
@ -784,8 +804,16 @@ func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) (
|
|||
dl.Verify = downloader.VerifyAlways
|
||||
}
|
||||
if c.RepoURL != "" {
|
||||
chartURL, err := repo.FindChartInAuthAndTLSAndPassRepoURL(c.RepoURL, c.Username, c.Password, name, version,
|
||||
c.CertFile, c.KeyFile, c.CaFile, c.InsecureSkipTLSverify, c.PassCredentialsAll, getter.All(settings))
|
||||
chartURL, err := repo.FindChartInRepoURL(
|
||||
c.RepoURL,
|
||||
name,
|
||||
getter.All(settings),
|
||||
repo.WithChartVersion(version),
|
||||
repo.WithClientTLS(c.CertFile, c.KeyFile, c.CaFile),
|
||||
repo.WithUsernamePassword(c.Username, c.Password),
|
||||
repo.WithInsecureSkipTLSverify(c.InsecureSkipTLSverify),
|
||||
repo.WithPassCredentialsAll(c.PassCredentialsAll),
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
|
@ -17,11 +17,13 @@ limitations under the License.
|
|||
package action
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
@ -32,14 +34,23 @@ import (
|
|||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
kuberuntime "k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/cli-runtime/pkg/resource"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest/fake"
|
||||
|
||||
"helm.sh/helm/v3/internal/test"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
kubefake "helm.sh/helm/v3/pkg/kube/fake"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"helm.sh/helm/v3/pkg/storage/driver"
|
||||
helmtime "helm.sh/helm/v3/pkg/time"
|
||||
"helm.sh/helm/v4/internal/test"
|
||||
chart "helm.sh/helm/v4/pkg/chart/v2"
|
||||
chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
|
||||
"helm.sh/helm/v4/pkg/kube"
|
||||
kubefake "helm.sh/helm/v4/pkg/kube/fake"
|
||||
release "helm.sh/helm/v4/pkg/release/v1"
|
||||
"helm.sh/helm/v4/pkg/storage/driver"
|
||||
helmtime "helm.sh/helm/v4/pkg/time"
|
||||
)
|
||||
|
||||
type nameTemplateTestCase struct {
|
||||
|
@ -48,6 +59,62 @@ type nameTemplateTestCase struct {
|
|||
expectedErrorStr string
|
||||
}
|
||||
|
||||
func createDummyResourceList(owned bool) kube.ResourceList {
|
||||
obj := &appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "dummyName",
|
||||
Namespace: "spaced",
|
||||
},
|
||||
}
|
||||
|
||||
if owned {
|
||||
obj.Labels = map[string]string{
|
||||
"app.kubernetes.io/managed-by": "Helm",
|
||||
}
|
||||
obj.Annotations = map[string]string{
|
||||
"meta.helm.sh/release-name": "test-install-release",
|
||||
"meta.helm.sh/release-namespace": "spaced",
|
||||
}
|
||||
}
|
||||
|
||||
resInfo := resource.Info{
|
||||
Name: "dummyName",
|
||||
Namespace: "spaced",
|
||||
Mapping: &meta.RESTMapping{
|
||||
Resource: schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployment"},
|
||||
GroupVersionKind: schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"},
|
||||
Scope: meta.RESTScopeNamespace,
|
||||
},
|
||||
Object: obj,
|
||||
}
|
||||
body := io.NopCloser(bytes.NewReader([]byte(kuberuntime.EncodeOrDie(appsv1Codec, obj))))
|
||||
|
||||
resInfo.Client = &fake.RESTClient{
|
||||
GroupVersion: schema.GroupVersion{Group: "apps", Version: "v1"},
|
||||
NegotiatedSerializer: scheme.Codecs.WithoutConversion(),
|
||||
Client: fake.CreateHTTPClient(func(_ *http.Request) (*http.Response, error) {
|
||||
header := http.Header{}
|
||||
header.Set("Content-Type", kuberuntime.ContentTypeJSON)
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Header: header,
|
||||
Body: body,
|
||||
}, nil
|
||||
}),
|
||||
}
|
||||
var resourceList kube.ResourceList
|
||||
resourceList.Append(&resInfo)
|
||||
return resourceList
|
||||
}
|
||||
|
||||
func installActionWithConfig(config *Configuration) *Install {
|
||||
instAction := NewInstall(config)
|
||||
instAction.Namespace = "spaced"
|
||||
instAction.ReleaseName = "test-install-release"
|
||||
|
||||
return instAction
|
||||
}
|
||||
|
||||
func installAction(t *testing.T) *Install {
|
||||
config := actionConfigFixture(t)
|
||||
instAction := NewInstall(config)
|
||||
|
@ -93,6 +160,61 @@ func TestInstallRelease(t *testing.T) {
|
|||
is.Equal(lastRelease.Info.Status, release.StatusDeployed)
|
||||
}
|
||||
|
||||
func TestInstallReleaseWithTakeOwnership_ResourceNotOwned(t *testing.T) {
|
||||
// This test will test checking ownership of a resource
|
||||
// returned by the fake client. If the resource is not
|
||||
// owned by the chart, ownership is taken.
|
||||
// To verify ownership has been taken, the fake client
|
||||
// needs to store state which is a bigger rewrite.
|
||||
// TODO: Ensure fake kube client stores state. Maybe using
|
||||
// "k8s.io/client-go/kubernetes/fake" could be sufficient? i.e
|
||||
// "Client{Namespace: namespace, kubeClient: k8sfake.NewClientset()}"
|
||||
|
||||
is := assert.New(t)
|
||||
|
||||
// Resource list from cluster is NOT owned by helm chart
|
||||
config := actionConfigFixtureWithDummyResources(t, createDummyResourceList(false))
|
||||
instAction := installActionWithConfig(config)
|
||||
instAction.TakeOwnership = true
|
||||
res, err := instAction.Run(buildChart(), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed install: %s", err)
|
||||
}
|
||||
|
||||
rel, err := instAction.cfg.Releases.Get(res.Name, res.Version)
|
||||
is.NoError(err)
|
||||
|
||||
is.Equal(rel.Info.Description, "Install complete")
|
||||
}
|
||||
|
||||
func TestInstallReleaseWithTakeOwnership_ResourceOwned(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
// Resource list from cluster is owned by helm chart
|
||||
config := actionConfigFixtureWithDummyResources(t, createDummyResourceList(true))
|
||||
instAction := installActionWithConfig(config)
|
||||
instAction.TakeOwnership = false
|
||||
res, err := instAction.Run(buildChart(), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed install: %s", err)
|
||||
}
|
||||
rel, err := instAction.cfg.Releases.Get(res.Name, res.Version)
|
||||
is.NoError(err)
|
||||
|
||||
is.Equal(rel.Info.Description, "Install complete")
|
||||
}
|
||||
|
||||
func TestInstallReleaseWithTakeOwnership_ResourceOwnedNoFlag(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
// Resource list from cluster is NOT owned by helm chart
|
||||
config := actionConfigFixtureWithDummyResources(t, createDummyResourceList(false))
|
||||
instAction := installActionWithConfig(config)
|
||||
_, err := instAction.Run(buildChart(), nil)
|
||||
is.Error(err)
|
||||
is.Contains(err.Error(), "Unable to continue with install")
|
||||
}
|
||||
|
||||
func TestInstallReleaseWithValues(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
instAction := installAction(t)
|
||||
|
@ -356,11 +478,14 @@ func TestInstallRelease_FailedHooks(t *testing.T) {
|
|||
failer := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
|
||||
failer.WatchUntilReadyError = fmt.Errorf("Failed watch")
|
||||
instAction.cfg.KubeClient = failer
|
||||
outBuffer := &bytes.Buffer{}
|
||||
failer.PrintingKubeClient = kubefake.PrintingKubeClient{Out: io.Discard, LogOutput: outBuffer}
|
||||
|
||||
vals := map[string]interface{}{}
|
||||
res, err := instAction.Run(buildChart(), vals)
|
||||
is.Error(err)
|
||||
is.Contains(res.Info.Description, "failed post-install")
|
||||
is.Equal("", outBuffer.String())
|
||||
is.Equal(release.StatusFailed, res.Info.Status)
|
||||
}
|
||||
|
||||
|
@ -409,7 +534,7 @@ func TestInstallRelease_Wait(t *testing.T) {
|
|||
failer := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
|
||||
failer.WaitError = fmt.Errorf("I timed out")
|
||||
instAction.cfg.KubeClient = failer
|
||||
instAction.Wait = true
|
||||
instAction.WaitStrategy = kube.StatusWatcherStrategy
|
||||
vals := map[string]interface{}{}
|
||||
|
||||
goroutines := runtime.NumGoroutine()
|
||||
|
@ -428,7 +553,7 @@ func TestInstallRelease_Wait_Interrupted(t *testing.T) {
|
|||
failer := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
|
||||
failer.WaitDuration = 10 * time.Second
|
||||
instAction.cfg.KubeClient = failer
|
||||
instAction.Wait = true
|
||||
instAction.WaitStrategy = kube.StatusWatcherStrategy
|
||||
vals := map[string]interface{}{}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
@ -451,7 +576,7 @@ func TestInstallRelease_WaitForJobs(t *testing.T) {
|
|||
failer := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
|
||||
failer.WaitError = fmt.Errorf("I timed out")
|
||||
instAction.cfg.KubeClient = failer
|
||||
instAction.Wait = true
|
||||
instAction.WaitStrategy = kube.StatusWatcherStrategy
|
||||
instAction.WaitForJobs = true
|
||||
vals := map[string]interface{}{}
|
||||
|
||||
|
@ -518,6 +643,8 @@ func TestInstallRelease_Atomic_Interrupted(t *testing.T) {
|
|||
ctx, cancel := context.WithCancel(context.Background())
|
||||
time.AfterFunc(time.Second, cancel)
|
||||
|
||||
goroutines := runtime.NumGoroutine()
|
||||
|
||||
res, err := instAction.RunWithContext(ctx, buildChart(), vals)
|
||||
is.Error(err)
|
||||
is.Contains(err.Error(), "context canceled")
|
||||
|
@ -528,6 +655,9 @@ func TestInstallRelease_Atomic_Interrupted(t *testing.T) {
|
|||
_, err = instAction.cfg.Releases.Get(res.Name, res.Version)
|
||||
is.Error(err)
|
||||
is.Equal(err, driver.ErrReleaseNotFound)
|
||||
is.Equal(goroutines+1, runtime.NumGoroutine()) // installation goroutine still is in background
|
||||
time.Sleep(10 * time.Second) // wait for goroutine to finish
|
||||
is.Equal(goroutines, runtime.NumGoroutine())
|
||||
|
||||
}
|
||||
func TestNameTemplate(t *testing.T) {
|
||||
|
|
|
@ -22,9 +22,9 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/lint"
|
||||
"helm.sh/helm/v3/pkg/lint/support"
|
||||
chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
|
||||
"helm.sh/helm/v4/pkg/lint"
|
||||
"helm.sh/helm/v4/pkg/lint/support"
|
||||
)
|
||||
|
||||
// Lint is the action for checking that the semantics of a chart are well-formed.
|
||||
|
@ -125,5 +125,11 @@ func lintChart(path string, vals map[string]interface{}, namespace string, kubeV
|
|||
return linter, fmt.Errorf("unable to check Chart.yaml file in chart: %w", err)
|
||||
}
|
||||
|
||||
return lint.AllWithKubeVersionAndSchemaValidation(chartPath, vals, namespace, kubeVersion, skipSchemaValidation), nil
|
||||
return lint.RunAll(
|
||||
chartPath,
|
||||
vals,
|
||||
namespace,
|
||||
lint.WithKubeVersion(kubeVersion),
|
||||
lint.WithSkipSchemaValidation(skipSchemaValidation),
|
||||
), nil
|
||||
}
|
||||
|
|
|
@ -22,8 +22,8 @@ import (
|
|||
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"helm.sh/helm/v3/pkg/releaseutil"
|
||||
releaseutil "helm.sh/helm/v4/pkg/release/util"
|
||||
release "helm.sh/helm/v4/pkg/release/v1"
|
||||
)
|
||||
|
||||
// ListStates represents zero or more status codes that a list item may have set
|
||||
|
|
|
@ -21,8 +21,8 @@ import (
|
|||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"helm.sh/helm/v3/pkg/storage"
|
||||
release "helm.sh/helm/v4/pkg/release/v1"
|
||||
"helm.sh/helm/v4/pkg/storage"
|
||||
)
|
||||
|
||||
func TestListStates(t *testing.T) {
|
||||
|
|
|
@ -26,9 +26,9 @@ import (
|
|||
"github.com/Masterminds/semver/v3"
|
||||
"golang.org/x/term"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chart/loader"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/provenance"
|
||||
"helm.sh/helm/v4/pkg/chart/v2/loader"
|
||||
chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
|
||||
"helm.sh/helm/v4/pkg/provenance"
|
||||
)
|
||||
|
||||
// Package is the action for packaging a chart.
|
||||
|
@ -39,15 +39,27 @@ type Package struct {
|
|||
Key string
|
||||
Keyring string
|
||||
PassphraseFile string
|
||||
cachedPassphrase []byte
|
||||
Version string
|
||||
AppVersion string
|
||||
Destination string
|
||||
DependencyUpdate bool
|
||||
|
||||
RepositoryConfig string
|
||||
RepositoryCache string
|
||||
RepositoryConfig string
|
||||
RepositoryCache string
|
||||
PlainHTTP bool
|
||||
Username string
|
||||
Password string
|
||||
CertFile string
|
||||
KeyFile string
|
||||
CaFile string
|
||||
InsecureSkipTLSverify bool
|
||||
}
|
||||
|
||||
const (
|
||||
passPhraseFileStdin = "-"
|
||||
)
|
||||
|
||||
// NewPackage creates a new Package object with the given configuration.
|
||||
func NewPackage() *Package {
|
||||
return &Package{}
|
||||
|
@ -121,7 +133,7 @@ func (p *Package) Clearsign(filename string) error {
|
|||
|
||||
passphraseFetcher := promptUser
|
||||
if p.PassphraseFile != "" {
|
||||
passphraseFetcher, err = passphraseFileFetcher(p.PassphraseFile, os.Stdin)
|
||||
passphraseFetcher, err = p.passphraseFileFetcher(p.PassphraseFile, os.Stdin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -149,25 +161,42 @@ func promptUser(name string) ([]byte, error) {
|
|||
return pw, err
|
||||
}
|
||||
|
||||
func passphraseFileFetcher(passphraseFile string, stdin *os.File) (provenance.PassphraseFetcher, error) {
|
||||
file, err := openPassphraseFile(passphraseFile, stdin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
func (p *Package) passphraseFileFetcher(passphraseFile string, stdin *os.File) (provenance.PassphraseFetcher, error) {
|
||||
// When reading from stdin we cache the passphrase here. If we are
|
||||
// packaging multiple charts, we reuse the cached passphrase. This
|
||||
// allows giving the passphrase once on stdin without failing with
|
||||
// complaints about stdin already being closed.
|
||||
//
|
||||
// An alternative to this would be to omit file.Close() for stdin
|
||||
// below and require the user to provide the same passphrase once
|
||||
// per chart on stdin, but that does not seem very user-friendly.
|
||||
|
||||
reader := bufio.NewReader(file)
|
||||
passphrase, _, err := reader.ReadLine()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if p.cachedPassphrase == nil {
|
||||
file, err := openPassphraseFile(passphraseFile, stdin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
reader := bufio.NewReader(file)
|
||||
passphrase, _, err := reader.ReadLine()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.cachedPassphrase = passphrase
|
||||
|
||||
return func(_ string) ([]byte, error) {
|
||||
return passphrase, nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
return func(_ string) ([]byte, error) {
|
||||
return passphrase, nil
|
||||
return p.cachedPassphrase, nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
func openPassphraseFile(passphraseFile string, stdin *os.File) (*os.File, error) {
|
||||
if passphraseFile == "-" {
|
||||
if passphraseFile == passPhraseFileStdin {
|
||||
stat, err := stdin.Stat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -23,14 +23,15 @@ import (
|
|||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
|
||||
"helm.sh/helm/v3/internal/test/ensure"
|
||||
"helm.sh/helm/v4/internal/test/ensure"
|
||||
)
|
||||
|
||||
func TestPassphraseFileFetcher(t *testing.T) {
|
||||
secret := "secret"
|
||||
directory := ensure.TempFile(t, "passphrase-file", []byte(secret))
|
||||
testPkg := NewPackage()
|
||||
|
||||
fetcher, err := passphraseFileFetcher(path.Join(directory, "passphrase-file"), nil)
|
||||
fetcher, err := testPkg.passphraseFileFetcher(path.Join(directory, "passphrase-file"), nil)
|
||||
if err != nil {
|
||||
t.Fatal("Unable to create passphraseFileFetcher", err)
|
||||
}
|
||||
|
@ -48,8 +49,9 @@ func TestPassphraseFileFetcher(t *testing.T) {
|
|||
func TestPassphraseFileFetcher_WithLineBreak(t *testing.T) {
|
||||
secret := "secret"
|
||||
directory := ensure.TempFile(t, "passphrase-file", []byte(secret+"\n\n."))
|
||||
testPkg := NewPackage()
|
||||
|
||||
fetcher, err := passphraseFileFetcher(path.Join(directory, "passphrase-file"), nil)
|
||||
fetcher, err := testPkg.passphraseFileFetcher(path.Join(directory, "passphrase-file"), nil)
|
||||
if err != nil {
|
||||
t.Fatal("Unable to create passphraseFileFetcher", err)
|
||||
}
|
||||
|
@ -66,17 +68,48 @@ func TestPassphraseFileFetcher_WithLineBreak(t *testing.T) {
|
|||
|
||||
func TestPassphraseFileFetcher_WithInvalidStdin(t *testing.T) {
|
||||
directory := t.TempDir()
|
||||
testPkg := NewPackage()
|
||||
|
||||
stdin, err := os.CreateTemp(directory, "non-existing")
|
||||
if err != nil {
|
||||
t.Fatal("Unable to create test file", err)
|
||||
}
|
||||
|
||||
if _, err := passphraseFileFetcher("-", stdin); err == nil {
|
||||
if _, err := testPkg.passphraseFileFetcher("-", stdin); err == nil {
|
||||
t.Error("Expected passphraseFileFetcher returning an error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPassphraseFileFetcher_WithStdinAndMultipleFetches(t *testing.T) {
|
||||
testPkg := NewPackage()
|
||||
stdin, w, err := os.Pipe()
|
||||
if err != nil {
|
||||
t.Fatal("Unable to create pipe", err)
|
||||
}
|
||||
|
||||
passphrase := "secret-from-stdin"
|
||||
|
||||
go func() {
|
||||
w.Write([]byte(passphrase + "\n"))
|
||||
}()
|
||||
|
||||
for i := 0; i < 4; i++ {
|
||||
fetcher, err := testPkg.passphraseFileFetcher("-", stdin)
|
||||
if err != nil {
|
||||
t.Errorf("Expected passphraseFileFetcher to not return an error, but got %v", err)
|
||||
}
|
||||
|
||||
pass, err := fetcher("key")
|
||||
if err != nil {
|
||||
t.Errorf("Expected passphraseFileFetcher invocation to succeed, failed with %v", err)
|
||||
}
|
||||
|
||||
if string(pass) != string(passphrase) {
|
||||
t.Errorf("Expected multiple passphrase fetch to return %q, got %q", passphrase, pass)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateVersion(t *testing.T) {
|
||||
type args struct {
|
||||
ver string
|
||||
|
|
|
@ -22,12 +22,12 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/cli"
|
||||
"helm.sh/helm/v3/pkg/downloader"
|
||||
"helm.sh/helm/v3/pkg/getter"
|
||||
"helm.sh/helm/v3/pkg/registry"
|
||||
"helm.sh/helm/v3/pkg/repo"
|
||||
chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
|
||||
"helm.sh/helm/v4/pkg/cli"
|
||||
"helm.sh/helm/v4/pkg/downloader"
|
||||
"helm.sh/helm/v4/pkg/getter"
|
||||
"helm.sh/helm/v4/pkg/registry"
|
||||
"helm.sh/helm/v4/pkg/repo"
|
||||
)
|
||||
|
||||
// Pull is the action for checking a given release's information.
|
||||
|
@ -54,13 +54,8 @@ func WithConfig(cfg *Configuration) PullOpt {
|
|||
}
|
||||
}
|
||||
|
||||
// NewPull creates a new Pull object.
|
||||
func NewPull() *Pull {
|
||||
return NewPullWithOpts()
|
||||
}
|
||||
|
||||
// NewPullWithOpts creates a new pull, with configuration options.
|
||||
func NewPullWithOpts(opts ...PullOpt) *Pull {
|
||||
// NewPull creates a new Pull with configuration options.
|
||||
func NewPull(opts ...PullOpt) *Pull {
|
||||
p := &Pull{}
|
||||
for _, fn := range opts {
|
||||
fn(p)
|
||||
|
@ -120,7 +115,16 @@ func (p *Pull) Run(chartRef string) (string, error) {
|
|||
}
|
||||
|
||||
if p.RepoURL != "" {
|
||||
chartURL, err := repo.FindChartInAuthAndTLSAndPassRepoURL(p.RepoURL, p.Username, p.Password, chartRef, p.Version, p.CertFile, p.KeyFile, p.CaFile, p.InsecureSkipTLSverify, p.PassCredentialsAll, getter.All(p.Settings))
|
||||
chartURL, err := repo.FindChartInRepoURL(
|
||||
p.RepoURL,
|
||||
chartRef,
|
||||
getter.All(p.Settings),
|
||||
repo.WithChartVersion(p.Version),
|
||||
repo.WithClientTLS(p.CertFile, p.KeyFile, p.CaFile),
|
||||
repo.WithUsernamePassword(p.Username, p.Password),
|
||||
repo.WithInsecureSkipTLSverify(p.InsecureSkipTLSverify),
|
||||
repo.WithPassCredentialsAll(p.PassCredentialsAll),
|
||||
)
|
||||
if err != nil {
|
||||
return out.String(), err
|
||||
}
|
||||
|
|
|
@ -20,10 +20,10 @@ import (
|
|||
"io"
|
||||
"strings"
|
||||
|
||||
"helm.sh/helm/v3/pkg/cli"
|
||||
"helm.sh/helm/v3/pkg/pusher"
|
||||
"helm.sh/helm/v3/pkg/registry"
|
||||
"helm.sh/helm/v3/pkg/uploader"
|
||||
"helm.sh/helm/v4/pkg/cli"
|
||||
"helm.sh/helm/v4/pkg/pusher"
|
||||
"helm.sh/helm/v4/pkg/registry"
|
||||
"helm.sh/helm/v4/pkg/uploader"
|
||||
)
|
||||
|
||||
// Push is the action for uploading a chart.
|
||||
|
|
|
@ -19,16 +19,17 @@ package action
|
|||
import (
|
||||
"io"
|
||||
|
||||
"helm.sh/helm/v3/pkg/registry"
|
||||
"helm.sh/helm/v4/pkg/registry"
|
||||
)
|
||||
|
||||
// RegistryLogin performs a registry login operation.
|
||||
type RegistryLogin struct {
|
||||
cfg *Configuration
|
||||
certFile string
|
||||
keyFile string
|
||||
caFile string
|
||||
insecure bool
|
||||
cfg *Configuration
|
||||
certFile string
|
||||
keyFile string
|
||||
caFile string
|
||||
insecure bool
|
||||
plainHTTP bool
|
||||
}
|
||||
|
||||
type RegistryLoginOpt func(*RegistryLogin) error
|
||||
|
@ -41,7 +42,7 @@ func WithCertFile(certFile string) RegistryLoginOpt {
|
|||
}
|
||||
}
|
||||
|
||||
// WithKeyFile specifies whether to very certificates when communicating.
|
||||
// WithInsecure specifies whether to verify certificates.
|
||||
func WithInsecure(insecure bool) RegistryLoginOpt {
|
||||
return func(r *RegistryLogin) error {
|
||||
r.insecure = insecure
|
||||
|
@ -65,6 +66,14 @@ func WithCAFile(caFile string) RegistryLoginOpt {
|
|||
}
|
||||
}
|
||||
|
||||
// WithPlainHTTPLogin use http rather than https for login.
|
||||
func WithPlainHTTPLogin(isPlain bool) RegistryLoginOpt {
|
||||
return func(r *RegistryLogin) error {
|
||||
r.plainHTTP = isPlain
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// NewRegistryLogin creates a new RegistryLogin object with the given configuration.
|
||||
func NewRegistryLogin(cfg *Configuration) *RegistryLogin {
|
||||
return &RegistryLogin{
|
||||
|
@ -84,5 +93,7 @@ func (a *RegistryLogin) Run(_ io.Writer, hostname string, username string, passw
|
|||
hostname,
|
||||
registry.LoginOptBasicAuth(username, password),
|
||||
registry.LoginOptInsecure(a.insecure),
|
||||
registry.LoginOptTLSClientConfig(a.certFile, a.keyFile, a.caFile))
|
||||
registry.LoginOptTLSClientConfig(a.certFile, a.keyFile, a.caFile),
|
||||
registry.LoginOptPlainText(a.plainHTTP),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -20,13 +20,15 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"slices"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
|
||||
"helm.sh/helm/v4/pkg/kube"
|
||||
release "helm.sh/helm/v4/pkg/release/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -74,7 +76,7 @@ func (r *ReleaseTesting) Run(name string) (*release.Release, error) {
|
|||
executingHooks := []*release.Hook{}
|
||||
if len(r.Filters[ExcludeNameFilter]) != 0 {
|
||||
for _, h := range rel.Hooks {
|
||||
if contains(r.Filters[ExcludeNameFilter], h.Name) {
|
||||
if slices.Contains(r.Filters[ExcludeNameFilter], h.Name) {
|
||||
skippedHooks = append(skippedHooks, h)
|
||||
} else {
|
||||
executingHooks = append(executingHooks, h)
|
||||
|
@ -85,7 +87,7 @@ func (r *ReleaseTesting) Run(name string) (*release.Release, error) {
|
|||
if len(r.Filters[IncludeNameFilter]) != 0 {
|
||||
executingHooks = nil
|
||||
for _, h := range rel.Hooks {
|
||||
if contains(r.Filters[IncludeNameFilter], h.Name) {
|
||||
if slices.Contains(r.Filters[IncludeNameFilter], h.Name) {
|
||||
executingHooks = append(executingHooks, h)
|
||||
} else {
|
||||
skippedHooks = append(skippedHooks, h)
|
||||
|
@ -94,7 +96,7 @@ func (r *ReleaseTesting) Run(name string) (*release.Release, error) {
|
|||
rel.Hooks = executingHooks
|
||||
}
|
||||
|
||||
if err := r.cfg.execHook(rel, release.HookTest, r.Timeout); err != nil {
|
||||
if err := r.cfg.execHook(rel, release.HookTest, kube.StatusWatcherStrategy, r.Timeout); err != nil {
|
||||
rel.Hooks = append(skippedHooks, rel.Hooks...)
|
||||
r.cfg.Releases.Update(rel)
|
||||
return rel, err
|
||||
|
@ -118,10 +120,10 @@ func (r *ReleaseTesting) GetPodLogs(out io.Writer, rel *release.Release) error {
|
|||
for _, h := range hooksByWight {
|
||||
for _, e := range h.Events {
|
||||
if e == release.HookTest {
|
||||
if contains(r.Filters[ExcludeNameFilter], h.Name) {
|
||||
if slices.Contains(r.Filters[ExcludeNameFilter], h.Name) {
|
||||
continue
|
||||
}
|
||||
if len(r.Filters[IncludeNameFilter]) > 0 && !contains(r.Filters[IncludeNameFilter], h.Name) {
|
||||
if len(r.Filters[IncludeNameFilter]) > 0 && !slices.Contains(r.Filters[IncludeNameFilter], h.Name) {
|
||||
continue
|
||||
}
|
||||
req := client.CoreV1().Pods(r.Namespace).GetLogs(h.Name, &v1.PodLogOptions{})
|
||||
|
@ -141,12 +143,3 @@ func (r *ReleaseTesting) GetPodLogs(out io.Writer, rel *release.Release) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func contains(arr []string, value string) bool {
|
||||
for _, item := range arr {
|
||||
if item == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -19,8 +19,8 @@ package action
|
|||
import (
|
||||
"strings"
|
||||
|
||||
"helm.sh/helm/v3/pkg/kube"
|
||||
"helm.sh/helm/v3/pkg/releaseutil"
|
||||
"helm.sh/helm/v4/pkg/kube"
|
||||
releaseutil "helm.sh/helm/v4/pkg/release/util"
|
||||
)
|
||||
|
||||
func filterManifestsToKeep(manifests []releaseutil.Manifest) (keep, remaining []releaseutil.Manifest) {
|
||||
|
|
|
@ -19,12 +19,14 @@ package action
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
helmtime "helm.sh/helm/v3/pkg/time"
|
||||
chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
|
||||
"helm.sh/helm/v4/pkg/kube"
|
||||
release "helm.sh/helm/v4/pkg/release/v1"
|
||||
helmtime "helm.sh/helm/v4/pkg/time"
|
||||
)
|
||||
|
||||
// Rollback is the action for rolling back to a given release.
|
||||
|
@ -35,7 +37,7 @@ type Rollback struct {
|
|||
|
||||
Version int
|
||||
Timeout time.Duration
|
||||
Wait bool
|
||||
WaitStrategy kube.WaitStrategy
|
||||
WaitForJobs bool
|
||||
DisableHooks bool
|
||||
DryRun bool
|
||||
|
@ -60,26 +62,26 @@ func (r *Rollback) Run(name string) error {
|
|||
|
||||
r.cfg.Releases.MaxHistory = r.MaxHistory
|
||||
|
||||
r.cfg.Log("preparing rollback of %s", name)
|
||||
slog.Debug("preparing rollback", "name", name)
|
||||
currentRelease, targetRelease, err := r.prepareRollback(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !r.DryRun {
|
||||
r.cfg.Log("creating rolled back release for %s", name)
|
||||
slog.Debug("creating rolled back release", "name", name)
|
||||
if err := r.cfg.Releases.Create(targetRelease); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
r.cfg.Log("performing rollback of %s", name)
|
||||
slog.Debug("performing rollback", "name", name)
|
||||
if _, err := r.performRollback(currentRelease, targetRelease); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !r.DryRun {
|
||||
r.cfg.Log("updating status for rolled back release for %s", name)
|
||||
slog.Debug("updating status for rolled back release", "name", name)
|
||||
if err := r.cfg.Releases.Update(targetRelease); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -126,7 +128,7 @@ func (r *Rollback) prepareRollback(name string) (*release.Release, *release.Rele
|
|||
return nil, nil, fmt.Errorf("release has no %d version", previousVersion)
|
||||
}
|
||||
|
||||
r.cfg.Log("rolling back %s (current: v%d, target: v%d)", name, currentRelease.Version, previousVersion)
|
||||
slog.Debug("rolling back", "name", name, "currentVersion", currentRelease.Version, "targetVersion", previousVersion)
|
||||
|
||||
previousRelease, err := r.cfg.Releases.Get(name, previousVersion)
|
||||
if err != nil {
|
||||
|
@ -159,7 +161,7 @@ func (r *Rollback) prepareRollback(name string) (*release.Release, *release.Rele
|
|||
|
||||
func (r *Rollback) performRollback(currentRelease, targetRelease *release.Release) (*release.Release, error) {
|
||||
if r.DryRun {
|
||||
r.cfg.Log("dry run for %s", targetRelease.Name)
|
||||
slog.Debug("dry run", "name", targetRelease.Name)
|
||||
return targetRelease, nil
|
||||
}
|
||||
|
||||
|
@ -174,11 +176,11 @@ func (r *Rollback) performRollback(currentRelease, targetRelease *release.Releas
|
|||
|
||||
// pre-rollback hooks
|
||||
if !r.DisableHooks {
|
||||
if err := r.cfg.execHook(targetRelease, release.HookPreRollback, r.Timeout); err != nil {
|
||||
if err := r.cfg.execHook(targetRelease, release.HookPreRollback, r.WaitStrategy, r.Timeout); err != nil {
|
||||
return targetRelease, err
|
||||
}
|
||||
} else {
|
||||
r.cfg.Log("rollback hooks disabled for %s", targetRelease.Name)
|
||||
slog.Debug("rollback hooks disabled", "name", targetRelease.Name)
|
||||
}
|
||||
|
||||
// It is safe to use "force" here because these are resources currently rendered by the chart.
|
||||
|
@ -190,21 +192,21 @@ func (r *Rollback) performRollback(currentRelease, targetRelease *release.Releas
|
|||
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("Rollback %q failed: %s", targetRelease.Name, err)
|
||||
r.cfg.Log("warning: %s", msg)
|
||||
slog.Warn(msg)
|
||||
currentRelease.Info.Status = release.StatusSuperseded
|
||||
targetRelease.Info.Status = release.StatusFailed
|
||||
targetRelease.Info.Description = msg
|
||||
r.cfg.recordRelease(currentRelease)
|
||||
r.cfg.recordRelease(targetRelease)
|
||||
if r.CleanupOnFail {
|
||||
r.cfg.Log("Cleanup on fail set, cleaning up %d resources", len(results.Created))
|
||||
slog.Debug("cleanup on fail set, cleaning up resources", "count", len(results.Created))
|
||||
_, errs := r.cfg.KubeClient.Delete(results.Created)
|
||||
if errs != nil {
|
||||
return targetRelease, fmt.Errorf(
|
||||
"an error occurred while cleaning up resources. original rollback error: %w",
|
||||
fmt.Errorf("unable to cleanup resources: %w", joinErrors(errs, ", ")))
|
||||
}
|
||||
r.cfg.Log("Resource cleanup complete")
|
||||
slog.Debug("resource cleanup complete")
|
||||
}
|
||||
return targetRelease, err
|
||||
}
|
||||
|
@ -215,31 +217,32 @@ func (r *Rollback) performRollback(currentRelease, targetRelease *release.Releas
|
|||
// levels, we should make these error level logs so users are notified
|
||||
// that they'll need to go do the cleanup on their own
|
||||
if err := recreate(r.cfg, results.Updated); err != nil {
|
||||
r.cfg.Log(err.Error())
|
||||
slog.Error(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if r.Wait {
|
||||
if r.WaitForJobs {
|
||||
if err := r.cfg.KubeClient.WaitWithJobs(target, r.Timeout); err != nil {
|
||||
targetRelease.SetStatus(release.StatusFailed, fmt.Sprintf("Release %q failed: %s", targetRelease.Name, err.Error()))
|
||||
r.cfg.recordRelease(currentRelease)
|
||||
r.cfg.recordRelease(targetRelease)
|
||||
return targetRelease, fmt.Errorf("release %s failed: %w", targetRelease.Name, err)
|
||||
}
|
||||
} else {
|
||||
if err := r.cfg.KubeClient.Wait(target, r.Timeout); err != nil {
|
||||
targetRelease.SetStatus(release.StatusFailed, fmt.Sprintf("Release %q failed: %s", targetRelease.Name, err.Error()))
|
||||
r.cfg.recordRelease(currentRelease)
|
||||
r.cfg.recordRelease(targetRelease)
|
||||
return targetRelease, fmt.Errorf("release %s failed: %w", targetRelease.Name, err)
|
||||
}
|
||||
waiter, err := r.cfg.KubeClient.GetWaiter(r.WaitStrategy)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to set metadata visitor from target release: %w", err)
|
||||
}
|
||||
if r.WaitForJobs {
|
||||
if err := waiter.WaitWithJobs(target, r.Timeout); err != nil {
|
||||
targetRelease.SetStatus(release.StatusFailed, fmt.Sprintf("Release %q failed: %s", targetRelease.Name, err.Error()))
|
||||
r.cfg.recordRelease(currentRelease)
|
||||
r.cfg.recordRelease(targetRelease)
|
||||
return targetRelease, fmt.Errorf("release %s failed: %w", targetRelease.Name, err)
|
||||
}
|
||||
} else {
|
||||
if err := waiter.Wait(target, r.Timeout); err != nil {
|
||||
targetRelease.SetStatus(release.StatusFailed, fmt.Sprintf("Release %q failed: %s", targetRelease.Name, err.Error()))
|
||||
r.cfg.recordRelease(currentRelease)
|
||||
r.cfg.recordRelease(targetRelease)
|
||||
return targetRelease, fmt.Errorf("release %s failed: %w", targetRelease.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
// post-rollback hooks
|
||||
if !r.DisableHooks {
|
||||
if err := r.cfg.execHook(targetRelease, release.HookPostRollback, r.Timeout); err != nil {
|
||||
if err := r.cfg.execHook(targetRelease, release.HookPostRollback, r.WaitStrategy, r.Timeout); err != nil {
|
||||
return targetRelease, err
|
||||
}
|
||||
}
|
||||
|
@ -250,7 +253,7 @@ func (r *Rollback) performRollback(currentRelease, targetRelease *release.Releas
|
|||
}
|
||||
// Supersede all previous deployments, see issue #2941.
|
||||
for _, rel := range deployed {
|
||||
r.cfg.Log("superseding previous deployment %d", rel.Version)
|
||||
slog.Debug("superseding previous deployment", "version", rel.Version)
|
||||
rel.Info.Status = release.StatusSuperseded
|
||||
r.cfg.recordRelease(rel)
|
||||
}
|
||||
|
|
|
@ -24,10 +24,10 @@ import (
|
|||
"k8s.io/cli-runtime/pkg/printers"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chart/loader"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/registry"
|
||||
chart "helm.sh/helm/v4/pkg/chart/v2"
|
||||
"helm.sh/helm/v4/pkg/chart/v2/loader"
|
||||
chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
|
||||
"helm.sh/helm/v4/pkg/registry"
|
||||
)
|
||||
|
||||
// ShowOutputFormat is the format of the output of `helm show`
|
||||
|
@ -64,27 +64,18 @@ type Show struct {
|
|||
}
|
||||
|
||||
// NewShow creates a new Show object with the given configuration.
|
||||
// Deprecated: Use NewShowWithConfig
|
||||
// TODO Helm 4: Fold NewShowWithConfig back into NewShow
|
||||
func NewShow(output ShowOutputFormat) *Show {
|
||||
return &Show{
|
||||
OutputFormat: output,
|
||||
}
|
||||
}
|
||||
|
||||
// NewShowWithConfig creates a new Show object with the given configuration.
|
||||
func NewShowWithConfig(output ShowOutputFormat, cfg *Configuration) *Show {
|
||||
func NewShow(output ShowOutputFormat, cfg *Configuration) *Show {
|
||||
sh := &Show{
|
||||
OutputFormat: output,
|
||||
}
|
||||
sh.ChartPathOptions.registryClient = cfg.RegistryClient
|
||||
sh.registryClient = cfg.RegistryClient
|
||||
|
||||
return sh
|
||||
}
|
||||
|
||||
// SetRegistryClient sets the registry client to use when pulling a chart from a registry.
|
||||
func (s *Show) SetRegistryClient(client *registry.Client) {
|
||||
s.ChartPathOptions.registryClient = client
|
||||
s.registryClient = client
|
||||
}
|
||||
|
||||
// Run executes 'helm show' against the given release.
|
||||
|
|
|
@ -19,12 +19,12 @@ package action
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
chart "helm.sh/helm/v4/pkg/chart/v2"
|
||||
)
|
||||
|
||||
func TestShow(t *testing.T) {
|
||||
config := actionConfigFixture(t)
|
||||
client := NewShowWithConfig(ShowAll, config)
|
||||
client := NewShow(ShowAll, config)
|
||||
client.chart = &chart.Chart{
|
||||
Metadata: &chart.Metadata{Name: "alpine"},
|
||||
Files: []*chart.File{
|
||||
|
@ -65,7 +65,8 @@ bar
|
|||
}
|
||||
|
||||
func TestShowNoValues(t *testing.T) {
|
||||
client := NewShow(ShowAll)
|
||||
config := actionConfigFixture(t)
|
||||
client := NewShow(ShowAll, config)
|
||||
client.chart = new(chart.Chart)
|
||||
|
||||
// Regression tests for missing values. See issue #1024.
|
||||
|
@ -81,7 +82,8 @@ func TestShowNoValues(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestShowValuesByJsonPathFormat(t *testing.T) {
|
||||
client := NewShow(ShowValues)
|
||||
config := actionConfigFixture(t)
|
||||
client := NewShow(ShowValues, config)
|
||||
client.JSONPathTemplate = "{$.nestedKey.simpleKey}"
|
||||
client.chart = buildChart(withSampleValues())
|
||||
output, err := client.Run("")
|
||||
|
@ -95,7 +97,8 @@ func TestShowValuesByJsonPathFormat(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestShowCRDs(t *testing.T) {
|
||||
client := NewShow(ShowCRDs)
|
||||
config := actionConfigFixture(t)
|
||||
client := NewShow(ShowCRDs, config)
|
||||
client.chart = &chart.Chart{
|
||||
Metadata: &chart.Metadata{Name: "alpine"},
|
||||
Files: []*chart.File{
|
||||
|
@ -123,7 +126,8 @@ bar
|
|||
}
|
||||
|
||||
func TestShowNoReadme(t *testing.T) {
|
||||
client := NewShow(ShowAll)
|
||||
config := actionConfigFixture(t)
|
||||
client := NewShow(ShowAll, config)
|
||||
client.chart = &chart.Chart{
|
||||
Metadata: &chart.Metadata{Name: "alpine"},
|
||||
Files: []*chart.File{
|
||||
|
|
|
@ -20,8 +20,8 @@ import (
|
|||
"bytes"
|
||||
"errors"
|
||||
|
||||
"helm.sh/helm/v3/pkg/kube"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"helm.sh/helm/v4/pkg/kube"
|
||||
release "helm.sh/helm/v4/pkg/release/v1"
|
||||
)
|
||||
|
||||
// Status is the action for checking the deployment status of releases.
|
||||
|
@ -32,15 +32,6 @@ type Status struct {
|
|||
|
||||
Version int
|
||||
|
||||
// If true, display description to output format,
|
||||
// only affect print type table.
|
||||
// TODO Helm 4: Remove this flag and output the description by default.
|
||||
ShowDescription bool
|
||||
|
||||
// ShowResources sets if the resources should be retrieved with the status.
|
||||
// TODO Helm 4: Remove this flag and output the resources by default.
|
||||
ShowResources bool
|
||||
|
||||
// ShowResourcesTable is used with ShowResources. When true this will cause
|
||||
// the resulting objects to be retrieved as a kind=table.
|
||||
ShowResourcesTable bool
|
||||
|
@ -59,10 +50,6 @@ func (s *Status) Run(name string) (*release.Release, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if !s.ShowResources {
|
||||
return s.cfg.releaseContent(name, s.Version)
|
||||
}
|
||||
|
||||
rel, err := s.cfg.releaseContent(name, s.Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -18,16 +18,17 @@ package action
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/kube"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"helm.sh/helm/v3/pkg/releaseutil"
|
||||
helmtime "helm.sh/helm/v3/pkg/time"
|
||||
chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
|
||||
"helm.sh/helm/v4/pkg/kube"
|
||||
releaseutil "helm.sh/helm/v4/pkg/release/util"
|
||||
release "helm.sh/helm/v4/pkg/release/v1"
|
||||
helmtime "helm.sh/helm/v4/pkg/time"
|
||||
)
|
||||
|
||||
// Uninstall is the action for uninstalling releases.
|
||||
|
@ -40,7 +41,7 @@ type Uninstall struct {
|
|||
DryRun bool
|
||||
IgnoreNotFound bool
|
||||
KeepHistory bool
|
||||
Wait bool
|
||||
WaitStrategy kube.WaitStrategy
|
||||
DeletionPropagation string
|
||||
Timeout time.Duration
|
||||
Description string
|
||||
|
@ -59,6 +60,11 @@ func (u *Uninstall) Run(name string) (*release.UninstallReleaseResponse, error)
|
|||
return nil, err
|
||||
}
|
||||
|
||||
waiter, err := u.cfg.KubeClient.GetWaiter(u.WaitStrategy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if u.DryRun {
|
||||
// In the dry run case, just see if the release exists
|
||||
r, err := u.cfg.releaseContent(name, 0)
|
||||
|
@ -98,29 +104,29 @@ func (u *Uninstall) Run(name string) (*release.UninstallReleaseResponse, error)
|
|||
return nil, fmt.Errorf("the release named %q is already deleted", name)
|
||||
}
|
||||
|
||||
u.cfg.Log("uninstall: Deleting %s", name)
|
||||
slog.Debug("uninstall: deleting release", "name", name)
|
||||
rel.Info.Status = release.StatusUninstalling
|
||||
rel.Info.Deleted = helmtime.Now()
|
||||
rel.Info.Description = "Deletion in progress (or silently failed)"
|
||||
res := &release.UninstallReleaseResponse{Release: rel}
|
||||
|
||||
if !u.DisableHooks {
|
||||
if err := u.cfg.execHook(rel, release.HookPreDelete, u.Timeout); err != nil {
|
||||
if err := u.cfg.execHook(rel, release.HookPreDelete, u.WaitStrategy, u.Timeout); err != nil {
|
||||
return res, err
|
||||
}
|
||||
} else {
|
||||
u.cfg.Log("delete hooks disabled for %s", name)
|
||||
slog.Debug("delete hooks disabled", "release", name)
|
||||
}
|
||||
|
||||
// From here on out, the release is currently considered to be in StatusUninstalling
|
||||
// state.
|
||||
if err := u.cfg.Releases.Update(rel); err != nil {
|
||||
u.cfg.Log("uninstall: Failed to store updated release: %s", err)
|
||||
slog.Debug("uninstall: Failed to store updated release", slog.Any("error", err))
|
||||
}
|
||||
|
||||
deletedResources, kept, errs := u.deleteRelease(rel)
|
||||
if errs != nil {
|
||||
u.cfg.Log("uninstall: Failed to delete release: %s", errs)
|
||||
slog.Debug("uninstall: Failed to delete release", slog.Any("error", errs))
|
||||
return nil, fmt.Errorf("failed to delete release: %s", name)
|
||||
}
|
||||
|
||||
|
@ -129,16 +135,12 @@ func (u *Uninstall) Run(name string) (*release.UninstallReleaseResponse, error)
|
|||
}
|
||||
res.Info = kept
|
||||
|
||||
if u.Wait {
|
||||
if kubeClient, ok := u.cfg.KubeClient.(kube.InterfaceExt); ok {
|
||||
if err := kubeClient.WaitForDelete(deletedResources, u.Timeout); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
if err := waiter.WaitForDelete(deletedResources, u.Timeout); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
if !u.DisableHooks {
|
||||
if err := u.cfg.execHook(rel, release.HookPostDelete, u.Timeout); err != nil {
|
||||
if err := u.cfg.execHook(rel, release.HookPostDelete, u.WaitStrategy, u.Timeout); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
@ -151,7 +153,7 @@ func (u *Uninstall) Run(name string) (*release.UninstallReleaseResponse, error)
|
|||
}
|
||||
|
||||
if !u.KeepHistory {
|
||||
u.cfg.Log("purge requested for %s", name)
|
||||
slog.Debug("purge requested", "release", name)
|
||||
err := u.purgeReleases(rels...)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("uninstall: Failed to purge the release: %w", err))
|
||||
|
@ -166,7 +168,7 @@ func (u *Uninstall) Run(name string) (*release.UninstallReleaseResponse, error)
|
|||
}
|
||||
|
||||
if err := u.cfg.Releases.Update(rel); err != nil {
|
||||
u.cfg.Log("uninstall: Failed to store updated release: %s", err)
|
||||
slog.Debug("uninstall: Failed to store updated release", slog.Any("error", err))
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
|
@ -239,7 +241,7 @@ func (u *Uninstall) deleteRelease(rel *release.Release) (kube.ResourceList, stri
|
|||
}
|
||||
if len(resources) > 0 {
|
||||
if kubeClient, ok := u.cfg.KubeClient.(kube.InterfaceDeletionPropagation); ok {
|
||||
_, errs = kubeClient.DeleteWithPropagationPolicy(resources, parseCascadingFlag(u.cfg, u.DeletionPropagation))
|
||||
_, errs = kubeClient.DeleteWithPropagationPolicy(resources, parseCascadingFlag(u.DeletionPropagation))
|
||||
return resources, kept, errs
|
||||
}
|
||||
_, errs = u.cfg.KubeClient.Delete(resources)
|
||||
|
@ -247,7 +249,7 @@ func (u *Uninstall) deleteRelease(rel *release.Release) (kube.ResourceList, stri
|
|||
return resources, kept, errs
|
||||
}
|
||||
|
||||
func parseCascadingFlag(cfg *Configuration, cascadingFlag string) v1.DeletionPropagation {
|
||||
func parseCascadingFlag(cascadingFlag string) v1.DeletionPropagation {
|
||||
switch cascadingFlag {
|
||||
case "orphan":
|
||||
return v1.DeletePropagationOrphan
|
||||
|
@ -256,7 +258,7 @@ func parseCascadingFlag(cfg *Configuration, cascadingFlag string) v1.DeletionPro
|
|||
case "background":
|
||||
return v1.DeletePropagationBackground
|
||||
default:
|
||||
cfg.Log("uninstall: given cascade value: %s, defaulting to delete propagation background", cascadingFlag)
|
||||
slog.Debug("uninstall: given cascade value, defaulting to delete propagation background", "value", cascadingFlag)
|
||||
return v1.DeletePropagationBackground
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,8 +22,9 @@ import (
|
|||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
kubefake "helm.sh/helm/v3/pkg/kube/fake"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"helm.sh/helm/v4/pkg/kube"
|
||||
kubefake "helm.sh/helm/v4/pkg/kube/fake"
|
||||
release "helm.sh/helm/v4/pkg/release/v1"
|
||||
)
|
||||
|
||||
func uninstallAction(t *testing.T) *Uninstall {
|
||||
|
@ -82,7 +83,7 @@ func TestUninstallRelease_Wait(t *testing.T) {
|
|||
unAction := uninstallAction(t)
|
||||
unAction.DisableHooks = true
|
||||
unAction.DryRun = false
|
||||
unAction.Wait = true
|
||||
unAction.WaitStrategy = kube.StatusWatcherStrategy
|
||||
|
||||
rel := releaseStub()
|
||||
rel.Name = "come-fail-away"
|
||||
|
@ -99,7 +100,7 @@ func TestUninstallRelease_Wait(t *testing.T) {
|
|||
}`
|
||||
unAction.cfg.Releases.Create(rel)
|
||||
failer := unAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
|
||||
failer.WaitError = fmt.Errorf("U timed out")
|
||||
failer.WaitForDeleteError = fmt.Errorf("U timed out")
|
||||
unAction.cfg.KubeClient = failer
|
||||
res, err := unAction.Run(rel.Name)
|
||||
is.Error(err)
|
||||
|
@ -113,7 +114,7 @@ func TestUninstallRelease_Cascade(t *testing.T) {
|
|||
unAction := uninstallAction(t)
|
||||
unAction.DisableHooks = true
|
||||
unAction.DryRun = false
|
||||
unAction.Wait = false
|
||||
unAction.WaitStrategy = kube.HookOnlyStrategy
|
||||
unAction.DeletionPropagation = "foreground"
|
||||
|
||||
rel := releaseStub()
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
@ -28,14 +29,14 @@ import (
|
|||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/cli-runtime/pkg/resource"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/kube"
|
||||
"helm.sh/helm/v3/pkg/postrender"
|
||||
"helm.sh/helm/v3/pkg/registry"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"helm.sh/helm/v3/pkg/releaseutil"
|
||||
"helm.sh/helm/v3/pkg/storage/driver"
|
||||
chart "helm.sh/helm/v4/pkg/chart/v2"
|
||||
chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
|
||||
"helm.sh/helm/v4/pkg/kube"
|
||||
"helm.sh/helm/v4/pkg/postrender"
|
||||
"helm.sh/helm/v4/pkg/registry"
|
||||
releaseutil "helm.sh/helm/v4/pkg/release/util"
|
||||
release "helm.sh/helm/v4/pkg/release/v1"
|
||||
"helm.sh/helm/v4/pkg/storage/driver"
|
||||
)
|
||||
|
||||
// Upgrade is the action for upgrading releases.
|
||||
|
@ -64,8 +65,8 @@ type Upgrade struct {
|
|||
SkipCRDs bool
|
||||
// Timeout is the timeout for this operation
|
||||
Timeout time.Duration
|
||||
// Wait determines whether the wait operation should be performed after the upgrade is requested.
|
||||
Wait bool
|
||||
// WaitStrategy determines what type of waiting should be done
|
||||
WaitStrategy kube.WaitStrategy
|
||||
// WaitForJobs determines whether the wait operation for the Jobs should be performed after the upgrade is requested.
|
||||
WaitForJobs bool
|
||||
// DisableHooks disables hook processing if set to true.
|
||||
|
@ -104,7 +105,7 @@ type Upgrade struct {
|
|||
// Description is the description of this operation
|
||||
Description string
|
||||
Labels map[string]string
|
||||
// PostRender is an optional post-renderer
|
||||
// PostRenderer is an optional post-renderer
|
||||
//
|
||||
// If this is non-nil, then after templates are rendered, they will be sent to the
|
||||
// post renderer before sending to the Kubernetes API server.
|
||||
|
@ -131,14 +132,14 @@ func NewUpgrade(cfg *Configuration) *Upgrade {
|
|||
up := &Upgrade{
|
||||
cfg: cfg,
|
||||
}
|
||||
up.ChartPathOptions.registryClient = cfg.RegistryClient
|
||||
up.registryClient = cfg.RegistryClient
|
||||
|
||||
return up
|
||||
}
|
||||
|
||||
// SetRegistryClient sets the registry client to use when fetching charts.
|
||||
func (u *Upgrade) SetRegistryClient(client *registry.Client) {
|
||||
u.ChartPathOptions.registryClient = client
|
||||
u.registryClient = client
|
||||
}
|
||||
|
||||
// Run executes the upgrade on the given release.
|
||||
|
@ -155,13 +156,15 @@ func (u *Upgrade) RunWithContext(ctx context.Context, name string, chart *chart.
|
|||
|
||||
// Make sure if Atomic is set, that wait is set as well. This makes it so
|
||||
// the user doesn't have to specify both
|
||||
u.Wait = u.Wait || u.Atomic
|
||||
if u.WaitStrategy == kube.HookOnlyStrategy && u.Atomic {
|
||||
u.WaitStrategy = kube.StatusWatcherStrategy
|
||||
}
|
||||
|
||||
if err := chartutil.ValidateReleaseName(name); err != nil {
|
||||
return nil, fmt.Errorf("release name is invalid: %s", name)
|
||||
}
|
||||
|
||||
u.cfg.Log("preparing upgrade for %s", name)
|
||||
slog.Debug("preparing upgrade", "name", name)
|
||||
currentRelease, upgradedRelease, err := u.prepareUpgrade(name, chart, vals)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -169,7 +172,7 @@ func (u *Upgrade) RunWithContext(ctx context.Context, name string, chart *chart.
|
|||
|
||||
u.cfg.Releases.MaxHistory = u.MaxHistory
|
||||
|
||||
u.cfg.Log("performing update for %s", name)
|
||||
slog.Debug("performing update", "name", name)
|
||||
res, err := u.performUpgrade(ctx, currentRelease, upgradedRelease)
|
||||
if err != nil {
|
||||
return res, err
|
||||
|
@ -177,7 +180,7 @@ func (u *Upgrade) RunWithContext(ctx context.Context, name string, chart *chart.
|
|||
|
||||
// Do not update for dry runs
|
||||
if !u.isDryRun() {
|
||||
u.cfg.Log("updating status for upgraded release for %s", name)
|
||||
slog.Debug("updating status for upgraded release", "name", name)
|
||||
if err := u.cfg.Releases.Update(upgradedRelease); err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
@ -243,7 +246,7 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[strin
|
|||
return nil, nil, err
|
||||
}
|
||||
|
||||
if err := chartutil.ProcessDependenciesWithMerge(chart, vals); err != nil {
|
||||
if err := chartutil.ProcessDependencies(chart, vals); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
|
@ -363,7 +366,7 @@ func (u *Upgrade) performUpgrade(ctx context.Context, originalRelease, upgradedR
|
|||
|
||||
// Run if it is a dry run
|
||||
if u.isDryRun() {
|
||||
u.cfg.Log("dry run for %s", upgradedRelease.Name)
|
||||
slog.Debug("dry run for release", "name", upgradedRelease.Name)
|
||||
if len(u.Description) > 0 {
|
||||
upgradedRelease.Info.Description = u.Description
|
||||
} else {
|
||||
|
@ -372,7 +375,7 @@ func (u *Upgrade) performUpgrade(ctx context.Context, originalRelease, upgradedR
|
|||
return upgradedRelease, nil
|
||||
}
|
||||
|
||||
u.cfg.Log("creating upgraded release for %s", upgradedRelease.Name)
|
||||
slog.Debug("creating upgraded release", "name", upgradedRelease.Name)
|
||||
if err := u.cfg.Releases.Create(upgradedRelease); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -418,12 +421,12 @@ func (u *Upgrade) releasingUpgrade(c chan<- resultMessage, upgradedRelease *rele
|
|||
// pre-upgrade hooks
|
||||
|
||||
if !u.DisableHooks {
|
||||
if err := u.cfg.execHook(upgradedRelease, release.HookPreUpgrade, u.Timeout); err != nil {
|
||||
if err := u.cfg.execHook(upgradedRelease, release.HookPreUpgrade, u.WaitStrategy, u.Timeout); err != nil {
|
||||
u.reportToPerformUpgrade(c, upgradedRelease, kube.ResourceList{}, fmt.Errorf("pre-upgrade hooks failed: %s", err))
|
||||
return
|
||||
}
|
||||
} else {
|
||||
u.cfg.Log("upgrade hooks disabled for %s", upgradedRelease.Name)
|
||||
slog.Debug("upgrade hooks disabled", "name", upgradedRelease.Name)
|
||||
}
|
||||
|
||||
results, err := u.cfg.KubeClient.Update(current, target, u.Force)
|
||||
|
@ -439,32 +442,32 @@ func (u *Upgrade) releasingUpgrade(c chan<- resultMessage, upgradedRelease *rele
|
|||
// levels, we should make these error level logs so users are notified
|
||||
// that they'll need to go do the cleanup on their own
|
||||
if err := recreate(u.cfg, results.Updated); err != nil {
|
||||
u.cfg.Log(err.Error())
|
||||
slog.Error(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if u.Wait {
|
||||
u.cfg.Log(
|
||||
"waiting for release %s resources (created: %d updated: %d deleted: %d)",
|
||||
upgradedRelease.Name, len(results.Created), len(results.Updated), len(results.Deleted))
|
||||
if u.WaitForJobs {
|
||||
if err := u.cfg.KubeClient.WaitWithJobs(target, u.Timeout); err != nil {
|
||||
u.cfg.recordRelease(originalRelease)
|
||||
u.reportToPerformUpgrade(c, upgradedRelease, results.Created, err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if err := u.cfg.KubeClient.Wait(target, u.Timeout); err != nil {
|
||||
u.cfg.recordRelease(originalRelease)
|
||||
u.reportToPerformUpgrade(c, upgradedRelease, results.Created, err)
|
||||
return
|
||||
}
|
||||
waiter, err := u.cfg.KubeClient.GetWaiter(u.WaitStrategy)
|
||||
if err != nil {
|
||||
u.cfg.recordRelease(originalRelease)
|
||||
u.reportToPerformUpgrade(c, upgradedRelease, results.Created, err)
|
||||
return
|
||||
}
|
||||
if u.WaitForJobs {
|
||||
if err := waiter.WaitWithJobs(target, u.Timeout); err != nil {
|
||||
u.cfg.recordRelease(originalRelease)
|
||||
u.reportToPerformUpgrade(c, upgradedRelease, results.Created, err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if err := waiter.Wait(target, u.Timeout); err != nil {
|
||||
u.cfg.recordRelease(originalRelease)
|
||||
u.reportToPerformUpgrade(c, upgradedRelease, results.Created, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// post-upgrade hooks
|
||||
if !u.DisableHooks {
|
||||
if err := u.cfg.execHook(upgradedRelease, release.HookPostUpgrade, u.Timeout); err != nil {
|
||||
if err := u.cfg.execHook(upgradedRelease, release.HookPostUpgrade, u.WaitStrategy, u.Timeout); err != nil {
|
||||
u.reportToPerformUpgrade(c, upgradedRelease, results.Created, fmt.Errorf("post-upgrade hooks failed: %s", err))
|
||||
return
|
||||
}
|
||||
|
@ -484,13 +487,13 @@ func (u *Upgrade) releasingUpgrade(c chan<- resultMessage, upgradedRelease *rele
|
|||
|
||||
func (u *Upgrade) failRelease(rel *release.Release, created kube.ResourceList, err error) (*release.Release, error) {
|
||||
msg := fmt.Sprintf("Upgrade %q failed: %s", rel.Name, err)
|
||||
u.cfg.Log("warning: %s", msg)
|
||||
slog.Warn("upgrade failed", "name", rel.Name, slog.Any("error", err))
|
||||
|
||||
rel.Info.Status = release.StatusFailed
|
||||
rel.Info.Description = msg
|
||||
u.cfg.recordRelease(rel)
|
||||
if u.CleanupOnFail && len(created) > 0 {
|
||||
u.cfg.Log("Cleanup on fail set, cleaning up %d resources", len(created))
|
||||
slog.Debug("cleanup on fail set", "cleaning_resources", len(created))
|
||||
_, errs := u.cfg.KubeClient.Delete(created)
|
||||
if errs != nil {
|
||||
return rel, fmt.Errorf(
|
||||
|
@ -502,10 +505,10 @@ func (u *Upgrade) failRelease(rel *release.Release, created kube.ResourceList, e
|
|||
),
|
||||
)
|
||||
}
|
||||
u.cfg.Log("Resource cleanup complete")
|
||||
slog.Debug("resource cleanup complete")
|
||||
}
|
||||
if u.Atomic {
|
||||
u.cfg.Log("Upgrade failed and atomic is set, rolling back to last successful release")
|
||||
slog.Debug("upgrade failed and atomic is set, rolling back to last successful release")
|
||||
|
||||
// As a protection, get the last successful release before rollback.
|
||||
// If there are no successful releases, bail out
|
||||
|
@ -529,7 +532,9 @@ func (u *Upgrade) failRelease(rel *release.Release, created kube.ResourceList, e
|
|||
|
||||
rollin := NewRollback(u.cfg)
|
||||
rollin.Version = filteredHistory[0].Version
|
||||
rollin.Wait = true
|
||||
if u.WaitStrategy == kube.HookOnlyStrategy {
|
||||
rollin.WaitStrategy = kube.StatusWatcherStrategy
|
||||
}
|
||||
rollin.WaitForJobs = u.WaitForJobs
|
||||
rollin.DisableHooks = u.DisableHooks
|
||||
rollin.Recreate = u.Recreate
|
||||
|
@ -555,13 +560,13 @@ func (u *Upgrade) failRelease(rel *release.Release, created kube.ResourceList, e
|
|||
func (u *Upgrade) reuseValues(chart *chart.Chart, current *release.Release, newVals map[string]interface{}) (map[string]interface{}, error) {
|
||||
if u.ResetValues {
|
||||
// If ResetValues is set, we completely ignore current.Config.
|
||||
u.cfg.Log("resetting values to the chart's original version")
|
||||
slog.Debug("resetting values to the chart's original version")
|
||||
return newVals, nil
|
||||
}
|
||||
|
||||
// If the ReuseValues flag is set, we always copy the old values over the new config's values.
|
||||
if u.ReuseValues {
|
||||
u.cfg.Log("reusing the old release's values")
|
||||
slog.Debug("reusing the old release's values")
|
||||
|
||||
// We have to regenerate the old coalesced values:
|
||||
oldVals, err := chartutil.CoalesceValues(current.Chart, current.Config)
|
||||
|
@ -578,7 +583,7 @@ func (u *Upgrade) reuseValues(chart *chart.Chart, current *release.Release, newV
|
|||
|
||||
// If the ResetThenReuseValues flag is set, we use the new chart's values, but we copy the old config's values over the new config's values.
|
||||
if u.ResetThenReuseValues {
|
||||
u.cfg.Log("merging values from old release to new values")
|
||||
slog.Debug("merging values from old release to new values")
|
||||
|
||||
newVals = chartutil.CoalesceTables(newVals, current.Config)
|
||||
|
||||
|
@ -586,7 +591,7 @@ func (u *Upgrade) reuseValues(chart *chart.Chart, current *release.Release, newV
|
|||
}
|
||||
|
||||
if len(newVals) == 0 && len(current.Config) > 0 {
|
||||
u.cfg.Log("copying values from %s (v%d) to new release.", current.Name, current.Version)
|
||||
slog.Debug("copying values from old release", "name", current.Name, "version", current.Version)
|
||||
newVals = current.Config
|
||||
}
|
||||
return newVals, nil
|
||||
|
|
|
@ -23,15 +23,16 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/storage/driver"
|
||||
chart "helm.sh/helm/v4/pkg/chart/v2"
|
||||
"helm.sh/helm/v4/pkg/kube"
|
||||
"helm.sh/helm/v4/pkg/storage/driver"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
kubefake "helm.sh/helm/v3/pkg/kube/fake"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
helmtime "helm.sh/helm/v3/pkg/time"
|
||||
kubefake "helm.sh/helm/v4/pkg/kube/fake"
|
||||
release "helm.sh/helm/v4/pkg/release/v1"
|
||||
helmtime "helm.sh/helm/v4/pkg/time"
|
||||
)
|
||||
|
||||
func upgradeAction(t *testing.T) *Upgrade {
|
||||
|
@ -52,7 +53,7 @@ func TestUpgradeRelease_Success(t *testing.T) {
|
|||
rel.Info.Status = release.StatusDeployed
|
||||
req.NoError(upAction.cfg.Releases.Create(rel))
|
||||
|
||||
upAction.Wait = true
|
||||
upAction.WaitStrategy = kube.StatusWatcherStrategy
|
||||
vals := map[string]interface{}{}
|
||||
|
||||
ctx, done := context.WithCancel(context.Background())
|
||||
|
@ -82,7 +83,7 @@ func TestUpgradeRelease_Wait(t *testing.T) {
|
|||
failer := upAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
|
||||
failer.WaitError = fmt.Errorf("I timed out")
|
||||
upAction.cfg.KubeClient = failer
|
||||
upAction.Wait = true
|
||||
upAction.WaitStrategy = kube.StatusWatcherStrategy
|
||||
vals := map[string]interface{}{}
|
||||
|
||||
res, err := upAction.Run(rel.Name, buildChart(), vals)
|
||||
|
@ -104,7 +105,7 @@ func TestUpgradeRelease_WaitForJobs(t *testing.T) {
|
|||
failer := upAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
|
||||
failer.WaitError = fmt.Errorf("I timed out")
|
||||
upAction.cfg.KubeClient = failer
|
||||
upAction.Wait = true
|
||||
upAction.WaitStrategy = kube.StatusWatcherStrategy
|
||||
upAction.WaitForJobs = true
|
||||
vals := map[string]interface{}{}
|
||||
|
||||
|
@ -128,7 +129,7 @@ func TestUpgradeRelease_CleanupOnFail(t *testing.T) {
|
|||
failer.WaitError = fmt.Errorf("I timed out")
|
||||
failer.DeleteError = fmt.Errorf("I tried to delete nil")
|
||||
upAction.cfg.KubeClient = failer
|
||||
upAction.Wait = true
|
||||
upAction.WaitStrategy = kube.StatusWatcherStrategy
|
||||
upAction.CleanupOnFail = true
|
||||
vals := map[string]interface{}{}
|
||||
|
||||
|
@ -395,7 +396,7 @@ func TestUpgradeRelease_Interrupted_Wait(t *testing.T) {
|
|||
failer := upAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
|
||||
failer.WaitDuration = 10 * time.Second
|
||||
upAction.cfg.KubeClient = failer
|
||||
upAction.Wait = true
|
||||
upAction.WaitStrategy = kube.StatusWatcherStrategy
|
||||
vals := map[string]interface{}{}
|
||||
|
||||
ctx := context.Background()
|
||||
|
|
|
@ -24,7 +24,7 @@ import (
|
|||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/cli-runtime/pkg/resource"
|
||||
|
||||
"helm.sh/helm/v3/pkg/kube"
|
||||
"helm.sh/helm/v4/pkg/kube"
|
||||
)
|
||||
|
||||
var accessor = meta.NewAccessor()
|
||||
|
|
|
@ -22,7 +22,7 @@ import (
|
|||
"net/http"
|
||||
"testing"
|
||||
|
||||
"helm.sh/helm/v3/pkg/kube"
|
||||
"helm.sh/helm/v4/pkg/kube"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"helm.sh/helm/v3/pkg/downloader"
|
||||
"helm.sh/helm/v4/pkg/downloader"
|
||||
)
|
||||
|
||||
// Verify is the action for building a given chart's Verify tree.
|
||||
|
|
|
@ -13,7 +13,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package chart
|
||||
package v2
|
||||
|
||||
import (
|
||||
"path/filepath"
|
|
@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package chart
|
||||
package v2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
|
@ -13,7 +13,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package chart
|
||||
package v2
|
||||
|
||||
import "time"
|
||||
|
||||
|
@ -25,28 +25,28 @@ type Dependency struct {
|
|||
// Name is the name of the dependency.
|
||||
//
|
||||
// This must mach the name in the dependency's Chart.yaml.
|
||||
Name string `json:"name"`
|
||||
Name string `json:"name" yaml:"name"`
|
||||
// Version is the version (range) of this chart.
|
||||
//
|
||||
// A lock file will always produce a single version, while a dependency
|
||||
// may contain a semantic version range.
|
||||
Version string `json:"version,omitempty"`
|
||||
Version string `json:"version,omitempty" yaml:"version,omitempty"`
|
||||
// The URL to the repository.
|
||||
//
|
||||
// Appending `index.yaml` to this string should result in a URL that can be
|
||||
// used to fetch the repository index.
|
||||
Repository string `json:"repository"`
|
||||
Repository string `json:"repository" yaml:"repository"`
|
||||
// A yaml path that resolves to a boolean, used for enabling/disabling charts (e.g. subchart1.enabled )
|
||||
Condition string `json:"condition,omitempty"`
|
||||
Condition string `json:"condition,omitempty" yaml:"condition,omitempty"`
|
||||
// Tags can be used to group charts for enabling/disabling together
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"`
|
||||
// Enabled bool determines if chart should be loaded
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
Enabled bool `json:"enabled,omitempty" yaml:"enabled,omitempty"`
|
||||
// ImportValues holds the mapping of source values to parent key to be imported. Each item can be a
|
||||
// string or pair of child/parent sublist items.
|
||||
ImportValues []interface{} `json:"import-values,omitempty"`
|
||||
ImportValues []interface{} `json:"import-values,omitempty" yaml:"import-values,omitempty"`
|
||||
// Alias usable alias to be used for the chart
|
||||
Alias string `json:"alias,omitempty"`
|
||||
Alias string `json:"alias,omitempty" yaml:"alias,omitempty"`
|
||||
}
|
||||
|
||||
// Validate checks for common problems with the dependency datastructure in
|
|
@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package chart
|
||||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
|
@ -1,11 +1,10 @@
|
|||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
@ -14,17 +13,11 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package kube // import "helm.sh/helm/v3/pkg/kube"
|
||||
/*
|
||||
Package v2 provides chart handling for apiVersion v1 and v2 charts
|
||||
|
||||
import "k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
|
||||
// GetConfig returns a Kubernetes client config.
|
||||
//
|
||||
// Deprecated
|
||||
func GetConfig(kubeconfig, context, namespace string) *genericclioptions.ConfigFlags {
|
||||
cf := genericclioptions.NewConfigFlags(true)
|
||||
cf.Namespace = &namespace
|
||||
cf.Context = &context
|
||||
cf.KubeConfig = &kubeconfig
|
||||
return cf
|
||||
}
|
||||
This package and its sub-packages provide handling for apiVersion v1 and v2 charts.
|
||||
The changes from v1 to v2 charts are minor and were able to be handled with minor
|
||||
switches based on characteristics.
|
||||
*/
|
||||
package v2
|
|
@ -13,7 +13,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package chart
|
||||
package v2
|
||||
|
||||
import "fmt"
|
||||
|
|
@ -13,7 +13,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package chart
|
||||
package v2
|
||||
|
||||
// File represents a file as a name/value pair.
|
||||
//
|
|
@ -13,7 +13,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package chart
|
||||
package v2
|
||||
|
||||
import (
|
||||
"testing"
|
|
@ -29,9 +29,18 @@ import (
|
|||
"regexp"
|
||||
"strings"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
chart "helm.sh/helm/v4/pkg/chart/v2"
|
||||
)
|
||||
|
||||
// MaxDecompressedChartSize is the maximum size of a chart archive that will be
|
||||
// decompressed. This is the decompressed size of all the files.
|
||||
// The default value is 100 MiB.
|
||||
var MaxDecompressedChartSize int64 = 100 * 1024 * 1024 // Default 100 MiB
|
||||
|
||||
// MaxDecompressedFileSize is the size of the largest file that Helm will attempt to load.
|
||||
// The size of the file is the decompressed version of it when it is stored in an archive.
|
||||
var MaxDecompressedFileSize int64 = 5 * 1024 * 1024 // Default 5 MiB
|
||||
|
||||
var drivePathPattern = regexp.MustCompile(`^[a-zA-Z]:/`)
|
||||
|
||||
// FileLoader loads a chart from a file
|
||||
|
@ -118,6 +127,7 @@ func LoadArchiveFiles(in io.Reader) ([]*BufferedFile, error) {
|
|||
|
||||
files := []*BufferedFile{}
|
||||
tr := tar.NewReader(unzipped)
|
||||
remainingSize := MaxDecompressedChartSize
|
||||
for {
|
||||
b := bytes.NewBuffer(nil)
|
||||
hd, err := tr.Next()
|
||||
|
@ -177,10 +187,30 @@ func LoadArchiveFiles(in io.Reader) ([]*BufferedFile, error) {
|
|||
return nil, errors.New("chart yaml not in base directory")
|
||||
}
|
||||
|
||||
if _, err := io.Copy(b, tr); err != nil {
|
||||
if hd.Size > remainingSize {
|
||||
return nil, fmt.Errorf("decompressed chart is larger than the maximum size %d", MaxDecompressedChartSize)
|
||||
}
|
||||
|
||||
if hd.Size > MaxDecompressedFileSize {
|
||||
return nil, fmt.Errorf("decompressed chart file %q is larger than the maximum file size %d", hd.Name, MaxDecompressedFileSize)
|
||||
}
|
||||
|
||||
limitedReader := io.LimitReader(tr, remainingSize)
|
||||
|
||||
bytesWritten, err := io.Copy(b, limitedReader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
remainingSize -= bytesWritten
|
||||
// When the bytesWritten are less than the file size it means the limit reader ended
|
||||
// copying early. Here we report that error. This is important if the last file extracted
|
||||
// is the one that goes over the limit. It assumes the Size stored in the tar header
|
||||
// is correct, something many applications do.
|
||||
if bytesWritten < hd.Size || remainingSize <= 0 {
|
||||
return nil, fmt.Errorf("decompressed chart is larger than the maximum size %d", MaxDecompressedChartSize)
|
||||
}
|
||||
|
||||
data := bytes.TrimPrefix(b.Bytes(), utf8bom)
|
||||
|
||||
files = append(files, &BufferedFile{Name: n, Data: data})
|
|
@ -23,9 +23,9 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"helm.sh/helm/v3/internal/sympath"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/ignore"
|
||||
"helm.sh/helm/v4/internal/sympath"
|
||||
chart "helm.sh/helm/v4/pkg/chart/v2"
|
||||
"helm.sh/helm/v4/pkg/ignore"
|
||||
)
|
||||
|
||||
var utf8bom = []byte{0xEF, 0xBB, 0xBF}
|
||||
|
@ -99,6 +99,10 @@ func LoadDir(dir string) (*chart.Chart, error) {
|
|||
return fmt.Errorf("cannot load irregular file %s as it has file mode type bits set", name)
|
||||
}
|
||||
|
||||
if fi.Size() > MaxDecompressedFileSize {
|
||||
return fmt.Errorf("chart file %q is larger than the maximum file size %d", fi.Name(), MaxDecompressedFileSize)
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading %s: %w", n, err)
|
|
@ -17,17 +17,21 @@ limitations under the License.
|
|||
package loader
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
utilyaml "k8s.io/apimachinery/pkg/util/yaml"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
chart "helm.sh/helm/v4/pkg/chart/v2"
|
||||
)
|
||||
|
||||
// ChartLoader loads a chart.
|
||||
|
@ -103,10 +107,11 @@ func LoadFiles(files []*BufferedFile) (*chart.Chart, error) {
|
|||
return c, fmt.Errorf("cannot load Chart.lock: %w", err)
|
||||
}
|
||||
case f.Name == "values.yaml":
|
||||
c.Values = make(map[string]interface{})
|
||||
if err := yaml.Unmarshal(f.Data, &c.Values); err != nil {
|
||||
values, err := LoadValues(bytes.NewReader(f.Data))
|
||||
if err != nil {
|
||||
return c, fmt.Errorf("cannot load values.yaml: %w", err)
|
||||
}
|
||||
c.Values = values
|
||||
case f.Name == "values.schema.json":
|
||||
c.Schema = f.Data
|
||||
|
||||
|
@ -201,3 +206,51 @@ func LoadFiles(files []*BufferedFile) (*chart.Chart, error) {
|
|||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// LoadValues loads values from a reader.
|
||||
//
|
||||
// The reader is expected to contain one or more YAML documents, the values of which are merged.
|
||||
// And the values can be either a chart's default values or a user-supplied values.
|
||||
func LoadValues(data io.Reader) (map[string]interface{}, error) {
|
||||
values := map[string]interface{}{}
|
||||
reader := utilyaml.NewYAMLReader(bufio.NewReader(data))
|
||||
for {
|
||||
currentMap := map[string]interface{}{}
|
||||
raw, err := reader.Read()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return nil, fmt.Errorf("error reading yaml document: %w", err)
|
||||
}
|
||||
if err := yaml.Unmarshal(raw, ¤tMap, func(d *json.Decoder) *json.Decoder {
|
||||
d.UseNumber()
|
||||
return d
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("cannot unmarshal yaml document: %w", err)
|
||||
}
|
||||
values = MergeMaps(values, currentMap)
|
||||
}
|
||||
return values, nil
|
||||
}
|
||||
|
||||
// MergeMaps merges two maps. If a key exists in both maps, the value from b will be used.
|
||||
// If the value is a map, the maps will be merged recursively.
|
||||
func MergeMaps(a, b map[string]interface{}) map[string]interface{} {
|
||||
out := make(map[string]interface{}, len(a))
|
||||
for k, v := range a {
|
||||
out[k] = v
|
||||
}
|
||||
for k, v := range b {
|
||||
if v, ok := v.(map[string]interface{}); ok {
|
||||
if bv, ok := out[k]; ok {
|
||||
if bv, ok := bv.(map[string]interface{}); ok {
|
||||
out[k] = MergeMaps(bv, v)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
out[k] = v
|
||||
}
|
||||
return out
|
||||
}
|
|
@ -24,12 +24,13 @@ import (
|
|||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
chart "helm.sh/helm/v4/pkg/chart/v2"
|
||||
)
|
||||
|
||||
func TestLoadDir(t *testing.T) {
|
||||
|
@ -488,6 +489,115 @@ func TestLoadInvalidArchive(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestLoadValues(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
data []byte
|
||||
expctedValues map[string]interface{}
|
||||
}{
|
||||
"It should load values correctly": {
|
||||
data: []byte(`
|
||||
foo:
|
||||
image: foo:v1
|
||||
bar:
|
||||
version: v2
|
||||
`),
|
||||
expctedValues: map[string]interface{}{
|
||||
"foo": map[string]interface{}{
|
||||
"image": "foo:v1",
|
||||
},
|
||||
"bar": map[string]interface{}{
|
||||
"version": "v2",
|
||||
},
|
||||
},
|
||||
},
|
||||
"It should load values correctly with multiple documents in one file": {
|
||||
data: []byte(`
|
||||
foo:
|
||||
image: foo:v1
|
||||
bar:
|
||||
version: v2
|
||||
---
|
||||
foo:
|
||||
image: foo:v2
|
||||
`),
|
||||
expctedValues: map[string]interface{}{
|
||||
"foo": map[string]interface{}{
|
||||
"image": "foo:v2",
|
||||
},
|
||||
"bar": map[string]interface{}{
|
||||
"version": "v2",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for testName, testCase := range testCases {
|
||||
t.Run(testName, func(tt *testing.T) {
|
||||
values, err := LoadValues(bytes.NewReader(testCase.data))
|
||||
if err != nil {
|
||||
tt.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(values, testCase.expctedValues) {
|
||||
tt.Errorf("Expected values: %v, got %v", testCase.expctedValues, values)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeValues(t *testing.T) {
|
||||
nestedMap := map[string]interface{}{
|
||||
"foo": "bar",
|
||||
"baz": map[string]string{
|
||||
"cool": "stuff",
|
||||
},
|
||||
}
|
||||
anotherNestedMap := map[string]interface{}{
|
||||
"foo": "bar",
|
||||
"baz": map[string]string{
|
||||
"cool": "things",
|
||||
"awesome": "stuff",
|
||||
},
|
||||
}
|
||||
flatMap := map[string]interface{}{
|
||||
"foo": "bar",
|
||||
"baz": "stuff",
|
||||
}
|
||||
anotherFlatMap := map[string]interface{}{
|
||||
"testing": "fun",
|
||||
}
|
||||
|
||||
testMap := MergeMaps(flatMap, nestedMap)
|
||||
equal := reflect.DeepEqual(testMap, nestedMap)
|
||||
if !equal {
|
||||
t.Errorf("Expected a nested map to overwrite a flat value. Expected: %v, got %v", nestedMap, testMap)
|
||||
}
|
||||
|
||||
testMap = MergeMaps(nestedMap, flatMap)
|
||||
equal = reflect.DeepEqual(testMap, flatMap)
|
||||
if !equal {
|
||||
t.Errorf("Expected a flat value to overwrite a map. Expected: %v, got %v", flatMap, testMap)
|
||||
}
|
||||
|
||||
testMap = MergeMaps(nestedMap, anotherNestedMap)
|
||||
equal = reflect.DeepEqual(testMap, anotherNestedMap)
|
||||
if !equal {
|
||||
t.Errorf("Expected a nested map to overwrite another nested map. Expected: %v, got %v", anotherNestedMap, testMap)
|
||||
}
|
||||
|
||||
testMap = MergeMaps(anotherFlatMap, anotherNestedMap)
|
||||
expectedMap := map[string]interface{}{
|
||||
"testing": "fun",
|
||||
"foo": "bar",
|
||||
"baz": map[string]string{
|
||||
"cool": "things",
|
||||
"awesome": "stuff",
|
||||
},
|
||||
}
|
||||
equal = reflect.DeepEqual(testMap, expectedMap)
|
||||
if !equal {
|
||||
t.Errorf("Expected a map with different keys to merge properly with another map. Expected: %v, got %v", expectedMap, testMap)
|
||||
}
|
||||
}
|
||||
|
||||
func verifyChart(t *testing.T, c *chart.Chart) {
|
||||
t.Helper()
|
||||
if c.Name() == "" {
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue