Merge branch 'main' of github.com:helm/helm into feature/errname_linter

pull/31647/head
Mads Jensen 2 weeks ago
commit 314ee6db11

@ -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@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: SARIF file
path: results.sarif

@ -33,6 +33,7 @@ linters:
- unused
- usestdlibvars
- usetesting
- exhaustive
exclusions:
@ -74,6 +75,9 @@ linters:
recommendations:
- github.com/evanphx/json-patch/v5
exhaustive:
default-signifies-exhaustive: true
run:
timeout: 10m

@ -58,20 +58,6 @@ 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
K8S_MODULES_VER=$(subst ., ,$(subst v,,$(shell go list -f '{{.Version}}' -m k8s.io/client-go)))
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/v4/pkg/chart/v2/lint/rules.k8sVersionMajor=$(K8S_MODULES_MAJOR_VER)
LDFLAGS += -X helm.sh/helm/v4/pkg/chart/v2/lint/rules.k8sVersionMinor=$(K8S_MODULES_MINOR_VER)
LDFLAGS += -X helm.sh/helm/v4/pkg/internal/v3/lint/rules.k8sVersionMajor=$(K8S_MODULES_MAJOR_VER)
LDFLAGS += -X helm.sh/helm/v4/pkg/internal/v3/lint/rules.k8sVersionMinor=$(K8S_MODULES_MINOR_VER)
LDFLAGS += -X helm.sh/helm/v4/pkg/chart/common.k8sVersionMajor=$(K8S_MODULES_MAJOR_VER)
LDFLAGS += -X helm.sh/helm/v4/pkg/chart/common.k8sVersionMinor=$(K8S_MODULES_MINOR_VER)
LDFLAGS += -X helm.sh/helm/v4/internal/version.kubeClientVersionMajor=$(K8S_MODULES_MAJOR_VER)
LDFLAGS += -X helm.sh/helm/v4/internal/version.kubeClientVersionMinor=$(K8S_MODULES_MINOR_VER)
.PHONY: all
all: build

@ -1,4 +1,5 @@
maintainers:
- banjoh
- gjenkins8
- joejulian
- marckhouzam
@ -9,7 +10,6 @@ maintainers:
- technosophos
- TerryHowe
triage:
- banjoh
- yxxhero
- zonggen
- z4ce

@ -1,10 +1,10 @@
module helm.sh/helm/v4
go 1.24.0
go 1.25.0
require (
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24
github.com/BurntSushi/toml v1.5.0
github.com/BurntSushi/toml v1.6.0
github.com/DATA-DOG/go-sqlmock v1.5.2
github.com/Masterminds/semver/v3 v3.4.0
github.com/Masterminds/sprig/v3 v3.3.0
@ -17,7 +17,7 @@ require (
github.com/evanphx/json-patch/v5 v5.9.11
github.com/extism/go-sdk v1.7.1
github.com/fatih/color v1.18.0
github.com/fluxcd/cli-utils v0.36.0-flux.14
github.com/fluxcd/cli-utils v0.37.0-flux.1
github.com/foxcpp/go-mockdns v1.1.0
github.com/gobwas/glob v0.2.3
github.com/gofrs/flock v0.13.0
@ -33,20 +33,20 @@ require (
github.com/spf13/cobra v1.10.2
github.com/spf13/pflag v1.0.10
github.com/stretchr/testify v1.11.1
github.com/tetratelabs/wazero v1.10.1
github.com/tetratelabs/wazero v1.11.0
go.yaml.in/yaml/v3 v3.0.4
golang.org/x/crypto v0.46.0
golang.org/x/term v0.38.0
golang.org/x/text v0.32.0
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/api v0.34.3
k8s.io/apiextensions-apiserver v0.34.3
k8s.io/apimachinery v0.34.3
k8s.io/apiserver v0.34.3
k8s.io/cli-runtime v0.34.3
k8s.io/client-go v0.34.3
k8s.io/api v0.35.0
k8s.io/apiextensions-apiserver v0.35.0
k8s.io/apimachinery v0.35.0
k8s.io/apiserver v0.35.0
k8s.io/cli-runtime v0.35.0
k8s.io/client-go v0.35.0
k8s.io/klog/v2 v2.130.1
k8s.io/kubectl v0.34.3
k8s.io/kubectl v0.35.0
oras.land/oras-go/v2 v2.6.0
sigs.k8s.io/controller-runtime v0.22.4
sigs.k8s.io/kustomize/kyaml v0.21.0
@ -85,14 +85,12 @@ require (
github.com/go-openapi/jsonpointer v0.21.1 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/swag v0.23.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/gnostic-models v0.7.0 // indirect
github.com/google/go-cmp v0.7.0 // 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.4-0.20250319132907-e064f32e3674 // indirect
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
github.com/hashicorp/golang-lru/arc/v2 v2.0.5 // indirect
@ -114,19 +112,16 @@ require (
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/spdystream v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // 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.37.0 // indirect
github.com/onsi/gomega v1.38.2 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.22.0 // indirect
github.com/prometheus/client_golang v1.23.2 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.65.0 // indirect
github.com/prometheus/common v0.66.1 // indirect
github.com/prometheus/procfs v0.17.0 // 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
@ -141,7 +136,7 @@ require (
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.58.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
go.opentelemetry.io/otel v1.37.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
@ -156,12 +151,12 @@ require (
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.37.0 // indirect
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
go.opentelemetry.io/otel/sdk v1.36.0 // indirect
go.opentelemetry.io/otel/sdk/log v0.8.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.34.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect
go.opentelemetry.io/otel/trace v1.37.0 // indirect
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
golang.org/x/mod v0.30.0 // indirect
golang.org/x/net v0.47.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
@ -170,16 +165,16 @@ require (
golang.org/x/time v0.12.0 // indirect
golang.org/x/tools v0.39.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb // indirect
google.golang.org/grpc v1.72.1 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect
google.golang.org/grpc v1.72.2 // indirect
google.golang.org/protobuf v1.36.8 // indirect
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/component-base v0.34.3 // indirect
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
k8s.io/component-base v0.35.0 // indirect
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
sigs.k8s.io/kustomize/api v0.20.1 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect

138
go.sum

@ -6,8 +6,8 @@ github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
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/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
github.com/BurntSushi/toml v1.6.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=
@ -26,8 +26,6 @@ github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBi
github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
@ -93,8 +91,8 @@ github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
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.14 h1:I//AMVUXTc+M04UtIXArMXQZCazGMwfemodV1j/yG8c=
github.com/fluxcd/cli-utils v0.36.0-flux.14/go.mod h1:uDo7BYOfbdmk/asnHuI0IQPl6u0FCgcN54AHDu3Y5As=
github.com/fluxcd/cli-utils v0.37.0-flux.1 h1:k/VvPNT3tGa/l2N+qzHduaQr3GVbgoWS6nw7tGZz16w=
github.com/fluxcd/cli-utils v0.37.0-flux.1/go.mod h1:aND5wX3LuTFtB7eUT7vsWr8mmxRVSPR2Wkvbn0SqPfw=
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=
@ -132,8 +130,6 @@ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x
github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw=
github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0=
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/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=
@ -155,8 +151,6 @@ 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.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY=
github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
@ -182,8 +176,6 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/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.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
@ -225,8 +217,6 @@ 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/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=
@ -242,12 +232,10 @@ github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/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.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=
github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8=
github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y=
github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns=
github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=
github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A=
github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k=
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.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
@ -255,8 +243,6 @@ github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgr
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/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@ -265,16 +251,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.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
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.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
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.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
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=
@ -321,14 +307,12 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834 h1:ZF+QBjOI+tILZjBaFj3HgFonKXUcwgJ4djLb6i42S3Q=
github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834/go.mod h1:m9ymHTgNSEjuxvw8E7WWe4Pl4hZQHXONY8wE6dMLaRk=
github.com/tetratelabs/wazero v1.10.1 h1:2DugeJf6VVk58KTPszlNfeeN8AhhpwcZqkJj2wwFuH8=
github.com/tetratelabs/wazero v1.10.1/go.mod h1:DRm5twOQ5Gr1AoEdSi0CLjDQF1J9ZAuyqFIjl1KKfQU=
github.com/tetratelabs/wazero v1.11.0 h1:+gKemEuKCTevU4d7ZTzlsvgd1uaToIDtlQlmNbwqYhA=
github.com/tetratelabs/wazero v1.11.0/go.mod h1:eV28rsN8Q+xwjogd7f4/Pp4xFxO7uOGbLcD/LzB1wiU=
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/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.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
@ -336,8 +320,8 @@ go.opentelemetry.io/contrib/bridges/prometheus v0.57.0 h1:UW0+QyeyBVhn+COBec3nGh
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.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 h1:WzNab7hOOLzdDF/EoWCt4glhrbMPVMOO5JYTmpz36Ls=
@ -366,40 +350,34 @@ go.opentelemetry.io/otel/log v0.8.0 h1:egZ8vV5atrUWUbnSsHn6vB8R21G2wrKqNiDt3iWer
go.opentelemetry.io/otel/log v0.8.0/go.mod h1:M9qvDdUTRCopJcGRKg57+JSQ9LgLBrwwfC32epk5NX8=
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
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.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
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=
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.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.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
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=
@ -407,11 +385,8 @@ golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
@ -426,8 +401,6 @@ golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKl
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
@ -438,9 +411,7 @@ golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -478,8 +449,6 @@ golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
@ -487,23 +456,20 @@ golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
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/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950=
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb h1:TLPQVbx1GJ8VKZxz52VAxl1EBgKXXbTiU9Fc5fZeLn4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA=
google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.72.2 h1:TdbGzwb82ty4OusHWepvFWGLgIbNo1/SUynEN0ssqv8=
google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo=
gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
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=
@ -512,34 +478,34 @@ 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=
k8s.io/api v0.34.3 h1:D12sTP257/jSH2vHV2EDYrb16bS7ULlHpdNdNhEw2S4=
k8s.io/api v0.34.3/go.mod h1:PyVQBF886Q5RSQZOim7DybQjAbVs8g7gwJNhGtY5MBk=
k8s.io/apiextensions-apiserver v0.34.3 h1:p10fGlkDY09eWKOTeUSioxwLukJnm+KuDZdrW71y40g=
k8s.io/apiextensions-apiserver v0.34.3/go.mod h1:aujxvqGFRdb/cmXYfcRTeppN7S2XV/t7WMEc64zB5A0=
k8s.io/apimachinery v0.34.3 h1:/TB+SFEiQvN9HPldtlWOTp0hWbJ+fjU+wkxysf/aQnE=
k8s.io/apimachinery v0.34.3/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
k8s.io/apiserver v0.34.3 h1:uGH1qpDvSiYG4HVFqc6A3L4CKiX+aBWDrrsxHYK0Bdo=
k8s.io/apiserver v0.34.3/go.mod h1:QPnnahMO5C2m3lm6fPW3+JmyQbvHZQ8uudAu/493P2w=
k8s.io/cli-runtime v0.34.3 h1:YRyMhiwX0dT9lmG0AtZDaeG33Nkxgt9OlCTZhRXj9SI=
k8s.io/cli-runtime v0.34.3/go.mod h1:GVwL1L5uaGEgM7eGeKjaTG2j3u134JgG4dAI6jQKhMc=
k8s.io/client-go v0.34.3 h1:wtYtpzy/OPNYf7WyNBTj3iUA0XaBHVqhv4Iv3tbrF5A=
k8s.io/client-go v0.34.3/go.mod h1:OxxeYagaP9Kdf78UrKLa3YZixMCfP6bgPwPwNBQBzpM=
k8s.io/component-base v0.34.3 h1:zsEgw6ELqK0XncCQomgO9DpUIzlrYuZYA0Cgo+JWpVk=
k8s.io/component-base v0.34.3/go.mod h1:5iIlD8wPfWE/xSHTRfbjuvUul2WZbI2nOUK65XL0E/c=
k8s.io/api v0.35.0 h1:iBAU5LTyBI9vw3L5glmat1njFK34srdLmktWwLTprlY=
k8s.io/api v0.35.0/go.mod h1:AQ0SNTzm4ZAczM03QH42c7l3bih1TbAXYo0DkF8ktnA=
k8s.io/apiextensions-apiserver v0.35.0 h1:3xHk2rTOdWXXJM+RDQZJvdx0yEOgC0FgQ1PlJatA5T4=
k8s.io/apiextensions-apiserver v0.35.0/go.mod h1:E1Ahk9SADaLQ4qtzYFkwUqusXTcaV2uw3l14aqpL2LU=
k8s.io/apimachinery v0.35.0 h1:Z2L3IHvPVv/MJ7xRxHEtk6GoJElaAqDCCU0S6ncYok8=
k8s.io/apimachinery v0.35.0/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns=
k8s.io/apiserver v0.35.0 h1:CUGo5o+7hW9GcAEF3x3usT3fX4f9r8xmgQeCBDaOgX4=
k8s.io/apiserver v0.35.0/go.mod h1:QUy1U4+PrzbJaM3XGu2tQ7U9A4udRRo5cyxkFX0GEds=
k8s.io/cli-runtime v0.35.0 h1:PEJtYS/Zr4p20PfZSLCbY6YvaoLrfByd6THQzPworUE=
k8s.io/cli-runtime v0.35.0/go.mod h1:VBRvHzosVAoVdP3XwUQn1Oqkvaa8facnokNkD7jOTMY=
k8s.io/client-go v0.35.0 h1:IAW0ifFbfQQwQmga0UdoH0yvdqrbwMdq9vIFEhRpxBE=
k8s.io/client-go v0.35.0/go.mod h1:q2E5AAyqcbeLGPdoRB+Nxe3KYTfPce1Dnu1myQdqz9o=
k8s.io/component-base v0.35.0 h1:+yBrOhzri2S1BVqyVSvcM3PtPyx5GUxCK2tinZz1G94=
k8s.io/component-base v0.35.0/go.mod h1:85SCX4UCa6SCFt6p3IKAPej7jSnF3L8EbfSyMZayJR0=
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-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA=
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=
k8s.io/kubectl v0.34.3 h1:vpM6//153gh5gvsYHXWHVJ4l4xmN5QFwTSmlfd8icm8=
k8s.io/kubectl v0.34.3/go.mod h1:zZQHtIZoUqTP1bAnPzq/3W1jfc0NeOeunFgcswrfg1c=
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE=
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ=
k8s.io/kubectl v0.35.0 h1:cL/wJKHDe8E8+rP3G7avnymcMg6bH6JEcR5w5uo06wc=
k8s.io/kubectl v0.35.0/go.mod h1:VR5/TSkYyxZwrRwY5I5dDq6l5KXmiCb+9w8IKplk3Qo=
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck=
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
oras.land/oras-go/v2 v2.6.0 h1:X4ELRsiGkrbeox69+9tzTu492FMUu7zJQW6eJU+I2oc=
oras.land/oras-go/v2 v2.6.0/go.mod h1:magiQDfG6H1O9APp+rOsvCPcW1GD2MM7vgnKY0Y+u1o=
sigs.k8s.io/controller-runtime v0.22.4 h1:GEjV7KV3TY8e+tJ2LCTxUTanW4z/FmNB7l327UfMq9A=
sigs.k8s.io/controller-runtime v0.22.4/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8=
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/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
sigs.k8s.io/kustomize/api v0.20.1 h1:iWP1Ydh3/lmldBnH/S5RXgT98vWYMaTUL1ADcr+Sv7I=
sigs.k8s.io/kustomize/api v0.20.1/go.mod h1:t6hUFxO+Ph0VxIk1sKp1WS0dOjbPCtLJ4p8aADLwqjM=
sigs.k8s.io/kustomize/kyaml v0.21.0 h1:7mQAf3dUwf0wBerWJd8rXhVcnkk5Tvn/q91cGkaP6HQ=

@ -175,16 +175,6 @@ func TestHelmCreateChart(t *testing.T) {
//
// Resources like hpa and ingress, which are disabled by default in values.yaml are enabled here using the equivalent
// of the `--set` flag.
//
// Note: This test requires the following ldflags to be set per the current Kubernetes version to avoid false-positive
// results.
// 1. -X helm.sh/helm/v4/pkg/lint/rules.k8sVersionMajor=<k8s-major-version>
// 2. -X helm.sh/helm/v4/pkg/lint/rules.k8sVersionMinor=<k8s-minor-version>
// or directly use '$(LDFLAGS)' in Makefile.
//
// When run without ldflags, the test passes giving a false-positive result. This is because the variables
// `k8sVersionMajor` and `k8sVersionMinor` by default are set to an older version of Kubernetes, with which, there
// might not be the deprecation warning.
func TestHelmCreateChart_CheckDeprecatedWarnings(t *testing.T) {
createdChart, err := chartutil.Create("checkdeprecatedwarnings", t.TempDir())
if err != nil {

@ -70,7 +70,7 @@ func Crds(linter *support.Linter) {
var yamlStruct *k8sYamlStruct
err := decoder.Decode(&yamlStruct)
if err == io.EOF {
if errors.Is(err, io.EOF) {
break
}

@ -28,15 +28,7 @@ import (
kscheme "k8s.io/client-go/kubernetes/scheme"
)
var (
// This should be set in the Makefile based on the version of client-go being imported.
// These constants will be overwritten with LDFLAGS. The version components must be
// strings in order for LDFLAGS to set them.
k8sVersionMajor = "1"
k8sVersionMinor = "20"
)
// deprecatedAPIError indicates that an API is deprecated in Kubernetes
// deprecatedAPIError indicates than an API is deprecated in Kubernetes
type deprecatedAPIError struct {
Deprecated string
Message string
@ -56,33 +48,29 @@ func validateNoDeprecations(resource *k8sYamlStruct, kubeVersion *common.KubeVer
return nil
}
majorVersion := k8sVersionMajor
minorVersion := k8sVersionMinor
if kubeVersion != nil {
majorVersion = kubeVersion.Major
minorVersion = kubeVersion.Minor
if kubeVersion == nil {
kubeVersion = &common.DefaultCapabilities.KubeVersion
}
runtimeObject, err := resourceToRuntimeObject(resource)
kubeVersionMajor, err := strconv.Atoi(kubeVersion.Major)
if err != nil {
// do not error for non-kubernetes resources
if runtime.IsNotRegisteredError(err) {
return nil
}
return err
}
major, err := strconv.Atoi(majorVersion)
kubeVersionMinor, err := strconv.Atoi(kubeVersion.Minor)
if err != nil {
return err
}
minor, err := strconv.Atoi(minorVersion)
runtimeObject, err := resourceToRuntimeObject(resource)
if err != nil {
// do not error for non-kubernetes resources
if runtime.IsNotRegisteredError(err) {
return nil
}
return err
}
if !deprecation.IsDeprecated(runtimeObject, major, minor) {
if !deprecation.IsDeprecated(runtimeObject, kubeVersionMajor, kubeVersionMinor) {
return nil
}
gvk := fmt.Sprintf("%s %s", resource.APIVersion, resource.Kind)

@ -150,7 +150,7 @@ func TemplatesWithSkipSchemaValidation(linter *support.Linter, values map[string
var yamlStruct *k8sYamlStruct
err := decoder.Decode(&yamlStruct)
if err == io.EOF {
if errors.Is(err, io.EOF) {
break
}

@ -56,7 +56,7 @@ func LoadFile(name string) (*chart.Chart, error) {
c, err := LoadArchive(raw)
if err != nil {
if err == gzip.ErrHeader {
if errors.Is(err, gzip.ErrHeader) {
return nil, fmt.Errorf("file '%s' does not appear to be a valid chart file (details: %s)", name, err)
}
}

@ -189,7 +189,7 @@ func LoadValues(data io.Reader) (map[string]interface{}, error) {
currentMap := map[string]interface{}{}
raw, err := reader.Read()
if err != nil {
if err == io.EOF {
if errors.Is(err, io.EOF) {
break
}
return nil, fmt.Errorf("error reading yaml document: %w", err)

@ -20,6 +20,7 @@ import (
"archive/tar"
"bytes"
"compress/gzip"
"errors"
"io"
"log"
"os"
@ -116,7 +117,7 @@ func TestBomTestData(t *testing.T) {
tr := tar.NewReader(unzipped)
for {
file, err := tr.Next()
if err == io.EOF {
if errors.Is(err, io.EOF) {
break
}
if err != nil {

@ -21,6 +21,7 @@ import (
"bytes"
"compress/gzip"
"crypto/sha256"
"errors"
"fmt"
"io"
"os"
@ -201,7 +202,7 @@ func retrieveAllHeadersFromTar(path string) ([]*tar.Header, error) {
headers := []*tar.Header{}
for {
hd, err := tr.Next()
if err == io.EOF {
if errors.Is(err, io.EOF) {
break
}

@ -36,6 +36,9 @@ type DebugCheckHandler struct {
// Enabled implements slog.Handler.Enabled
func (h *DebugCheckHandler) Enabled(_ context.Context, level slog.Level) bool {
if level == slog.LevelDebug {
if h.debugEnabled == nil {
return false
}
return h.debugEnabled()
}
return true // Always log other levels

@ -18,8 +18,10 @@ package logging
import (
"bytes"
"context"
"log/slog"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
@ -113,3 +115,259 @@ func TestLogHolder_InterfaceCompliance(t *testing.T) {
assert.Equal(t, handler, logger.Handler())
})
}
func TestDebugCheckHandler_Enabled(t *testing.T) {
t.Run("returns debugEnabled function result for debug level", func(t *testing.T) {
// Test with debug enabled
debugEnabled := func() bool { return true }
buf := &bytes.Buffer{}
baseHandler := slog.NewTextHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug})
handler := &DebugCheckHandler{
handler: baseHandler,
debugEnabled: debugEnabled,
}
assert.True(t, handler.Enabled(t.Context(), slog.LevelDebug))
})
t.Run("returns false for debug level when debug disabled", func(t *testing.T) {
// Test with debug disabled
debugEnabled := func() bool { return false }
buf := &bytes.Buffer{}
baseHandler := slog.NewTextHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug})
handler := &DebugCheckHandler{
handler: baseHandler,
debugEnabled: debugEnabled,
}
assert.False(t, handler.Enabled(t.Context(), slog.LevelDebug))
})
t.Run("always returns true for non-debug levels", func(t *testing.T) {
debugEnabled := func() bool { return false } // Debug disabled
buf := &bytes.Buffer{}
baseHandler := slog.NewTextHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug})
handler := &DebugCheckHandler{
handler: baseHandler,
debugEnabled: debugEnabled,
}
// Even with debug disabled, other levels should always be enabled
assert.True(t, handler.Enabled(t.Context(), slog.LevelInfo))
assert.True(t, handler.Enabled(t.Context(), slog.LevelWarn))
assert.True(t, handler.Enabled(t.Context(), slog.LevelError))
})
t.Run("calls debugEnabled function dynamically", func(t *testing.T) {
callCount := 0
debugEnabled := func() bool {
callCount++
return callCount%2 == 1 // Alternates between true and false
}
buf := &bytes.Buffer{}
baseHandler := slog.NewTextHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug})
handler := &DebugCheckHandler{
handler: baseHandler,
debugEnabled: debugEnabled,
}
// First call should return true
assert.True(t, handler.Enabled(t.Context(), slog.LevelDebug))
assert.Equal(t, 1, callCount)
// Second call should return false
assert.False(t, handler.Enabled(t.Context(), slog.LevelDebug))
assert.Equal(t, 2, callCount)
// Third call should return true again
assert.True(t, handler.Enabled(t.Context(), slog.LevelDebug))
assert.Equal(t, 3, callCount)
})
}
func TestDebugCheckHandler_Handle(t *testing.T) {
t.Run("delegates to underlying handler", func(t *testing.T) {
buf := &bytes.Buffer{}
baseHandler := slog.NewTextHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug})
handler := &DebugCheckHandler{
handler: baseHandler,
debugEnabled: func() bool { return true },
}
record := slog.NewRecord(time.Now(), slog.LevelInfo, "test message", 0)
err := handler.Handle(t.Context(), record)
assert.NoError(t, err)
assert.Contains(t, buf.String(), "test message")
})
t.Run("handles context correctly", func(t *testing.T) {
buf := &bytes.Buffer{}
baseHandler := slog.NewTextHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug})
handler := &DebugCheckHandler{
handler: baseHandler,
debugEnabled: func() bool { return true },
}
type testKey string
ctx := context.WithValue(t.Context(), testKey("test"), "value")
record := slog.NewRecord(time.Now(), slog.LevelInfo, "context test", 0)
err := handler.Handle(ctx, record)
assert.NoError(t, err)
assert.Contains(t, buf.String(), "context test")
})
}
func TestDebugCheckHandler_WithAttrs(t *testing.T) {
t.Run("returns new DebugCheckHandler with attributes", func(t *testing.T) {
logger := NewLogger(func() bool { return true })
handler := logger.Handler()
newHandler := handler.WithAttrs([]slog.Attr{
slog.String("key1", "value1"),
slog.Int("key2", 42),
})
// Should return a DebugCheckHandler
debugHandler, ok := newHandler.(*DebugCheckHandler)
assert.True(t, ok)
assert.NotNil(t, debugHandler)
// Should preserve the debugEnabled function
assert.True(t, debugHandler.Enabled(t.Context(), slog.LevelDebug))
// Should have the attributes applied to the underlying handler
assert.NotEqual(t, handler, debugHandler.handler)
})
t.Run("preserves debugEnabled function", func(t *testing.T) {
callCount := 0
debugEnabled := func() bool {
callCount++
return callCount%2 == 1
}
buf := &bytes.Buffer{}
baseHandler := slog.NewTextHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug})
handler := &DebugCheckHandler{
handler: baseHandler,
debugEnabled: debugEnabled,
}
attrs := []slog.Attr{slog.String("test", "value")}
newHandler := handler.WithAttrs(attrs)
// The new handler should use the same debugEnabled function
assert.True(t, newHandler.Enabled(t.Context(), slog.LevelDebug))
assert.Equal(t, 1, callCount)
assert.False(t, newHandler.Enabled(t.Context(), slog.LevelDebug))
assert.Equal(t, 2, callCount)
})
}
func TestDebugCheckHandler_WithGroup(t *testing.T) {
t.Run("returns new DebugCheckHandler with group", func(t *testing.T) {
buf := &bytes.Buffer{}
baseHandler := slog.NewTextHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug})
handler := &DebugCheckHandler{
handler: baseHandler,
debugEnabled: func() bool { return true },
}
newHandler := handler.WithGroup("testgroup")
// Should return a DebugCheckHandler
debugHandler, ok := newHandler.(*DebugCheckHandler)
assert.True(t, ok)
assert.NotNil(t, debugHandler)
// Should preserve the debugEnabled function
assert.True(t, debugHandler.Enabled(t.Context(), slog.LevelDebug))
// Should have the group applied to the underlying handler
assert.NotEqual(t, handler.handler, debugHandler.handler)
})
t.Run("preserves debugEnabled function", func(t *testing.T) {
callCount := 0
debugEnabled := func() bool {
callCount++
return callCount%2 == 1
}
buf := &bytes.Buffer{}
baseHandler := slog.NewTextHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug})
handler := &DebugCheckHandler{
handler: baseHandler,
debugEnabled: debugEnabled,
}
newHandler := handler.WithGroup("testgroup")
// The new handler should use the same debugEnabled function
assert.True(t, newHandler.Enabled(t.Context(), slog.LevelDebug))
assert.Equal(t, 1, callCount)
assert.False(t, newHandler.Enabled(t.Context(), slog.LevelDebug))
assert.Equal(t, 2, callCount)
})
}
func TestDebugCheckHandler_Integration(t *testing.T) {
t.Run("works with NewLogger function", func(t *testing.T) {
debugEnabled := func() bool { return true }
logger := NewLogger(debugEnabled)
assert.NotNil(t, logger)
// The logger should have a DebugCheckHandler
handler := logger.Handler()
debugHandler, ok := handler.(*DebugCheckHandler)
assert.True(t, ok)
// Should enable debug when debugEnabled returns true
assert.True(t, debugHandler.Enabled(t.Context(), slog.LevelDebug))
// Should enable other levels regardless
assert.True(t, debugHandler.Enabled(t.Context(), slog.LevelInfo))
})
t.Run("dynamic debug checking works in practice", func(t *testing.T) {
debugState := false
debugEnabled := func() bool { return debugState }
logger := NewLogger(debugEnabled)
// Initially debug should be disabled
assert.False(t, logger.Handler().(*DebugCheckHandler).Enabled(t.Context(), slog.LevelDebug))
// Enable debug
debugState = true
assert.True(t, logger.Handler().(*DebugCheckHandler).Enabled(t.Context(), slog.LevelDebug))
// Disable debug again
debugState = false
assert.False(t, logger.Handler().(*DebugCheckHandler).Enabled(t.Context(), slog.LevelDebug))
})
t.Run("handles nil debugEnabled function", func(t *testing.T) {
logger := NewLogger(nil)
assert.NotNil(t, logger)
// The logger should have a DebugCheckHandler
handler := logger.Handler()
debugHandler, ok := handler.(*DebugCheckHandler)
assert.True(t, ok)
// When debugEnabled is nil, debug level should be disabled (default behavior)
assert.False(t, debugHandler.Enabled(t.Context(), slog.LevelDebug))
// Other levels should always be enabled
assert.True(t, debugHandler.Enabled(t.Context(), slog.LevelInfo))
assert.True(t, debugHandler.Enabled(t.Context(), slog.LevelWarn))
assert.True(t, debugHandler.Enabled(t.Context(), slog.LevelError))
})
}

@ -23,14 +23,13 @@ import (
"go.yaml.in/yaml/v3"
)
// Config represents an plugin type specific configuration
// It is expected to type assert (cast) the a Config to its expected underlying type (schema.ConfigCLIV1, schema.ConfigGetterV1, etc).
// Config represents a plugin type specific configuration
// It is expected to type assert (cast) the Config to its expected underlying type (schema.ConfigCLIV1, schema.ConfigGetterV1, etc).
type Config interface {
Validate() error
}
func unmarshaConfig(pluginType string, configData map[string]any) (Config, error) {
func unmarshalConfig(pluginType string, configData map[string]any) (Config, error) {
pluginTypeMeta, ok := pluginTypesIndex[pluginType]
if !ok {
return nil, fmt.Errorf("unknown plugin type %q", pluginType)

@ -27,7 +27,7 @@ import (
func TestUnmarshaConfig(t *testing.T) {
// Test unmarshalling a CLI plugin config
{
config, err := unmarshaConfig("cli/v1", map[string]any{
config, err := unmarshalConfig("cli/v1", map[string]any{
"usage": "usage string",
"shortHelp": "short help string",
"longHelp": "long help string",
@ -46,7 +46,7 @@ func TestUnmarshaConfig(t *testing.T) {
// Test unmarshalling invalid config data
{
config, err := unmarshaConfig("cli/v1", map[string]any{
config, err := unmarshalConfig("cli/v1", map[string]any{
"invalid field": "foo",
})
require.Error(t, err)

@ -140,7 +140,7 @@ func (g *TarGzExtractor) Extract(buffer *bytes.Buffer, targetDir string) error {
tarReader := tar.NewReader(uncompressedStream)
for {
header, err := tarReader.Next()
if err == io.EOF {
if errors.Is(err, io.EOF) {
break
}
if err != nil {

@ -19,6 +19,7 @@ import (
"archive/tar"
"bytes"
"compress/gzip"
"errors"
"fmt"
"io"
"log/slog"
@ -214,7 +215,7 @@ func extractTar(r io.Reader, targetDir string) error {
for {
header, err := tarReader.Next()
if err == io.EOF {
if errors.Is(err, io.EOF) {
break
}
if err != nil {

@ -174,13 +174,12 @@ func buildLegacyRuntimeConfig(m MetadataLegacy) RuntimeConfig {
}
func fromMetadataV1(mv1 MetadataV1) (*Metadata, error) {
config, err := unmarshaConfig(mv1.Type, mv1.Config)
config, err := unmarshalConfig(mv1.Type, mv1.Config)
if err != nil {
return nil, err
}
runtimeConfig, err := convertMetdataRuntimeConfig(mv1.Runtime, mv1.RuntimeConfig)
runtimeConfig, err := convertMetadataRuntimeConfig(mv1.Runtime, mv1.RuntimeConfig)
if err != nil {
return nil, err
}
@ -197,7 +196,7 @@ func fromMetadataV1(mv1 MetadataV1) (*Metadata, error) {
}, nil
}
func convertMetdataRuntimeConfig(runtimeType string, runtimeConfigRaw map[string]any) (RuntimeConfig, error) {
func convertMetadataRuntimeConfig(runtimeType string, runtimeConfigRaw map[string]any) (RuntimeConfig, error) {
var runtimeConfig RuntimeConfig
var err error

@ -63,7 +63,7 @@ func ExtractTgzPluginMetadata(r io.Reader) (*Metadata, error) {
tr := tar.NewReader(gzr)
for {
header, err := tr.Next()
if err == io.EOF {
if errors.Is(err, io.EOF) {
break
}
if err != nil {

@ -86,19 +86,19 @@ func podConditions(u *unstructured.Unstructured) (*status.Result, error) {
},
},
}, 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,
default:
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
}, nil
}
}

@ -0,0 +1,44 @@
/*
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 version
import (
"fmt"
"runtime/debug"
"slices"
_ "k8s.io/client-go/kubernetes" // Force k8s.io/client-go to be included in the build
)
func K8sIOClientGoModVersion() (string, error) {
info, ok := debug.ReadBuildInfo()
if !ok {
return "", fmt.Errorf("failed to read build info")
}
idx := slices.IndexFunc(info.Deps, func(m *debug.Module) bool {
return m.Path == "k8s.io/client-go"
})
if idx == -1 {
return "", fmt.Errorf("k8s.io/client-go not found in build info")
}
m := info.Deps[idx]
return m.Version, nil
}

@ -0,0 +1,30 @@
/*
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 version
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestK8sClientGoModVersion(t *testing.T) {
// Unfortunately, test builds don't include debug info / module info
// So we expect "K8sIOClientGoModVersion" to return error
_, err := K8sIOClientGoModVersion()
require.ErrorContains(t, err, "k8s.io/client-go not found in build info")
}

@ -14,13 +14,17 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package version // import "helm.sh/helm/v4/internal/version"
package version
import (
"flag"
"fmt"
"log/slog"
"runtime"
"strings"
"testing"
"github.com/Masterminds/semver/v3"
)
var (
@ -38,11 +42,10 @@ var (
gitCommit = ""
// gitTreeState is the state of the git tree
gitTreeState = ""
)
// The Kubernetes version can be set by LDFLAGS. In order to do that the value
// must be a string.
kubeClientVersionMajor = ""
kubeClientVersionMinor = ""
const (
kubeClientGoVersionTesting = "v1.20"
)
// BuildInfo describes the compile time information.
@ -74,12 +77,39 @@ func GetUserAgent() string {
// Get returns build info
func Get() BuildInfo {
makeKubeClientVersionString := func() string {
// Test builds don't include debug info / module info
// (And even if they did, we probably want a stable version during tests anyway)
// Return a default value for test builds
if testing.Testing() {
return kubeClientGoVersionTesting
}
vstr, err := K8sIOClientGoModVersion()
if err != nil {
slog.Error("failed to retrieve k8s.io/client-go version", slog.Any("error", err))
return ""
}
v, err := semver.NewVersion(vstr)
if err != nil {
slog.Error("unable to parse k8s.io/client-go version", slog.String("version", vstr), slog.Any("error", err))
return ""
}
kubeClientVersionMajor := v.Major() + 1
kubeClientVersionMinor := v.Minor()
return fmt.Sprintf("v%d.%d", kubeClientVersionMajor, kubeClientVersionMinor)
}
v := BuildInfo{
Version: GetVersion(),
GitCommit: gitCommit,
GitTreeState: gitTreeState,
GoVersion: runtime.Version(),
KubeClientVersion: fmt.Sprintf("v%s.%s", kubeClientVersionMajor, kubeClientVersionMinor),
KubeClientVersion: makeKubeClientVersionString(),
}
// HACK(bacongobbler): strip out GoVersion during a test run for consistent test output

@ -201,6 +201,12 @@ func withMetadataDependency(dependency chart.Dependency) chartOption {
}
}
func withFile(file common.File) chartOption {
return func(opts *chartOptions) {
opts.Files = append(opts.Files, &file)
}
}
func withSampleTemplates() chartOption {
return func(opts *chartOptions) {
modTime := time.Now()

@ -121,7 +121,7 @@ func TestGetMetadata_Run_WithDependencies(t *testing.T) {
Namespace: "default",
}
cfg.Releases.Create(rel)
require.NoError(t, cfg.Releases.Create(rel))
result, err := client.Run(releaseName)
require.NoError(t, err)
@ -180,7 +180,7 @@ func TestGetMetadata_Run_WithDependenciesAliases(t *testing.T) {
Namespace: "default",
}
cfg.Releases.Create(rel)
require.NoError(t, cfg.Releases.Create(rel))
result, err := client.Run(releaseName)
require.NoError(t, err)
@ -251,7 +251,7 @@ func TestGetMetadata_Run_WithMixedDependencies(t *testing.T) {
Namespace: "default",
}
cfg.Releases.Create(rel)
require.NoError(t, cfg.Releases.Create(rel))
result, err := client.Run(releaseName)
require.NoError(t, err)
@ -315,7 +315,7 @@ func TestGetMetadata_Run_WithAnnotations(t *testing.T) {
Namespace: "default",
}
cfg.Releases.Create(rel)
require.NoError(t, cfg.Releases.Create(rel))
result, err := client.Run(releaseName)
require.NoError(t, err)
@ -370,8 +370,8 @@ func TestGetMetadata_Run_SpecificVersion(t *testing.T) {
Namespace: "default",
}
cfg.Releases.Create(rel1)
cfg.Releases.Create(rel2)
require.NoError(t, cfg.Releases.Create(rel1))
require.NoError(t, cfg.Releases.Create(rel2))
result, err := client.Run(releaseName)
require.NoError(t, err)
@ -424,7 +424,7 @@ func TestGetMetadata_Run_DifferentStatuses(t *testing.T) {
Namespace: "default",
}
cfg.Releases.Create(rel)
require.NoError(t, cfg.Releases.Create(rel))
result, err := client.Run(releaseName)
require.NoError(t, err)
@ -480,7 +480,7 @@ func TestGetMetadata_Run_EmptyAppVersion(t *testing.T) {
Namespace: "default",
}
cfg.Releases.Create(rel)
require.NoError(t, cfg.Releases.Create(rel))
result, err := client.Run(releaseName)
require.NoError(t, err)

@ -0,0 +1,69 @@
/*
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 (
"errors"
"io"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
kubefake "helm.sh/helm/v4/pkg/kube/fake"
"helm.sh/helm/v4/pkg/release/common"
)
func TestNewGet(t *testing.T) {
config := actionConfigFixture(t)
client := NewGet(config)
assert.NotNil(t, client)
assert.Equal(t, config, client.cfg)
assert.Equal(t, 0, client.Version)
}
func TestGetRun(t *testing.T) {
config := actionConfigFixture(t)
client := NewGet(config)
simpleRelease := namedReleaseStub("test-release", common.StatusPendingUpgrade)
require.NoError(t, config.Releases.Create(simpleRelease))
releaser, err := client.Run(simpleRelease.Name)
require.NoError(t, err)
result, err := releaserToV1Release(releaser)
require.NoError(t, err)
assert.Equal(t, simpleRelease.Name, result.Name)
assert.Equal(t, simpleRelease.Version, result.Version)
}
func TestGetRun_UnreachableKubeClient(t *testing.T) {
config := actionConfigFixture(t)
failingKubeClient := kubefake.FailingKubeClient{PrintingKubeClient: kubefake.PrintingKubeClient{Out: io.Discard}, DummyResources: nil}
failingKubeClient.ConnectionError = errors.New("connection refused")
config.KubeClient = &failingKubeClient
client := NewGet(config)
simpleRelease := namedReleaseStub("test-release", common.StatusPendingUpgrade)
require.NoError(t, config.Releases.Create(simpleRelease))
result, err := client.Run(simpleRelease.Name)
assert.Nil(t, result)
assert.Error(t, err)
}

@ -79,7 +79,7 @@ func TestGetValues_Run_UserConfigOnly(t *testing.T) {
Namespace: "default",
}
cfg.Releases.Create(rel)
require.NoError(t, cfg.Releases.Create(rel))
result, err := client.Run(releaseName)
require.NoError(t, err)
@ -127,7 +127,7 @@ func TestGetValues_Run_AllValues(t *testing.T) {
Namespace: "default",
}
cfg.Releases.Create(rel)
require.NoError(t, cfg.Releases.Create(rel))
result, err := client.Run(releaseName)
require.NoError(t, err)
@ -161,7 +161,7 @@ func TestGetValues_Run_EmptyValues(t *testing.T) {
Namespace: "default",
}
cfg.Releases.Create(rel)
require.NoError(t, cfg.Releases.Create(rel))
result, err := client.Run(releaseName)
require.NoError(t, err)
@ -212,7 +212,7 @@ func TestGetValues_Run_NilConfig(t *testing.T) {
Namespace: "default",
}
cfg.Releases.Create(rel)
require.NoError(t, cfg.Releases.Create(rel))
result, err := client.Run(releaseName)
require.NoError(t, err)

@ -20,7 +20,7 @@ import (
"fmt"
chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
release "helm.sh/helm/v4/pkg/release"
"helm.sh/helm/v4/pkg/release"
)
// History is the action for checking the release's ledger.

@ -0,0 +1,108 @@
/*
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 (
"errors"
"io"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
kubefake "helm.sh/helm/v4/pkg/kube/fake"
release "helm.sh/helm/v4/pkg/release/v1"
"helm.sh/helm/v4/pkg/release/common"
)
func TestNewHistory(t *testing.T) {
config := actionConfigFixture(t)
client := NewHistory(config)
assert.NotNil(t, client)
assert.Equal(t, config, client.cfg)
}
func TestHistoryRun(t *testing.T) {
releaseName := "test-release"
simpleRelease := namedReleaseStub(releaseName, common.StatusPendingUpgrade)
updatedRelease := namedReleaseStub(releaseName, common.StatusDeployed)
updatedRelease.Chart.Metadata.Version = "0.1.1"
updatedRelease.Version = 2
config := actionConfigFixture(t)
client := NewHistory(config)
client.Max = 3
client.cfg.Releases.MaxHistory = 3
for _, rel := range []*release.Release{simpleRelease, updatedRelease} {
if err := client.cfg.Releases.Create(rel); err != nil {
t.Fatal(err, "Could not add releases to Config")
}
}
releases, err := config.Releases.ListReleases()
require.NoError(t, err)
assert.Len(t, releases, 2, "expected 2 Releases in Config")
releasers, err := client.Run(releaseName)
require.NoError(t, err)
assert.Len(t, releasers, 2, "expected 2 Releases in History result")
release1, err := releaserToV1Release(releasers[0])
require.NoError(t, err)
assert.Equal(t, simpleRelease.Name, release1.Name)
assert.Equal(t, simpleRelease.Version, release1.Version)
release2, err := releaserToV1Release(releasers[1])
require.NoError(t, err)
assert.Equal(t, updatedRelease.Name, release2.Name)
assert.Equal(t, updatedRelease.Version, release2.Version)
}
func TestHistoryRun_UnreachableKubeClient(t *testing.T) {
config := actionConfigFixture(t)
failingKubeClient := kubefake.FailingKubeClient{PrintingKubeClient: kubefake.PrintingKubeClient{Out: io.Discard}, DummyResources: nil}
failingKubeClient.ConnectionError = errors.New("connection refused")
config.KubeClient = &failingKubeClient
client := NewHistory(config)
result, err := client.Run("release-name")
assert.Nil(t, result)
assert.Error(t, err)
}
func TestHistoryRun_InvalidReleaseNames(t *testing.T) {
config := actionConfigFixture(t)
client := NewHistory(config)
invalidReleaseNames := []string{
"",
"too-long-release-name-max-53-characters-abcdefghijklmnopqrstuvwxyz",
"MyRelease",
"release_name",
"release@123",
"-badstart",
"badend-",
".dotstart",
}
for _, name := range invalidReleaseNames {
result, err := client.Run(name)
assert.Nil(t, result)
assert.ErrorContains(t, err, "release name is invalid")
}
}

@ -116,7 +116,7 @@ type Install struct {
Labels map[string]string
// KubeVersion allows specifying a custom kubernetes version to use and
// APIVersions allows a manual set of supported API Versions to be passed
// (for things like templating). These are ignored if ClientOnly is false
// (for things like templating).
KubeVersion *common.KubeVersion
APIVersions common.VersionSet
// Used by helm template to render charts with .Release.IsUpgrade. Ignored if Dry-Run is false
@ -192,7 +192,7 @@ func (i *Install) installCRDs(crds []chart.CRD) error {
kube.ClientCreateOptionServerSideApply(i.ServerSideApply, i.ForceConflicts)); err != nil {
// If the error is CRD already exists, continue.
if apierrors.IsAlreadyExists(err) {
crdName := res[0].Name
crdName := obj.Name
i.cfg.Logger().Debug("CRD is already present. Skipping", "crd", crdName)
continue
}

@ -35,6 +35,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
appsv1 "k8s.io/api/apps/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kuberuntime "k8s.io/apimachinery/pkg/runtime"
@ -43,10 +44,14 @@ import (
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest/fake"
ci "helm.sh/helm/v4/pkg/chart"
"helm.sh/helm/v4/internal/test"
"helm.sh/helm/v4/pkg/chart/common"
chart "helm.sh/helm/v4/pkg/chart/v2"
"helm.sh/helm/v4/pkg/kube"
kubefake "helm.sh/helm/v4/pkg/kube/fake"
"helm.sh/helm/v4/pkg/registry"
rcommon "helm.sh/helm/v4/pkg/release/common"
release "helm.sh/helm/v4/pkg/release/v1"
"helm.sh/helm/v4/pkg/storage/driver"
@ -500,7 +505,7 @@ func TestInstallRelease_NoHooks(t *testing.T) {
instAction := installAction(t)
instAction.DisableHooks = true
instAction.ReleaseName = "no-hooks"
instAction.cfg.Releases.Create(releaseStub())
require.NoError(t, instAction.cfg.Releases.Create(releaseStub()))
vals := map[string]interface{}{}
resi, err := instAction.Run(buildChart(), vals)
@ -540,7 +545,7 @@ func TestInstallRelease_ReplaceRelease(t *testing.T) {
rel := releaseStub()
rel.Info.Status = rcommon.StatusUninstalled
instAction.cfg.Releases.Create(rel)
require.NoError(t, instAction.cfg.Releases.Create(rel))
instAction.ReleaseName = rel.Name
vals := map[string]interface{}{}
@ -1068,3 +1073,116 @@ func TestInstallRun_UnreachableKubeClient(t *testing.T) {
assert.Nil(t, res)
assert.ErrorContains(t, err, "connection refused")
}
func TestInstallSetRegistryClient(t *testing.T) {
config := actionConfigFixture(t)
instAction := NewInstall(config)
registryClient := &registry.Client{}
instAction.SetRegistryClient(registryClient)
assert.Equal(t, registryClient, instAction.GetRegistryClient())
}
func TestInstalLCRDs(t *testing.T) {
config := actionConfigFixture(t)
instAction := NewInstall(config)
mockFile := common.File{
Name: "crds/foo.yaml",
Data: []byte("hello"),
}
mockChart := buildChart(withFile(mockFile))
crdsToInstall := mockChart.CRDObjects()
assert.Len(t, crdsToInstall, 1)
assert.Equal(t, crdsToInstall[0].File.Data, mockFile.Data)
require.NoError(t, instAction.installCRDs(crdsToInstall))
}
func TestInstalLCRDs_KubeClient_BuildError(t *testing.T) {
config := actionConfigFixture(t)
failingKubeClient := kubefake.FailingKubeClient{PrintingKubeClient: kubefake.PrintingKubeClient{Out: io.Discard}, DummyResources: nil}
failingKubeClient.BuildError = errors.New("build error")
config.KubeClient = &failingKubeClient
instAction := NewInstall(config)
mockFile := common.File{
Name: "crds/foo.yaml",
Data: []byte("hello"),
}
mockChart := buildChart(withFile(mockFile))
crdsToInstall := mockChart.CRDObjects()
require.Error(t, instAction.installCRDs(crdsToInstall), "failed to install CRD")
}
func TestInstalLCRDs_KubeClient_CreateError(t *testing.T) {
config := actionConfigFixture(t)
failingKubeClient := kubefake.FailingKubeClient{PrintingKubeClient: kubefake.PrintingKubeClient{Out: io.Discard}, DummyResources: nil}
failingKubeClient.CreateError = errors.New("create error")
config.KubeClient = &failingKubeClient
instAction := NewInstall(config)
mockFile := common.File{
Name: "crds/foo.yaml",
Data: []byte("hello"),
}
mockChart := buildChart(withFile(mockFile))
crdsToInstall := mockChart.CRDObjects()
require.Error(t, instAction.installCRDs(crdsToInstall), "failed to install CRD")
}
func TestInstalLCRDs_AlreadyExist(t *testing.T) {
config := actionConfigFixture(t)
failingKubeClient := kubefake.FailingKubeClient{PrintingKubeClient: kubefake.PrintingKubeClient{Out: io.Discard}, DummyResources: nil}
mockError := &apierrors.StatusError{ErrStatus: metav1.Status{
Status: metav1.StatusFailure,
Reason: metav1.StatusReasonAlreadyExists,
}}
failingKubeClient.CreateError = mockError
config.KubeClient = &failingKubeClient
instAction := NewInstall(config)
mockFile := common.File{
Name: "crds/foo.yaml",
Data: []byte("hello"),
}
mockChart := buildChart(withFile(mockFile))
crdsToInstall := mockChart.CRDObjects()
assert.Nil(t, instAction.installCRDs(crdsToInstall))
}
func TestInstalLCRDs_WaiterError(t *testing.T) {
config := actionConfigFixture(t)
failingKubeClient := kubefake.FailingKubeClient{PrintingKubeClient: kubefake.PrintingKubeClient{Out: io.Discard}, DummyResources: nil}
failingKubeClient.WaitError = errors.New("wait error")
failingKubeClient.BuildDummy = true
config.KubeClient = &failingKubeClient
instAction := NewInstall(config)
mockFile := common.File{
Name: "crds/foo.yaml",
Data: []byte("hello"),
}
mockChart := buildChart(withFile(mockFile))
crdsToInstall := mockChart.CRDObjects()
require.Error(t, instAction.installCRDs(crdsToInstall), "wait error")
}
func TestCheckDependencies(t *testing.T) {
dependency := chart.Dependency{Name: "hello"}
mockChart := buildChart(withDependency())
assert.Nil(t, CheckDependencies(mockChart, []ci.Dependency{&dependency}))
}
func TestCheckDependencies_MissingDependency(t *testing.T) {
dependency := chart.Dependency{Name: "missing"}
mockChart := buildChart(withDependency())
assert.ErrorContains(t, CheckDependencies(mockChart, []ci.Dependency{&dependency}), "missing in charts")
}

@ -17,7 +17,12 @@ limitations under the License.
package action
import (
"errors"
"testing"
"github.com/stretchr/testify/assert"
"helm.sh/helm/v4/pkg/chart/v2/lint/support"
)
var (
@ -163,3 +168,45 @@ func TestLint_ChartWithWarnings(t *testing.T) {
}
})
}
func TestHasWarningsOrErrors(t *testing.T) {
testError := errors.New("test-error")
cases := []struct {
name string
data LintResult
expected bool
}{
{
name: "has no warning messages and no errors",
data: LintResult{TotalChartsLinted: 1, Messages: make([]support.Message, 0), Errors: make([]error, 0)},
expected: false,
},
{
name: "has error",
data: LintResult{TotalChartsLinted: 1, Messages: make([]support.Message, 0), Errors: []error{testError}},
expected: true,
},
{
name: "has info message only",
data: LintResult{TotalChartsLinted: 1, Messages: []support.Message{{Severity: support.InfoSev, Path: "", Err: testError}}, Errors: make([]error, 0)},
expected: false,
},
{
name: "has warning message",
data: LintResult{TotalChartsLinted: 1, Messages: []support.Message{{Severity: support.WarningSev, Path: "", Err: testError}}, Errors: make([]error, 0)},
expected: true,
},
{
name: "has error message",
data: LintResult{TotalChartsLinted: 1, Messages: []support.Message{{Severity: support.ErrorSev, Path: "", Err: testError}}, Errors: make([]error, 0)},
expected: true,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
result := HasWarningsOrErrors(&tc.data)
assert.Equal(t, tc.expected, result)
})
}
}

@ -22,6 +22,7 @@ import (
"testing"
"github.com/Masterminds/semver/v3"
"github.com/stretchr/testify/require"
"helm.sh/helm/v4/internal/test/ensure"
)
@ -90,7 +91,8 @@ func TestPassphraseFileFetcher_WithStdinAndMultipleFetches(t *testing.T) {
passphrase := "secret-from-stdin"
go func() {
w.Write([]byte(passphrase + "\n"))
_, err = w.Write([]byte(passphrase + "\n"))
require.NoError(t, err)
}()
for range 4 {
@ -152,3 +154,18 @@ func TestValidateVersion(t *testing.T) {
})
}
}
func TestRun_ErrorPath(t *testing.T) {
client := NewPackage()
_, err := client.Run("err-path", nil)
require.Error(t, err)
}
func TestRun(t *testing.T) {
chartPath := "testdata/charts/chart-with-schema"
client := NewPackage()
filename, err := client.Run(chartPath, nil)
require.NoError(t, err)
require.Equal(t, "empty-0.1.0.tgz", filename)
require.NoError(t, os.Remove(filename))
}

@ -0,0 +1,80 @@
/*
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 (
"net/http"
"net/http/httptest"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"helm.sh/helm/v4/pkg/cli"
"helm.sh/helm/v4/pkg/registry"
)
func TestNewPull(t *testing.T) {
config := actionConfigFixture(t)
client := NewPull(WithConfig(config))
assert.NotNil(t, client)
assert.Equal(t, config, client.cfg)
}
func TestPullSetRegistryClient(t *testing.T) {
config := actionConfigFixture(t)
client := NewPull(WithConfig(config))
registryClient := &registry.Client{}
client.SetRegistryClient(registryClient)
assert.Equal(t, registryClient, client.cfg.RegistryClient)
}
func TestPullRun_ChartNotFound(t *testing.T) {
srv, err := startLocalServerForTests(t, nil)
if err != nil {
t.Fatal(err)
}
defer srv.Close()
config := actionConfigFixture(t)
client := NewPull(WithConfig(config))
client.Settings = cli.New()
client.RepoURL = srv.URL
chartRef := "nginx"
_, err = client.Run(chartRef)
require.ErrorContains(t, err, "404 Not Found")
}
func startLocalServerForTests(t *testing.T, handler http.Handler) (*httptest.Server, error) {
t.Helper()
if handler == nil {
fileBytes, err := os.ReadFile("../repo/v1/testdata/local-index.yaml")
if err != nil {
return nil, err
}
handler = http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
_, err = w.Write(fileBytes)
require.NoError(t, err)
})
}
return httptest.NewServer(handler), nil
}

@ -0,0 +1,66 @@
/*
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"
"testing"
"github.com/stretchr/testify/assert"
)
func TestNewPushWithPushConfig(t *testing.T) {
config := actionConfigFixture(t)
client := NewPushWithOpts(WithPushConfig(config))
assert.NotNil(t, client)
assert.Equal(t, config, client.cfg)
}
func TestNewPushWithTLSClientConfig(t *testing.T) {
certFile := "certFile"
keyFile := "keyFile"
caFile := "caFile"
client := NewPushWithOpts(WithTLSClientConfig(certFile, keyFile, caFile))
assert.NotNil(t, client)
assert.Equal(t, certFile, client.certFile)
assert.Equal(t, keyFile, client.keyFile)
assert.Equal(t, caFile, client.caFile)
}
func TestNewPushWithInsecureSkipTLSVerify(t *testing.T) {
client := NewPushWithOpts(WithInsecureSkipTLSVerify(true))
assert.NotNil(t, client)
assert.Equal(t, true, client.insecureSkipTLSVerify)
}
func TestNewPushWithPlainHTTP(t *testing.T) {
client := NewPushWithOpts(WithPlainHTTP(true))
assert.NotNil(t, client)
assert.Equal(t, true, client.plainHTTP)
}
func TestNewPushWithPushOptWriter(t *testing.T) {
buf := new(bytes.Buffer)
client := NewPushWithOpts(WithPushOptWriter(buf))
assert.NotNil(t, client)
assert.Equal(t, buf, client.out)
}

@ -0,0 +1,84 @@
/*
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 (
"testing"
"github.com/stretchr/testify/assert"
)
func TestNewRegistryLogin(t *testing.T) {
config := actionConfigFixture(t)
client := NewRegistryLogin(config)
assert.NotNil(t, client)
assert.Equal(t, config, client.cfg)
}
func TestWithCertFile(t *testing.T) {
config := actionConfigFixture(t)
client := NewRegistryLogin(config)
certFile := "testdata/cert.pem"
opt := WithCertFile(certFile)
assert.Nil(t, opt(client))
assert.Equal(t, certFile, client.certFile)
}
func TestWithInsecure(t *testing.T) {
config := actionConfigFixture(t)
client := NewRegistryLogin(config)
opt := WithInsecure(true)
assert.Nil(t, opt(client))
assert.Equal(t, true, client.insecure)
}
func TestWithKeyFile(t *testing.T) {
config := actionConfigFixture(t)
client := NewRegistryLogin(config)
keyFile := "testdata/key.pem"
opt := WithKeyFile(keyFile)
assert.Nil(t, opt(client))
assert.Equal(t, keyFile, client.keyFile)
}
func TestWithCAFile(t *testing.T) {
config := actionConfigFixture(t)
client := NewRegistryLogin(config)
caFile := "testdata/ca.pem"
opt := WithCAFile(caFile)
assert.Nil(t, opt(client))
assert.Equal(t, caFile, client.caFile)
}
func TestWithPlainHTTPLogin(t *testing.T) {
config := actionConfigFixture(t)
client := NewRegistryLogin(config)
opt := WithPlainHTTPLogin(true)
assert.Nil(t, opt(client))
assert.Equal(t, true, client.plainHTTP)
}

@ -0,0 +1,31 @@
/*
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 (
"testing"
"github.com/stretchr/testify/assert"
)
func TestNewRegistryLogout(t *testing.T) {
config := actionConfigFixture(t)
client := NewRegistryLogout(config)
assert.NotNil(t, client)
assert.Equal(t, config, client.cfg)
}

@ -0,0 +1,91 @@
/*
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"
"errors"
"io"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"helm.sh/helm/v4/pkg/cli"
kubefake "helm.sh/helm/v4/pkg/kube/fake"
release "helm.sh/helm/v4/pkg/release/v1"
)
func TestNewReleaseTesting(t *testing.T) {
config := actionConfigFixture(t)
client := NewReleaseTesting(config)
assert.NotNil(t, client)
assert.Equal(t, config, client.cfg)
}
func TestReleaseTestingRun_UnreachableKubeClient(t *testing.T) {
config := actionConfigFixture(t)
failingKubeClient := kubefake.FailingKubeClient{PrintingKubeClient: kubefake.PrintingKubeClient{Out: io.Discard}, DummyResources: nil}
failingKubeClient.ConnectionError = errors.New("connection refused")
config.KubeClient = &failingKubeClient
client := NewReleaseTesting(config)
result, err := client.Run("")
assert.Nil(t, result)
assert.Error(t, err)
}
func TestReleaseTestingGetPodLogs_FilterEvents(t *testing.T) {
config := actionConfigFixture(t)
require.NoError(t, config.Init(cli.New().RESTClientGetter(), "", os.Getenv("HELM_DRIVER")))
client := NewReleaseTesting(config)
client.Filters[ExcludeNameFilter] = []string{"event-1"}
client.Filters[IncludeNameFilter] = []string{"event-3"}
hooks := []*release.Hook{
{
Name: "event-1",
Events: []release.HookEvent{release.HookTest},
},
{
Name: "event-2",
Events: []release.HookEvent{release.HookTest},
},
}
out := &bytes.Buffer{}
require.NoError(t, client.GetPodLogs(out, &release.Release{Hooks: hooks}))
assert.Empty(t, out.String())
}
func TestReleaseTestingGetPodLogs_PodRetrievalError(t *testing.T) {
config := actionConfigFixture(t)
require.NoError(t, config.Init(cli.New().RESTClientGetter(), "", os.Getenv("HELM_DRIVER")))
client := NewReleaseTesting(config)
hooks := []*release.Hook{
{
Name: "event-1",
Events: []release.HookEvent{release.HookTest},
},
}
require.ErrorContains(t, client.GetPodLogs(&bytes.Buffer{}, &release.Release{Hooks: hooks}), "unable to get pod logs")
}

@ -0,0 +1,45 @@
/*
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 (
"errors"
"io"
"testing"
"github.com/stretchr/testify/assert"
kubefake "helm.sh/helm/v4/pkg/kube/fake"
)
func TestNewRollback(t *testing.T) {
config := actionConfigFixture(t)
client := NewRollback(config)
assert.NotNil(t, client)
assert.Equal(t, config, client.cfg)
}
func TestRollbackRun_UnreachableKubeClient(t *testing.T) {
config := actionConfigFixture(t)
failingKubeClient := kubefake.FailingKubeClient{PrintingKubeClient: kubefake.PrintingKubeClient{Out: io.Discard}, DummyResources: nil}
failingKubeClient.ConnectionError = errors.New("connection refused")
config.KubeClient = &failingKubeClient
client := NewRollback(config)
assert.Error(t, client.Run(""))
}

@ -20,8 +20,11 @@ import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"helm.sh/helm/v4/pkg/chart/common"
chart "helm.sh/helm/v4/pkg/chart/v2"
"helm.sh/helm/v4/pkg/registry"
)
func TestShow(t *testing.T) {
@ -168,3 +171,12 @@ bar
t.Errorf("Expected\n%q\nGot\n%q\n", expect, output)
}
}
func TestShowSetRegistryClient(t *testing.T) {
config := actionConfigFixture(t)
client := NewShow(ShowAll, config)
registryClient := &registry.Client{}
client.SetRegistryClient(registryClient)
assert.Equal(t, registryClient, client.registryClient)
}

@ -0,0 +1,143 @@
/*
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 (
"errors"
"io"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
kubefake "helm.sh/helm/v4/pkg/kube/fake"
rcommon "helm.sh/helm/v4/pkg/release/common"
release "helm.sh/helm/v4/pkg/release/v1"
)
func TestNewStatus(t *testing.T) {
config := actionConfigFixture(t)
client := NewStatus(config)
assert.NotNil(t, client)
assert.Equal(t, config, client.cfg)
assert.Equal(t, 0, client.Version)
}
func TestStatusRun(t *testing.T) {
config := actionConfigFixture(t)
failingKubeClient := kubefake.FailingKubeClient{PrintingKubeClient: kubefake.PrintingKubeClient{Out: io.Discard}, DummyResources: nil}
failingKubeClient.BuildDummy = true
config.KubeClient = &failingKubeClient
client := NewStatus(config)
client.ShowResourcesTable = true
releaseName := "test-release"
require.NoError(t, configureReleaseContent(config, releaseName))
releaser, err := client.Run(releaseName)
require.NoError(t, err)
result, err := releaserToV1Release(releaser)
require.NoError(t, err)
assert.Equal(t, releaseName, result.Name)
assert.Equal(t, 1, result.Version)
}
func TestStatusRun_KubeClientNotReachable(t *testing.T) {
config := actionConfigFixture(t)
failingKubeClient := kubefake.FailingKubeClient{PrintingKubeClient: kubefake.PrintingKubeClient{Out: io.Discard}, DummyResources: nil}
failingKubeClient.ConnectionError = errors.New("connection refused")
config.KubeClient = &failingKubeClient
client := NewStatus(config)
result, err := client.Run("")
assert.Nil(t, result)
assert.Error(t, err)
}
func TestStatusRun_KubeClientBuildTableError(t *testing.T) {
config := actionConfigFixture(t)
failingKubeClient := kubefake.FailingKubeClient{PrintingKubeClient: kubefake.PrintingKubeClient{Out: io.Discard}, DummyResources: nil}
failingKubeClient.BuildTableError = errors.New("build table error")
config.KubeClient = &failingKubeClient
releaseName := "test-release"
require.NoError(t, configureReleaseContent(config, releaseName))
client := NewStatus(config)
client.ShowResourcesTable = true
result, err := client.Run(releaseName)
assert.Nil(t, result)
assert.ErrorContains(t, err, "build table error")
}
func TestStatusRun_KubeClientBuildError(t *testing.T) {
config := actionConfigFixture(t)
failingKubeClient := kubefake.FailingKubeClient{PrintingKubeClient: kubefake.PrintingKubeClient{Out: io.Discard}, DummyResources: nil}
failingKubeClient.BuildError = errors.New("build error")
config.KubeClient = &failingKubeClient
releaseName := "test-release"
require.NoError(t, configureReleaseContent(config, releaseName))
client := NewStatus(config)
client.ShowResourcesTable = false
result, err := client.Run(releaseName)
assert.Nil(t, result)
assert.ErrorContains(t, err, "build error")
}
func TestStatusRun_KubeClientGetError(t *testing.T) {
config := actionConfigFixture(t)
failingKubeClient := kubefake.FailingKubeClient{PrintingKubeClient: kubefake.PrintingKubeClient{Out: io.Discard}, DummyResources: nil}
failingKubeClient.BuildError = errors.New("get error")
config.KubeClient = &failingKubeClient
releaseName := "test-release"
require.NoError(t, configureReleaseContent(config, releaseName))
client := NewStatus(config)
result, err := client.Run(releaseName)
assert.Nil(t, result)
assert.ErrorContains(t, err, "get error")
}
func configureReleaseContent(cfg *Configuration, releaseName string) error {
rel := &release.Release{
Name: releaseName,
Info: &release.Info{
Status: rcommon.StatusDeployed,
},
Manifest: testManifest,
Version: 1,
Namespace: "default",
}
return cfg.Releases.Create(rel)
}
const testManifest = `
apiVersion: v1
kind: Pod
metadata:
namespace: default
name: test-application
`

@ -82,7 +82,7 @@ func TestUninstallRelease_deleteRelease(t *testing.T) {
"password": "password"
}
}`
unAction.cfg.Releases.Create(rel)
require.NoError(t, unAction.cfg.Releases.Create(rel))
res, err := unAction.Run(rel.Name)
is.NoError(err)
expected := `These resources were kept due to the resource policy:
@ -112,7 +112,7 @@ func TestUninstallRelease_Wait(t *testing.T) {
"password": "password"
}
}`
unAction.cfg.Releases.Create(rel)
require.NoError(t, unAction.cfg.Releases.Create(rel))
failer := unAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
failer.WaitForDeleteError = fmt.Errorf("U timed out")
unAction.cfg.KubeClient = failer
@ -146,7 +146,7 @@ func TestUninstallRelease_Cascade(t *testing.T) {
"password": "password"
}
}`
unAction.cfg.Releases.Create(rel)
require.NoError(t, unAction.cfg.Releases.Create(rel))
failer := unAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
failer.DeleteError = fmt.Errorf("Uninstall with cascade failed")
failer.BuildDummy = true

@ -25,16 +25,19 @@ import (
"testing"
"time"
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"
appsv1 "k8s.io/api/apps/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/cli-runtime/pkg/resource"
chart "helm.sh/helm/v4/pkg/chart/v2"
"helm.sh/helm/v4/pkg/kube"
kubefake "helm.sh/helm/v4/pkg/kube/fake"
"helm.sh/helm/v4/pkg/registry"
"helm.sh/helm/v4/pkg/release/common"
release "helm.sh/helm/v4/pkg/release/v1"
"helm.sh/helm/v4/pkg/storage/driver"
)
func upgradeAction(t *testing.T) *Upgrade {
@ -85,7 +88,7 @@ func TestUpgradeRelease_Wait(t *testing.T) {
rel := releaseStub()
rel.Name = "come-fail-away"
rel.Info.Status = common.StatusDeployed
upAction.cfg.Releases.Create(rel)
require.NoError(t, upAction.cfg.Releases.Create(rel))
failer := upAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
failer.WaitError = fmt.Errorf("I timed out")
@ -109,7 +112,7 @@ func TestUpgradeRelease_WaitForJobs(t *testing.T) {
rel := releaseStub()
rel.Name = "come-fail-away"
rel.Info.Status = common.StatusDeployed
upAction.cfg.Releases.Create(rel)
require.NoError(t, upAction.cfg.Releases.Create(rel))
failer := upAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
failer.WaitError = fmt.Errorf("I timed out")
@ -134,7 +137,7 @@ func TestUpgradeRelease_CleanupOnFail(t *testing.T) {
rel := releaseStub()
rel.Name = "come-fail-away"
rel.Info.Status = common.StatusDeployed
upAction.cfg.Releases.Create(rel)
require.NoError(t, upAction.cfg.Releases.Create(rel))
failer := upAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
failer.WaitError = fmt.Errorf("I timed out")
@ -163,7 +166,7 @@ func TestUpgradeRelease_RollbackOnFailure(t *testing.T) {
rel := releaseStub()
rel.Name = "nuketown"
rel.Info.Status = common.StatusDeployed
upAction.cfg.Releases.Create(rel)
require.NoError(t, upAction.cfg.Releases.Create(rel))
failer := upAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
// We can't make Update error because then the rollback won't work
@ -193,7 +196,7 @@ func TestUpgradeRelease_RollbackOnFailure(t *testing.T) {
rel := releaseStub()
rel.Name = "fallout"
rel.Info.Status = common.StatusDeployed
upAction.cfg.Releases.Create(rel)
require.NoError(t, upAction.cfg.Releases.Create(rel))
failer := upAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
failer.UpdateError = fmt.Errorf("update fail")
@ -401,12 +404,12 @@ func TestUpgradeRelease_Pending(t *testing.T) {
rel := releaseStub()
rel.Name = "come-fail-away"
rel.Info.Status = common.StatusDeployed
upAction.cfg.Releases.Create(rel)
require.NoError(t, upAction.cfg.Releases.Create(rel))
rel2 := releaseStub()
rel2.Name = "come-fail-away"
rel2.Info.Status = common.StatusPendingUpgrade
rel2.Version = 2
upAction.cfg.Releases.Create(rel2)
require.NoError(t, upAction.cfg.Releases.Create(rel2))
vals := map[string]interface{}{}
@ -422,7 +425,7 @@ func TestUpgradeRelease_Interrupted_Wait(t *testing.T) {
rel := releaseStub()
rel.Name = "interrupted-release"
rel.Info.Status = common.StatusDeployed
upAction.cfg.Releases.Create(rel)
require.NoError(t, upAction.cfg.Releases.Create(rel))
failer := upAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
failer.WaitDuration = 10 * time.Second
@ -451,7 +454,7 @@ func TestUpgradeRelease_Interrupted_RollbackOnFailure(t *testing.T) {
rel := releaseStub()
rel.Name = "interrupted-release"
rel.Info.Status = common.StatusDeployed
upAction.cfg.Releases.Create(rel)
require.NoError(t, upAction.cfg.Releases.Create(rel))
failer := upAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
failer.WaitDuration = 5 * time.Second
@ -755,3 +758,20 @@ func TestUpgradeRun_UnreachableKubeClient(t *testing.T) {
assert.Nil(t, result)
assert.ErrorContains(t, err, "connection refused")
}
func TestUpgradeSetRegistryClient(t *testing.T) {
config := actionConfigFixture(t)
client := NewUpgrade(config)
registryClient := &registry.Client{}
client.SetRegistryClient(registryClient)
assert.Equal(t, registryClient, client.registryClient)
}
func TestObjectKey(t *testing.T) {
obj := &appsv1.Deployment{}
obj.SetGroupVersionKind(schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"})
info := resource.Info{Name: "name", Namespace: "namespace", Object: obj}
assert.Equal(t, "apps/v1/Deployment/namespace/name", objectKey(&info))
}

@ -0,0 +1,48 @@
/*
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 (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewVerify(t *testing.T) {
client := NewVerify()
assert.NotNil(t, client)
}
func TestVerifyRun(t *testing.T) {
client := NewVerify()
client.Keyring = "../downloader/testdata/helm-test-key.pub"
output, err := client.Run("../downloader/testdata/signtest-0.1.0.tgz")
assert.Contains(t, output, "Signed by:")
assert.Contains(t, output, "Using Key With Fingerprint:")
assert.Contains(t, output, "Chart Hash Verified:")
require.NoError(t, err)
}
func TestVerifyRun_DownloadError(t *testing.T) {
client := NewVerify()
output, err := client.Run("invalid-chart-path")
require.Error(t, err)
assert.Empty(t, output)
}

@ -186,7 +186,7 @@ func structToMap(obj interface{}) (map[string]interface{}, error) {
objValue := reflect.ValueOf(obj)
// If the value is a pointer, dereference it
if objValue.Kind() == reflect.Ptr {
if objValue.Kind() == reflect.Pointer {
objValue = objValue.Elem()
}
@ -209,7 +209,7 @@ func structToMap(obj interface{}) (map[string]interface{}, error) {
return nil, err
}
result[field.Name] = nestedMap
case reflect.Ptr:
case reflect.Pointer:
// Recurse for pointers by dereferencing
if value.IsNil() {
result[field.Name] = nil
@ -224,7 +224,7 @@ func structToMap(obj interface{}) (map[string]interface{}, error) {
sliceOfMaps := make([]interface{}, value.Len())
for j := 0; j < value.Len(); j++ {
sliceElement := value.Index(j)
if sliceElement.Kind() == reflect.Struct || sliceElement.Kind() == reflect.Ptr {
if sliceElement.Kind() == reflect.Struct || sliceElement.Kind() == reflect.Pointer {
nestedMap, err := structToMap(sliceElement.Interface())
if err != nil {
return nil, err

@ -20,7 +20,9 @@ import (
"slices"
"strconv"
"strings"
"testing"
"github.com/Masterminds/semver/v3"
"k8s.io/client-go/kubernetes/scheme"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
@ -30,27 +32,23 @@ import (
helmversion "helm.sh/helm/v4/internal/version"
)
var (
// The Kubernetes version can be set by LDFLAGS. In order to do that the value
// must be a string.
k8sVersionMajor = "1"
k8sVersionMinor = "20"
const (
kubeVersionMajorTesting = 1
kubeVersionMinorTesting = 20
)
var (
// DefaultVersionSet is the default version set, which includes only Core V1 ("v1").
DefaultVersionSet = allKnownVersions()
// DefaultCapabilities is the default set of capabilities.
version = fmt.Sprintf("v%s.%s.0", k8sVersionMajor, k8sVersionMinor)
DefaultCapabilities = &Capabilities{
KubeVersion: KubeVersion{
Version: version,
normalizedVersion: version,
Major: k8sVersionMajor,
Minor: k8sVersionMinor,
},
APIVersions: DefaultVersionSet,
HelmVersion: helmversion.Get(),
}
DefaultCapabilities = func() *Capabilities {
caps, err := makeDefaultCapabilities()
if err != nil {
panic(fmt.Sprintf("failed to create default capabilities: %v", err))
}
return caps
}()
)
// Capabilities describes the capabilities of the Kubernetes cluster.
@ -143,3 +141,42 @@ func allKnownVersions() VersionSet {
}
return vs
}
func makeDefaultCapabilities() (*Capabilities, error) {
// Test builds don't include debug info / module info
// (And even if they did, we probably want stable capabilities for tests anyway)
// Return a default value for test builds
if testing.Testing() {
return newCapabilities(kubeVersionMajorTesting, kubeVersionMinorTesting)
}
vstr, err := helmversion.K8sIOClientGoModVersion()
if err != nil {
return nil, fmt.Errorf("failed to retrieve k8s.io/client-go version: %w", err)
}
v, err := semver.NewVersion(vstr)
if err != nil {
return nil, fmt.Errorf("unable to parse k8s.io/client-go version %q: %v", vstr, err)
}
kubeVersionMajor := v.Major() + 1
kubeVersionMinor := v.Minor()
return newCapabilities(kubeVersionMajor, kubeVersionMinor)
}
func newCapabilities(kubeVersionMajor, kubeVersionMinor uint64) (*Capabilities, error) {
version := fmt.Sprintf("v%d.%d.0", kubeVersionMajor, kubeVersionMinor)
return &Capabilities{
KubeVersion: KubeVersion{
Version: version,
normalizedVersion: version,
Major: fmt.Sprintf("%d", kubeVersionMajor),
Minor: fmt.Sprintf("%d", kubeVersionMinor),
},
APIVersions: DefaultVersionSet,
HelmVersion: helmversion.Get(),
}, nil
}

@ -41,7 +41,8 @@ func TestDefaultVersionSet(t *testing.T) {
}
func TestDefaultCapabilities(t *testing.T) {
kv := DefaultCapabilities.KubeVersion
caps := DefaultCapabilities
kv := caps.KubeVersion
if kv.String() != "v1.20.0" {
t.Errorf("Expected default KubeVersion.String() to be v1.20.0, got %q", kv.String())
}
@ -57,11 +58,8 @@ func TestDefaultCapabilities(t *testing.T) {
if kv.Minor != "20" {
t.Errorf("Expected default KubeVersion.Minor to be 20, got %q", kv.Minor)
}
}
func TestDefaultCapabilitiesHelmVersion(t *testing.T) {
hv := DefaultCapabilities.HelmVersion
hv := caps.HelmVersion
if hv.Version != "v4.1" {
t.Errorf("Expected default HelmVersion to be v4.1, got %q", hv.Version)
}

@ -68,7 +68,7 @@ func LoadArchiveFiles(in io.Reader) ([]*BufferedFile, error) {
for {
b := bytes.NewBuffer(nil)
hd, err := tr.Next()
if err == io.EOF {
if errors.Is(err, io.EOF) {
break
}
if err != nil {

@ -131,8 +131,8 @@ func LoadFile(name string) (chart.Charter, error) {
files, err := archive.LoadArchiveFiles(raw)
if err != nil {
if err == gzip.ErrHeader {
return nil, fmt.Errorf("file '%s' does not appear to be a valid chart file (details: %s)", name, err)
if errors.Is(err, gzip.ErrHeader) {
return nil, fmt.Errorf("file '%s' does not appear to be a valid chart file (details: %w)", name, err)
}
return nil, errors.New("unable to load chart archive")
}
@ -163,7 +163,7 @@ func LoadArchive(in io.Reader) (chart.Charter, error) {
files, err := archive.LoadArchiveFiles(in)
if err != nil {
if err == gzip.ErrHeader {
if errors.Is(err, gzip.ErrHeader) {
return nil, fmt.Errorf("stream does not appear to be a valid chart file (details: %w)", err)
}
return nil, fmt.Errorf("unable to load chart archive: %w", err)

@ -179,16 +179,6 @@ func TestHelmCreateChart(t *testing.T) {
//
// Resources like hpa and ingress, which are disabled by default in values.yaml are enabled here using the equivalent
// of the `--set` flag.
//
// Note: This test requires the following ldflags to be set per the current Kubernetes version to avoid false-positive
// results.
// 1. -X helm.sh/helm/v4/pkg/lint/rules.k8sVersionMajor=<k8s-major-version>
// 2. -X helm.sh/helm/v4/pkg/lint/rules.k8sVersionMinor=<k8s-minor-version>
// or directly use '$(LDFLAGS)' in Makefile.
//
// When run without ldflags, the test passes giving a false-positive result. This is because the variables
// `k8sVersionMajor` and `k8sVersionMinor` by default are set to an older version of Kubernetes, with which, there
// might not be the deprecation warning.
func TestHelmCreateChart_CheckDeprecatedWarnings(t *testing.T) {
createdChart, err := chartutil.Create("checkdeprecatedwarnings", t.TempDir())
if err != nil {

@ -70,7 +70,7 @@ func Crds(linter *support.Linter) {
var yamlStruct *k8sYamlStruct
err := decoder.Decode(&yamlStruct)
if err == io.EOF {
if errors.Is(err, io.EOF) {
break
}

@ -28,15 +28,7 @@ import (
kscheme "k8s.io/client-go/kubernetes/scheme"
)
var (
// This should be set in the Makefile based on the version of client-go being imported.
// These constants will be overwritten with LDFLAGS. The version components must be
// strings in order for LDFLAGS to set them.
k8sVersionMajor = "1"
k8sVersionMinor = "20"
)
// deprecatedAPIError indicates that an API is deprecated in Kubernetes
// deprecatedAPIError indicates than an API is deprecated in Kubernetes
type deprecatedAPIError struct {
Deprecated string
Message string
@ -56,12 +48,8 @@ func validateNoDeprecations(resource *k8sYamlStruct, kubeVersion *common.KubeVer
return nil
}
majorVersion := k8sVersionMajor
minorVersion := k8sVersionMinor
if kubeVersion != nil {
majorVersion = kubeVersion.Major
minorVersion = kubeVersion.Minor
if kubeVersion == nil {
kubeVersion = &common.DefaultCapabilities.KubeVersion
}
runtimeObject, err := resourceToRuntimeObject(resource)
@ -73,16 +61,16 @@ func validateNoDeprecations(resource *k8sYamlStruct, kubeVersion *common.KubeVer
return err
}
major, err := strconv.Atoi(majorVersion)
kubeVersionMajor, err := strconv.Atoi(kubeVersion.Major)
if err != nil {
return err
}
minor, err := strconv.Atoi(minorVersion)
kubeVersionMinor, err := strconv.Atoi(kubeVersion.Minor)
if err != nil {
return err
}
if !deprecation.IsDeprecated(runtimeObject, major, minor) {
if !deprecation.IsDeprecated(runtimeObject, kubeVersionMajor, kubeVersionMinor) {
return nil
}
gvk := fmt.Sprintf("%s %s", resource.APIVersion, resource.Kind)

@ -180,7 +180,7 @@ func (t *templateLinter) Lint() {
var yamlStruct *k8sYamlStruct
err := decoder.Decode(&yamlStruct)
if err == io.EOF {
if errors.Is(err, io.EOF) {
break
}

@ -56,8 +56,8 @@ func LoadFile(name string) (*chart.Chart, error) {
c, err := LoadArchive(raw)
if err != nil {
if err == gzip.ErrHeader {
return nil, fmt.Errorf("file '%s' does not appear to be a valid chart file (details: %s)", name, err)
if errors.Is(err, gzip.ErrHeader) {
return nil, fmt.Errorf("file '%s' does not appear to be a valid chart file (details: %w)", name, err)
}
}
return c, err

@ -216,7 +216,7 @@ func LoadValues(data io.Reader) (map[string]interface{}, error) {
currentMap := map[string]interface{}{}
raw, err := reader.Read()
if err != nil {
if err == io.EOF {
if errors.Is(err, io.EOF) {
break
}
return nil, fmt.Errorf("error reading yaml document: %w", err)

@ -20,6 +20,7 @@ import (
"archive/tar"
"bytes"
"compress/gzip"
"errors"
"io"
"log"
"os"
@ -116,7 +117,7 @@ func TestBomTestData(t *testing.T) {
tr := tar.NewReader(unzipped)
for {
file, err := tr.Next()
if err == io.EOF {
if errors.Is(err, io.EOF) {
break
}
if err != nil {

@ -21,6 +21,7 @@ import (
"bytes"
"compress/gzip"
"crypto/sha256"
"errors"
"fmt"
"io"
"os"
@ -205,7 +206,7 @@ func retrieveAllHeadersFromTar(path string) ([]*tar.Header, error) {
headers := []*tar.Header{}
for {
hd, err := tr.Next()
if err == io.EOF {
if errors.Is(err, io.EOF) {
break
}

@ -99,6 +99,7 @@ func New() *EnvSettings {
env := &EnvSettings{
namespace: os.Getenv("HELM_NAMESPACE"),
MaxHistory: envIntOr("HELM_MAX_HISTORY", defaultMaxHistory),
KubeConfig: os.Getenv("KUBECONFIG"),
KubeContext: os.Getenv("HELM_KUBECONTEXT"),
KubeToken: os.Getenv("HELM_KUBETOKEN"),
KubeAsUser: os.Getenv("HELM_KUBEASUSER"),

@ -59,7 +59,7 @@ func AddWaitFlag(cmd *cobra.Command, wait *kube.WaitStrategy) {
cmd.Flags().Var(
newWaitValue(kube.HookOnlyStrategy, wait),
"wait",
"if specified, wait until resources are ready (up to --timeout). Values: 'watcher' (default), 'hookOnly', and 'legacy'.",
"if specified, wait until resources are ready (up to --timeout). Values: 'watcher', 'hookOnly', and 'legacy'.",
)
// Sets the strategy to use the watcher strategy if `--wait` is used without an argument
cmd.Flags().Lookup("wait").NoOptDefVal = string(kube.StatusWatcherStrategy)

@ -76,7 +76,7 @@ func TestLintCmdWithKubeVersionFlag(t *testing.T) {
golden: "output/lint-chart-with-deprecated-api-strict.txt",
wantError: true,
}, {
// the test builds will use the default k8sVersionMinor const in deprecations.go and capabilities.go
// the test builds will use the kubeVersionMinorTesting const in capabilities.go
// which is "20"
name: "lint chart with deprecated api version without kube version",
cmd: fmt.Sprintf("lint %s", testChart),

@ -30,6 +30,7 @@ import (
func newRepoListCmd(out io.Writer) *cobra.Command {
var outfmt output.Format
var noHeaders bool
cmd := &cobra.Command{
Use: "list",
Aliases: []string{"ls"},
@ -46,12 +47,17 @@ func newRepoListCmd(out io.Writer) *cobra.Command {
return nil
}
return outfmt.Write(out, &repoListWriter{f.Repositories})
w := &repoListWriter{
repos: f.Repositories,
noHeaders: noHeaders,
}
return outfmt.Write(out, w)
},
}
cmd.Flags().BoolVar(&noHeaders, "no-headers", false, "suppress headers in the output")
bindOutputFlag(cmd, &outfmt)
return cmd
}
@ -61,12 +67,15 @@ type repositoryElement struct {
}
type repoListWriter struct {
repos []*repo.Entry
repos []*repo.Entry
noHeaders bool
}
func (r *repoListWriter) WriteTable(out io.Writer) error {
table := uitable.New()
table.AddRow("NAME", "URL")
if !r.noHeaders {
table.AddRow("NAME", "URL")
}
for _, re := range r.repos {
table.AddRow(re.Name, re.URL)
}
@ -94,11 +103,11 @@ func (r *repoListWriter) encodeByFormat(out io.Writer, format output.Format) err
return output.EncodeJSON(out, repolist)
case output.YAML:
return output.EncodeYAML(out, repolist)
default:
// Because this is a non-exported function and only called internally by
// WriteJSON and WriteYAML, we shouldn't get invalid types
return nil
}
// Because this is a non-exported function and only called internally by
// WriteJSON and WriteYAML, we shouldn't get invalid types
return nil
}
// Returns all repos from repos, except those with names matching ignoredRepoNames

@ -48,6 +48,12 @@ func TestRepoList(t *testing.T) {
golden: "output/repo-list.txt",
wantError: false,
},
{
name: "list without headers",
cmd: fmt.Sprintf("repo list --repository-config %s --repository-cache %s --no-headers", repoFile2, rootDir),
golden: "output/repo-list-no-headers.txt",
wantError: false,
},
}
runTestCmd(t, tests)

@ -190,9 +190,10 @@ func (h *hubSearchWriter) encodeByFormat(out io.Writer, format output.Format) er
return output.EncodeJSON(out, chartList)
case output.YAML:
return output.EncodeYAML(out, chartList)
default:
// Because this is a non-exported function and only called internally by
// WriteJSON and WriteYAML, we shouldn't get invalid types
return nil
}
// Because this is a non-exported function and only called internally by
// WriteJSON and WriteYAML, we shouldn't get invalid types
return nil
}

@ -260,11 +260,11 @@ func (r *repoSearchWriter) encodeByFormat(out io.Writer, format output.Format) e
return output.EncodeJSON(out, chartList)
case output.YAML:
return output.EncodeYAML(out, chartList)
default:
// Because this is a non-exported function and only called internally by
// WriteJSON and WriteYAML, we shouldn't get invalid types
return nil
}
// Because this is a non-exported function and only called internally by
// WriteJSON and WriteYAML, we shouldn't get invalid types
return nil
}
// Provides the list of charts that are part of the specified repo, and that starts with 'prefix'.

@ -0,0 +1,3 @@
charts https://charts.helm.sh/stable
firstexample http://firstexample.com
secondexample http://secondexample.com

@ -1 +1 @@
version.BuildInfo{Version:"v4.1", GitCommit:"", GitTreeState:"", GoVersion:"", KubeClientVersion:"v."}
version.BuildInfo{Version:"v4.1", GitCommit:"", GitTreeState:"", GoVersion:"", KubeClientVersion:"v1.20"}

@ -153,6 +153,8 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
instClient.EnableDNS = client.EnableDNS
instClient.HideSecret = client.HideSecret
instClient.TakeOwnership = client.TakeOwnership
instClient.ForceConflicts = client.ForceConflicts
instClient.ServerSideApply = client.ServerSideApply != "false"
if isReleaseUninstalled(versions) {
instClient.Replace = true

@ -605,3 +605,58 @@ func TestUpgradeWithDryRun(t *testing.T) {
t.Error("expected error when --hide-secret used without --dry-run")
}
}
func TestUpgradeInstallServerSideApply(t *testing.T) {
_, _, chartPath := prepareMockRelease(t, "ssa-test")
defer resetEnv()()
tests := []struct {
name string
serverSideFlag string
expectedApplyMethod string
}{
{
name: "upgrade --install with --server-side=false uses client-side apply",
serverSideFlag: "--server-side=false",
expectedApplyMethod: "csa",
},
{
name: "upgrade --install with --server-side=true uses server-side apply",
serverSideFlag: "--server-side=true",
expectedApplyMethod: "ssa",
},
{
name: "upgrade --install with --server-side=auto uses server-side apply (default for new install)",
serverSideFlag: "--server-side=auto",
expectedApplyMethod: "ssa",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
store := storageFixture()
releaseName := fmt.Sprintf("ssa-test-%s", tt.expectedApplyMethod)
cmd := fmt.Sprintf("upgrade %s --install %s '%s'", releaseName, tt.serverSideFlag, chartPath)
_, _, err := executeActionCommandC(store, cmd)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
rel, err := store.Get(releaseName, 1)
if err != nil {
t.Fatalf("unexpected error getting release: %v", err)
}
relV1, err := releaserToV1Release(rel)
if err != nil {
t.Fatalf("unexpected error converting release: %v", err)
}
if relV1.ApplyMethod != tt.expectedApplyMethod {
t.Errorf("expected ApplyMethod %q, got %q", tt.expectedApplyMethod, relV1.ApplyMethod)
}
})
}
}

@ -923,7 +923,7 @@ func TestGetPodList(t *testing.T) {
responsePodList.Items = append(responsePodList.Items, newPodWithStatus(name, v1.PodStatus{}, namespace))
}
kubeClient := k8sfake.NewSimpleClientset(&responsePodList)
kubeClient := k8sfake.NewClientset(&responsePodList)
c := Client{Namespace: namespace, kubeClient: kubeClient}
podList, err := c.GetPodList(namespace, metav1.ListOptions{})
@ -936,7 +936,7 @@ func TestOutputContainerLogsForPodList(t *testing.T) {
namespace := "some-namespace"
somePodList := newPodList("jimmy", "three", "structs")
kubeClient := k8sfake.NewSimpleClientset(&somePodList)
kubeClient := k8sfake.NewClientset(&somePodList)
c := Client{Namespace: namespace, kubeClient: kubeClient}
outBuffer := &bytes.Buffer{}
outBufferFunc := func(_, _, _ string) io.Writer { return outBuffer }
@ -1314,7 +1314,7 @@ func TestIsReachable(t *testing.T) {
setupClient: func(t *testing.T) *Client {
t.Helper()
client := newTestClient(t)
client.kubeClient = k8sfake.NewSimpleClientset()
client.kubeClient = k8sfake.NewClientset()
return client
},
expectError: false,

@ -146,6 +146,9 @@ func (f *FailingKubeClient) BuildTable(r io.Reader, _ bool) (kube.ResourceList,
if f.BuildTableError != nil {
return []*resource.Info{}, f.BuildTableError
}
if f.BuildDummy {
return createDummyResourceList(), nil
}
return f.PrintingKubeClient.BuildTable(r, false)
}

@ -354,6 +354,8 @@ func (c *ReadyChecker) crdBetaReady(crd apiextv1beta1.CustomResourceDefinition)
// continue.
return true
}
default:
// intentionally left empty
}
}
return false
@ -374,6 +376,8 @@ func (c *ReadyChecker) crdReady(crd apiextv1.CustomResourceDefinition) bool {
// continue.
return true
}
default:
// intentionally left empty
}
}
return false

@ -132,7 +132,9 @@ func (w *statusWaiter) waitForDelete(ctx context.Context, resourceList ResourceL
}
resources = append(resources, obj)
}
eventCh := sw.Watch(cancelCtx, resources, watcher.Options{})
eventCh := sw.Watch(cancelCtx, resources, watcher.Options{
RESTScopeStrategy: watcher.RESTScopeNamespace,
})
statusCollector := collector.NewResourceStatusCollector(resources)
done := statusCollector.ListenWithObserver(eventCh, statusObserver(cancel, status.NotFoundStatus))
<-done
@ -175,7 +177,9 @@ func (w *statusWaiter) wait(ctx context.Context, resourceList ResourceList, sw w
resources = append(resources, obj)
}
eventCh := sw.Watch(cancelCtx, resources, watcher.Options{})
eventCh := sw.Watch(cancelCtx, resources, watcher.Options{
RESTScopeStrategy: watcher.RESTScopeNamespace,
})
statusCollector := collector.NewResourceStatusCollector(resources)
done := statusCollector.ListenWithObserver(eventCh, statusObserver(cancel, status.CurrentStatus))
<-done

@ -18,6 +18,8 @@ package kube // import "helm.sh/helm/v3/pkg/kube"
import (
"errors"
"fmt"
"strings"
"testing"
"time"
@ -27,12 +29,15 @@ import (
appsv1 "k8s.io/api/apps/v1"
batchv1 "k8s.io/api/batch/v1"
v1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/apimachinery/pkg/watch"
dynamicfake "k8s.io/client-go/dynamic/fake"
clienttesting "k8s.io/client-go/testing"
"k8s.io/kubectl/pkg/scheme"
)
@ -153,6 +158,83 @@ spec:
- containerPort: 80
`
var podNamespace1Manifest = `
apiVersion: v1
kind: Pod
metadata:
name: pod-ns1
namespace: namespace-1
status:
conditions:
- type: Ready
status: "True"
phase: Running
`
var podNamespace2Manifest = `
apiVersion: v1
kind: Pod
metadata:
name: pod-ns2
namespace: namespace-2
status:
conditions:
- type: Ready
status: "True"
phase: Running
`
var podNamespace1NoStatusManifest = `
apiVersion: v1
kind: Pod
metadata:
name: pod-ns1
namespace: namespace-1
`
var jobNamespace1CompleteManifest = `
apiVersion: batch/v1
kind: Job
metadata:
name: job-ns1
namespace: namespace-1
generation: 1
status:
succeeded: 1
active: 0
conditions:
- type: Complete
status: "True"
`
var podNamespace2SucceededManifest = `
apiVersion: v1
kind: Pod
metadata:
name: pod-ns2
namespace: namespace-2
status:
phase: Succeeded
`
var clusterRoleManifest = `
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: test-cluster-role
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
`
var namespaceManifest = `
apiVersion: v1
kind: Namespace
metadata:
name: test-namespace
`
func getGVR(t *testing.T, mapper meta.RESTMapper, obj *unstructured.Unstructured) schema.GroupVersionResource {
t.Helper()
gvk := obj.GroupVersionKind()
@ -232,11 +314,11 @@ func TestStatusWaitForDelete(t *testing.T) {
for _, objToDelete := range objsToDelete {
u := objToDelete.(*unstructured.Unstructured)
gvr := getGVR(t, fakeMapper, u)
go func() {
go func(gvr schema.GroupVersionResource, u *unstructured.Unstructured) {
time.Sleep(timeUntilPodDelete)
err := fakeClient.Tracker().Delete(gvr, u.GetNamespace(), u.GetName())
assert.NoError(t, err)
}()
}(gvr, u)
}
resourceList := getResourceListFromRuntimeObjs(t, c, objsToCreate)
err := statusWaiter.WaitForDelete(resourceList, timeout)
@ -448,3 +530,406 @@ func TestWatchForReady(t *testing.T) {
})
}
}
func TestStatusWaitMultipleNamespaces(t *testing.T) {
t.Parallel()
tests := []struct {
name string
objManifests []string
expectErrs []error
testFunc func(statusWaiter, ResourceList, time.Duration) error
}{
{
name: "pods in multiple namespaces",
objManifests: []string{podNamespace1Manifest, podNamespace2Manifest},
testFunc: func(sw statusWaiter, rl ResourceList, timeout time.Duration) error {
return sw.Wait(rl, timeout)
},
},
{
name: "hooks in multiple namespaces",
objManifests: []string{jobNamespace1CompleteManifest, podNamespace2SucceededManifest},
testFunc: func(sw statusWaiter, rl ResourceList, timeout time.Duration) error {
return sw.WatchUntilReady(rl, timeout)
},
},
{
name: "error when resource not ready in one namespace",
objManifests: []string{podNamespace1NoStatusManifest, podNamespace2Manifest},
expectErrs: []error{errors.New("resource not ready, name: pod-ns1, kind: Pod, status: InProgress"), errors.New("context deadline exceeded")},
testFunc: func(sw statusWaiter, rl ResourceList, timeout time.Duration) error {
return sw.Wait(rl, timeout)
},
},
{
name: "delete resources in multiple namespaces",
objManifests: []string{podNamespace1Manifest, podNamespace2Manifest},
testFunc: func(sw statusWaiter, rl ResourceList, timeout time.Duration) error {
return sw.WaitForDelete(rl, timeout)
},
},
{
name: "cluster-scoped resources work correctly with unrestricted permissions",
objManifests: []string{podNamespace1Manifest, clusterRoleManifest},
testFunc: func(sw statusWaiter, rl ResourceList, timeout time.Duration) error {
return sw.Wait(rl, timeout)
},
},
{
name: "namespace-scoped and cluster-scoped resources work together",
objManifests: []string{podNamespace1Manifest, podNamespace2Manifest, clusterRoleManifest},
testFunc: func(sw statusWaiter, rl ResourceList, timeout time.Duration) error {
return sw.Wait(rl, timeout)
},
},
{
name: "delete cluster-scoped resources works correctly",
objManifests: []string{podNamespace1Manifest, namespaceManifest},
testFunc: func(sw statusWaiter, rl ResourceList, timeout time.Duration) error {
return sw.WaitForDelete(rl, timeout)
},
},
{
name: "watch cluster-scoped resources works correctly",
objManifests: []string{clusterRoleManifest},
testFunc: func(sw statusWaiter, rl ResourceList, timeout time.Duration) error {
return sw.WatchUntilReady(rl, timeout)
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
c := newTestClient(t)
fakeClient := dynamicfake.NewSimpleDynamicClient(scheme.Scheme)
fakeMapper := testutil.NewFakeRESTMapper(
v1.SchemeGroupVersion.WithKind("Pod"),
batchv1.SchemeGroupVersion.WithKind("Job"),
schema.GroupVersion{Group: "rbac.authorization.k8s.io", Version: "v1"}.WithKind("ClusterRole"),
v1.SchemeGroupVersion.WithKind("Namespace"),
)
sw := statusWaiter{
client: fakeClient,
restMapper: fakeMapper,
}
objs := getRuntimeObjFromManifests(t, tt.objManifests)
for _, obj := range objs {
u := obj.(*unstructured.Unstructured)
gvr := getGVR(t, fakeMapper, u)
err := fakeClient.Tracker().Create(gvr, u, u.GetNamespace())
assert.NoError(t, err)
}
if strings.Contains(tt.name, "delete") {
timeUntilDelete := time.Millisecond * 500
for _, obj := range objs {
u := obj.(*unstructured.Unstructured)
gvr := getGVR(t, fakeMapper, u)
go func(gvr schema.GroupVersionResource, u *unstructured.Unstructured) {
time.Sleep(timeUntilDelete)
err := fakeClient.Tracker().Delete(gvr, u.GetNamespace(), u.GetName())
assert.NoError(t, err)
}(gvr, u)
}
}
resourceList := getResourceListFromRuntimeObjs(t, c, objs)
err := tt.testFunc(sw, resourceList, time.Second*3)
if tt.expectErrs != nil {
assert.EqualError(t, err, errors.Join(tt.expectErrs...).Error())
return
}
assert.NoError(t, err)
})
}
}
// restrictedClientConfig holds the configuration for RBAC simulation on a fake dynamic client
type restrictedClientConfig struct {
allowedNamespaces map[string]bool
clusterScopedListAttempted bool
}
// setupRestrictedClient configures a fake dynamic client to simulate RBAC restrictions
// by using PrependReactor and PrependWatchReactor to intercept list/watch operations.
func setupRestrictedClient(fakeClient *dynamicfake.FakeDynamicClient, allowedNamespaces []string) *restrictedClientConfig {
allowed := make(map[string]bool)
for _, ns := range allowedNamespaces {
allowed[ns] = true
}
config := &restrictedClientConfig{
allowedNamespaces: allowed,
}
// Intercept list operations
fakeClient.PrependReactor("list", "*", func(action clienttesting.Action) (bool, runtime.Object, error) {
listAction := action.(clienttesting.ListAction)
ns := listAction.GetNamespace()
if ns == "" {
// Cluster-scoped list
config.clusterScopedListAttempted = true
return true, nil, apierrors.NewForbidden(
action.GetResource().GroupResource(),
"",
fmt.Errorf("user does not have cluster-wide LIST permissions for cluster-scoped resources"),
)
}
if !config.allowedNamespaces[ns] {
return true, nil, apierrors.NewForbidden(
action.GetResource().GroupResource(),
"",
fmt.Errorf("user does not have LIST permissions in namespace %q", ns),
)
}
// Fall through to the default handler
return false, nil, nil
})
// Intercept watch operations
fakeClient.PrependWatchReactor("*", func(action clienttesting.Action) (bool, watch.Interface, error) {
watchAction := action.(clienttesting.WatchAction)
ns := watchAction.GetNamespace()
if ns == "" {
// Cluster-scoped watch
config.clusterScopedListAttempted = true
return true, nil, apierrors.NewForbidden(
action.GetResource().GroupResource(),
"",
fmt.Errorf("user does not have cluster-wide WATCH permissions for cluster-scoped resources"),
)
}
if !config.allowedNamespaces[ns] {
return true, nil, apierrors.NewForbidden(
action.GetResource().GroupResource(),
"",
fmt.Errorf("user does not have WATCH permissions in namespace %q", ns),
)
}
// Fall through to the default handler
return false, nil, nil
})
return config
}
func TestStatusWaitRestrictedRBAC(t *testing.T) {
t.Parallel()
tests := []struct {
name string
objManifests []string
allowedNamespaces []string
expectErrs []error
testFunc func(statusWaiter, ResourceList, time.Duration) error
}{
{
name: "pods in multiple namespaces with namespace permissions",
objManifests: []string{podNamespace1Manifest, podNamespace2Manifest},
allowedNamespaces: []string{"namespace-1", "namespace-2"},
testFunc: func(sw statusWaiter, rl ResourceList, timeout time.Duration) error {
return sw.Wait(rl, timeout)
},
},
{
name: "delete pods in multiple namespaces with namespace permissions",
objManifests: []string{podNamespace1Manifest, podNamespace2Manifest},
allowedNamespaces: []string{"namespace-1", "namespace-2"},
testFunc: func(sw statusWaiter, rl ResourceList, timeout time.Duration) error {
return sw.WaitForDelete(rl, timeout)
},
},
{
name: "hooks in multiple namespaces with namespace permissions",
objManifests: []string{jobNamespace1CompleteManifest, podNamespace2SucceededManifest},
allowedNamespaces: []string{"namespace-1", "namespace-2"},
testFunc: func(sw statusWaiter, rl ResourceList, timeout time.Duration) error {
return sw.WatchUntilReady(rl, timeout)
},
},
{
name: "error when cluster-scoped resource included",
objManifests: []string{podNamespace1Manifest, clusterRoleManifest},
allowedNamespaces: []string{"namespace-1"},
expectErrs: []error{fmt.Errorf("user does not have cluster-wide LIST permissions for cluster-scoped resources")},
testFunc: func(sw statusWaiter, rl ResourceList, timeout time.Duration) error {
return sw.Wait(rl, timeout)
},
},
{
name: "error when deleting cluster-scoped resource",
objManifests: []string{podNamespace1Manifest, namespaceManifest},
allowedNamespaces: []string{"namespace-1"},
expectErrs: []error{fmt.Errorf("user does not have cluster-wide LIST permissions for cluster-scoped resources")},
testFunc: func(sw statusWaiter, rl ResourceList, timeout time.Duration) error {
return sw.WaitForDelete(rl, timeout)
},
},
{
name: "error when accessing disallowed namespace",
objManifests: []string{podNamespace1Manifest, podNamespace2Manifest},
allowedNamespaces: []string{"namespace-1"},
expectErrs: []error{fmt.Errorf("user does not have LIST permissions in namespace %q", "namespace-2")},
testFunc: func(sw statusWaiter, rl ResourceList, timeout time.Duration) error {
return sw.Wait(rl, timeout)
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
c := newTestClient(t)
baseFakeClient := dynamicfake.NewSimpleDynamicClient(scheme.Scheme)
fakeMapper := testutil.NewFakeRESTMapper(
v1.SchemeGroupVersion.WithKind("Pod"),
batchv1.SchemeGroupVersion.WithKind("Job"),
schema.GroupVersion{Group: "rbac.authorization.k8s.io", Version: "v1"}.WithKind("ClusterRole"),
v1.SchemeGroupVersion.WithKind("Namespace"),
)
restrictedConfig := setupRestrictedClient(baseFakeClient, tt.allowedNamespaces)
sw := statusWaiter{
client: baseFakeClient,
restMapper: fakeMapper,
}
objs := getRuntimeObjFromManifests(t, tt.objManifests)
for _, obj := range objs {
u := obj.(*unstructured.Unstructured)
gvr := getGVR(t, fakeMapper, u)
err := baseFakeClient.Tracker().Create(gvr, u, u.GetNamespace())
assert.NoError(t, err)
}
if strings.Contains(tt.name, "delet") {
timeUntilDelete := time.Millisecond * 500
for _, obj := range objs {
u := obj.(*unstructured.Unstructured)
gvr := getGVR(t, fakeMapper, u)
go func(gvr schema.GroupVersionResource, u *unstructured.Unstructured) {
time.Sleep(timeUntilDelete)
err := baseFakeClient.Tracker().Delete(gvr, u.GetNamespace(), u.GetName())
assert.NoError(t, err)
}(gvr, u)
}
}
resourceList := getResourceListFromRuntimeObjs(t, c, objs)
err := tt.testFunc(sw, resourceList, time.Second*3)
if tt.expectErrs != nil {
require.Error(t, err)
for _, expectedErr := range tt.expectErrs {
assert.Contains(t, err.Error(), expectedErr.Error())
}
return
}
assert.NoError(t, err)
assert.False(t, restrictedConfig.clusterScopedListAttempted)
})
}
}
func TestStatusWaitMixedResources(t *testing.T) {
t.Parallel()
tests := []struct {
name string
objManifests []string
allowedNamespaces []string
expectErrs []error
testFunc func(statusWaiter, ResourceList, time.Duration) error
}{
{
name: "wait succeeds with namespace-scoped resources only",
objManifests: []string{podNamespace1Manifest, podNamespace2Manifest},
allowedNamespaces: []string{"namespace-1", "namespace-2"},
testFunc: func(sw statusWaiter, rl ResourceList, timeout time.Duration) error {
return sw.Wait(rl, timeout)
},
},
{
name: "wait fails when cluster-scoped resource included",
objManifests: []string{podNamespace1Manifest, clusterRoleManifest},
allowedNamespaces: []string{"namespace-1"},
expectErrs: []error{fmt.Errorf("user does not have cluster-wide LIST permissions for cluster-scoped resources")},
testFunc: func(sw statusWaiter, rl ResourceList, timeout time.Duration) error {
return sw.Wait(rl, timeout)
},
},
{
name: "waitForDelete fails when cluster-scoped resource included",
objManifests: []string{podNamespace1Manifest, clusterRoleManifest},
allowedNamespaces: []string{"namespace-1"},
expectErrs: []error{fmt.Errorf("user does not have cluster-wide LIST permissions for cluster-scoped resources")},
testFunc: func(sw statusWaiter, rl ResourceList, timeout time.Duration) error {
return sw.WaitForDelete(rl, timeout)
},
},
{
name: "wait fails when namespace resource included",
objManifests: []string{podNamespace1Manifest, namespaceManifest},
allowedNamespaces: []string{"namespace-1"},
expectErrs: []error{fmt.Errorf("user does not have cluster-wide LIST permissions for cluster-scoped resources")},
testFunc: func(sw statusWaiter, rl ResourceList, timeout time.Duration) error {
return sw.Wait(rl, timeout)
},
},
{
name: "error when accessing disallowed namespace",
objManifests: []string{podNamespace1Manifest, podNamespace2Manifest},
allowedNamespaces: []string{"namespace-1"},
expectErrs: []error{fmt.Errorf("user does not have LIST permissions in namespace %q", "namespace-2")},
testFunc: func(sw statusWaiter, rl ResourceList, timeout time.Duration) error {
return sw.Wait(rl, timeout)
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
c := newTestClient(t)
baseFakeClient := dynamicfake.NewSimpleDynamicClient(scheme.Scheme)
fakeMapper := testutil.NewFakeRESTMapper(
v1.SchemeGroupVersion.WithKind("Pod"),
batchv1.SchemeGroupVersion.WithKind("Job"),
schema.GroupVersion{Group: "rbac.authorization.k8s.io", Version: "v1"}.WithKind("ClusterRole"),
v1.SchemeGroupVersion.WithKind("Namespace"),
)
restrictedConfig := setupRestrictedClient(baseFakeClient, tt.allowedNamespaces)
sw := statusWaiter{
client: baseFakeClient,
restMapper: fakeMapper,
}
objs := getRuntimeObjFromManifests(t, tt.objManifests)
for _, obj := range objs {
u := obj.(*unstructured.Unstructured)
gvr := getGVR(t, fakeMapper, u)
err := baseFakeClient.Tracker().Create(gvr, u, u.GetNamespace())
assert.NoError(t, err)
}
if strings.Contains(tt.name, "delet") {
timeUntilDelete := time.Millisecond * 500
for _, obj := range objs {
u := obj.(*unstructured.Unstructured)
gvr := getGVR(t, fakeMapper, u)
go func(gvr schema.GroupVersionResource, u *unstructured.Unstructured) {
time.Sleep(timeUntilDelete)
err := baseFakeClient.Tracker().Delete(gvr, u.GetNamespace(), u.GetName())
assert.NoError(t, err)
}(gvr, u)
}
}
resourceList := getResourceListFromRuntimeObjs(t, c, objs)
err := tt.testFunc(sw, resourceList, time.Second*3)
if tt.expectErrs != nil {
require.Error(t, err)
for _, expectedErr := range tt.expectErrs {
assert.Contains(t, err.Error(), expectedErr.Error())
}
return
}
assert.NoError(t, err)
assert.False(t, restrictedConfig.clusterScopedListAttempted)
})
}
}

@ -333,6 +333,8 @@ func (hw *legacyWaiter) waitForPodSuccess(obj runtime.Object, name string) (bool
slog.Debug("pod pending", "pod", o.Name)
case corev1.PodRunning:
slog.Debug("pod running", "pod", o.Name)
case corev1.PodUnknown:
slog.Debug("pod unknown", "pod", o.Name)
}
return false, nil

@ -94,7 +94,7 @@ func TestIndexCustomSchemeDownload(t *testing.T) {
}
}
func TestConcurrenyDownloadIndex(t *testing.T) {
func TestConcurrencyDownloadIndex(t *testing.T) {
srv, err := startLocalServerForTests(nil)
if err != nil {
t.Fatal(err)

@ -17,6 +17,7 @@ package strvals
import (
"bytes"
"errors"
"fmt"
"io"
"strconv"
@ -66,7 +67,7 @@ func (t *literalParser) parse() error {
if err == nil {
continue
}
if err == io.EOF {
if errors.Is(err, io.EOF) {
return nil
}
return err
@ -183,7 +184,7 @@ func (t *literalParser) listItem(list []interface{}, i, nestedNameLevel int) ([]
case lastRune == '=':
value, err := t.val()
if err != nil && err != io.EOF {
if err != nil && !errors.Is(err, io.EOF) {
return list, err
}
return setIndex(list, i, string(value))

@ -161,7 +161,7 @@ func (t *parser) parse() error {
if err == nil {
continue
}
if err == io.EOF {
if errors.Is(err, io.EOF) {
return nil
}
return err

Loading…
Cancel
Save