pull/32211/merge
Md Mushfiqur Rahim 4 months ago committed by GitHub
commit a91892cbed
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -48,7 +48,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # pinv4.36.2
uses: github/codeql-action/init@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # pinv4.36.0
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@ -59,7 +59,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # pinv4.36.2
uses: github/codeql-action/autobuild@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # pinv4.36.0
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@ -73,4 +73,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # pinv4.36.2
uses: github/codeql-action/analyze@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # pinv4.36.0

@ -64,6 +64,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard (optional).
# Commenting out will disable upload of results to your repo's Code Scanning dashboard
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
uses: github/codeql-action/upload-sarif@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0
with:
sarif_file: results.sarif

@ -19,12 +19,9 @@ linters:
# Keep sorted alphabetically
enable:
- bidichk
- depguard
- dupl
- errorlint
- exhaustive
- gocritic
- gomodguard
- govet
- ineffassign
@ -41,7 +38,6 @@ linters:
- unused
- usestdlibvars
- usetesting
- whitespace
exclusions:
@ -69,57 +65,9 @@ linters:
dupl:
threshold: 400
errorlint:
# Check for plain type assertions and type switches.
asserts: false
# Check for plain error comparisons.
comparison: false
exhaustive:
default-signifies-exhaustive: true
gocritic:
disabled-checks:
- appendAssign
- appendCombine
- assignOp
- badCall
- commentedOutCode
- commentFormatting
- deferInLoop
- elseif
- emptyStringTest
- exposedSyncMutex
- filepathJoin
- httpNoBody
- hugeParam
- ifElseChain
- importShadow
- nestingReduce
- nilValReturn
- octalLiteral
- paramTypeCombine
- preferStringWriter
- ptrToRefParam
- rangeValCopy
- regexpSimplify
- singleCaseSwitch
- sloppyReassign
- sloppyLen
- sprintfQuotedString
- stringConcatSimplify
- stringsCompare
- stringXbytes
- todoCommentWithoutDetail
- tooManyResultsChecker
- typeAssertChain
- uncheckedInlineErr
- unnamedResult
- unnecessaryDefer
- unslice
- whyNoLint
enable-all: true
gomodguard:
blocked:
modules:
@ -153,6 +101,8 @@ linters:
- empty
- encoded-compare
- equal-values
- error-is-as
- error-nil
- expected-actual
- float-compare
- go-require

@ -33,11 +33,11 @@ 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.12.0
github.com/tetratelabs/wazero v1.11.0
go.yaml.in/yaml/v3 v3.0.4
golang.org/x/crypto v0.53.0
golang.org/x/term v0.44.0
golang.org/x/text v0.38.0
golang.org/x/crypto v0.51.0
golang.org/x/term v0.43.0
golang.org/x/text v0.37.0
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/api v0.36.1
k8s.io/apiextensions-apiserver v0.36.1
@ -47,7 +47,7 @@ require (
k8s.io/client-go v0.36.1
k8s.io/klog/v2 v2.140.0
k8s.io/kubectl v0.36.1
oras.land/oras-go/v2 v2.6.1
oras.land/oras-go/v2 v2.6.0
sigs.k8s.io/controller-runtime v0.24.1
sigs.k8s.io/kustomize/kyaml v0.21.1
sigs.k8s.io/yaml v1.6.0
@ -157,13 +157,13 @@ require (
go.opentelemetry.io/otel/trace v1.43.0 // indirect
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
golang.org/x/mod v0.36.0 // indirect
golang.org/x/net v0.55.0 // indirect
golang.org/x/mod v0.35.0 // indirect
golang.org/x/net v0.53.0 // indirect
golang.org/x/oauth2 v0.36.0 // indirect
golang.org/x/sync v0.21.0 // indirect
golang.org/x/sys v0.46.0 // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/sys v0.44.0 // indirect
golang.org/x/time v0.15.0 // indirect
golang.org/x/tools v0.45.0 // indirect
golang.org/x/tools v0.44.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect
google.golang.org/grpc v1.80.0 // indirect

@ -309,8 +309,8 @@ 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.12.0 h1:DuWcpNu/FzgEXgGBDp8J1Spc+CWOvvtvVyjKlaZopYU=
github.com/tetratelabs/wazero v1.12.0/go.mod h1:LvKtzl2RqO4gyF27BiXU+nKAjcV8f38U+kP/q2vgxh0=
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=
@ -382,14 +382,14 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI=
golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
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=
@ -400,8 +400,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -412,8 +412,8 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
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=
@ -430,8 +430,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@ -439,8 +439,8 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
@ -448,8 +448,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -458,8 +458,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8=
golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0=
golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c=
golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
@ -507,8 +507,8 @@ k8s.io/kubectl v0.36.1 h1:96HqS9twIdHM0MlJLTwbo14b9kUKPkOzZ4tlRDLv4qI=
k8s.io/kubectl v0.36.1/go.mod h1:/DGPAIewKsFWF9VFgGvkPhao2Ev4SNuE3BioZo8yPbk=
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU=
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
oras.land/oras-go/v2 v2.6.1 h1:bonOEkjLfp8tt6qXWRRWP6p1F+9octchOf2EqnWB4Zs=
oras.land/oras-go/v2 v2.6.1/go.mod h1:dhtFrFOuZuDtAVeZ9FUnaa5zfzplG3ZnFX9/uH1J/Yk=
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.24.1 h1:miPEwrmirImAvgME1L9qebGHrOnGJoVmVdtOU9fRfo4=
sigs.k8s.io/controller-runtime v0.24.1/go.mod h1:vFkfY5fGt5xAC/sKb8IBFKgWPNKG9OUG29dR8Y2wImw=
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=

@ -140,7 +140,6 @@ func (ch *Chart) AppVersion() string {
}
// CRDs returns a list of File objects in the 'crds/' directory of a Helm chart.
//
// Deprecated: use CRDObjects()
func (ch *Chart) CRDs() []*common.File {
files := []*common.File{}

@ -104,7 +104,7 @@ func TestMetadata(t *testing.T) {
is.Equal("foo.yaml", chrt.Name())
is.Equal("1.0.0", chrt.AppVersion())
is.NoError(chrt.Validate())
is.Equal(nil, chrt.Validate())
}
func TestIsRoot(t *testing.T) {

@ -44,6 +44,7 @@ func WithSkipSchemaValidation(skipSchemaValidation bool) LinterOption {
}
func RunAll(baseDir string, values map[string]any, namespace string, options ...LinterOption) support.Linter {
chartDir, _ := filepath.Abs(baseDir)
lo := linterOptions{}

@ -242,6 +242,7 @@ data:
//
// See https://github.com/helm/helm/issues/7483
func TestStrictTemplateParsingMapError(t *testing.T) {
ch := chart.Chart{
Metadata: &chart.Metadata{
Name: "regression7483",
@ -370,6 +371,7 @@ func TestValidateTopIndentLevel(t *testing.T) {
t.Errorf("Expected %t for %q", shouldFail, doc)
}
}
}
// TestEmptyWithCommentsManifests checks the lint is not failing against empty manifests that contains only comments
@ -461,4 +463,5 @@ func TestIsYamlFileExtension(t *testing.T) {
t.Errorf("isYamlFileExtension(%s) = %v; want %v", test.filename, result, test.expected)
}
}
}

@ -337,6 +337,7 @@ icon: https://example.com/64x64.png
if text.String() != "" {
t.Errorf("Expected no message to Stderr, got %s", text.String())
}
}
// Packaging the chart on a Windows machine will produce an
@ -606,6 +607,7 @@ func verifyChart(t *testing.T, c *chart.Chart) {
t.Errorf("Expected %s version %s, got %s", dep.Name(), exp["version"], dep.Metadata.Version)
}
}
}
func verifyDependencies(t *testing.T, c *chart.Chart) {

@ -702,6 +702,7 @@ func CreateFrom(chartfile *chart.Metadata, dest, src string) error {
// error. In such a case, this will attempt to clean up by removing the
// new chart directory.
func Create(name, dir string) (string, error) {
// Sanity-check the name of a chart so user doesn't create one that causes problems.
if err := validateChartName(name); err != nil {
return "", err

@ -459,6 +459,7 @@ func TestDependentChartAliases(t *testing.T) {
if aliasChart := getAliasDependency(c.Dependencies(), req[2]); aliasChart != nil {
t.Fatalf("expected no chart but got %s", aliasChart.Name())
}
}
func TestDependentChartWithSubChartsAbsentInDependency(t *testing.T) {

@ -79,6 +79,7 @@ func ValidateReleaseName(name string) error {
// This case is preserved for backwards compatibility
if name == "" {
return errMissingName
}
if len(name) > maxReleaseNameLen || !validName.MatchString(name) {
return errInvalidName

@ -24,6 +24,7 @@ import (
)
func TestColorizeStatus(t *testing.T) {
tests := []struct {
name string
status common.Status
@ -106,6 +107,7 @@ func TestColorizeStatus(t *testing.T) {
}
func TestColorizeHeader(t *testing.T) {
tests := []struct {
name string
header string
@ -147,6 +149,7 @@ func TestColorizeHeader(t *testing.T) {
}
func TestColorizeNamespace(t *testing.T) {
tests := []struct {
name string
namespace string

@ -33,6 +33,7 @@ type Client struct {
// New creates a new client
func New(u string) (*Client, error) {
// Validate we have a URL
if err := validate(u); err != nil {
return nil, err
@ -45,6 +46,7 @@ func New(u string) (*Client, error) {
// Validate if the base URL for monocular is valid.
func validate(u string) error {
// Check if it is parsable
p, err := url.Parse(u)
if err != nil {

@ -99,6 +99,7 @@ type ChartVersion struct {
// Search performs a search against the monocular search API
func (c *Client) Search(term string) ([]SearchResult, error) {
// Create the URL to the search endpoint
// Note, this is currently an internal API for the Hub. This should be
// formatted without showing how monocular operates.

@ -27,6 +27,7 @@ import (
var searchResult = `{"data":[{"id":"stable/phpmyadmin","type":"chart","attributes":{"name":"phpmyadmin","repo":{"name":"stable","url":"https://charts.helm.sh/stable"},"description":"phpMyAdmin is an mysql administration frontend","home":"https://www.phpmyadmin.net/","keywords":["mariadb","mysql","phpmyadmin"],"maintainers":[{"name":"Bitnami","email":"containers@bitnami.com"}],"sources":["https://github.com/bitnami/bitnami-docker-phpmyadmin"],"icon":""},"links":{"self":"/v1/charts/stable/phpmyadmin"},"relationships":{"latestChartVersion":{"data":{"version":"3.0.0","app_version":"4.9.0-1","created":"2019-08-08T17:57:31.38Z","digest":"119c499251bffd4b06ff0cd5ac98c2ce32231f84899fb4825be6c2d90971c742","urls":["https://charts.helm.sh/stable/phpmyadmin-3.0.0.tgz"],"readme":"/v1/assets/stable/phpmyadmin/versions/3.0.0/README.md","values":"/v1/assets/stable/phpmyadmin/versions/3.0.0/values.yaml"},"links":{"self":"/v1/charts/stable/phpmyadmin/versions/3.0.0"}}}},{"id":"bitnami/phpmyadmin","type":"chart","attributes":{"name":"phpmyadmin","repo":{"name":"bitnami","url":"https://charts.bitnami.com"},"description":"phpMyAdmin is an mysql administration frontend","home":"https://www.phpmyadmin.net/","keywords":["mariadb","mysql","phpmyadmin"],"maintainers":[{"name":"Bitnami","email":"containers@bitnami.com"}],"sources":["https://github.com/bitnami/bitnami-docker-phpmyadmin"],"icon":""},"links":{"self":"/v1/charts/bitnami/phpmyadmin"},"relationships":{"latestChartVersion":{"data":{"version":"3.0.0","app_version":"4.9.0-1","created":"2019-08-08T18:34:13.341Z","digest":"66d77cf6d8c2b52c488d0a294cd4996bd5bad8dc41d3829c394498fb401c008a","urls":["https://charts.bitnami.com/bitnami/phpmyadmin-3.0.0.tgz"],"readme":"/v1/assets/bitnami/phpmyadmin/versions/3.0.0/README.md","values":"/v1/assets/bitnami/phpmyadmin/versions/3.0.0/values.yaml"},"links":{"self":"/v1/charts/bitnami/phpmyadmin/versions/3.0.0"}}}}]}`
func TestSearch(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
fmt.Fprintln(w, searchResult)
}))

@ -35,6 +35,7 @@ func TestPath(t *testing.T) {
}
for _, tt := range tests {
t.Setenv("HELM_PLUGINS", tt.helmPluginsDir)
baseIns := newBase(tt.source)
baseInsPath := baseIns.Path()

@ -90,6 +90,7 @@ func NewExtractor(source string) (Extractor, error) {
// - Beginning a path with a path separator is illegal
// - Rudimentary symlink protections are offered by SecureJoin.
func cleanJoin(root, dest string) (string, error) {
// On Windows, this is a drive separator. On UNIX-like, this is the path list separator.
// In neither case do we want to trust a TAR that contains these.
if strings.Contains(dest, ":") {

@ -124,6 +124,7 @@ func TestHTTPInstaller(t *testing.T) {
} else if err.Error() != "plugin already exists" {
t.Fatalf("expected error for plugin exists, got (%v)", err)
}
}
func TestHTTPInstallerNonExistentVersion(t *testing.T) {
@ -156,6 +157,7 @@ func TestHTTPInstallerNonExistentVersion(t *testing.T) {
if err := Install(i); err == nil {
t.Fatal("expected error from http client")
}
}
func TestHTTPInstallerUpdate(t *testing.T) {
@ -295,6 +297,7 @@ func TestExtract(t *testing.T) {
t.Fatalf("Expected %s to have %o mode but has %o (umask: %o)",
readmeFullPath, expectedReadmePerm, info.Mode().Perm(), currentUmask)
}
}
func TestCleanJoin(t *testing.T) {
@ -324,9 +327,11 @@ func TestCleanJoin(t *testing.T) {
t.Errorf("Test %d: Expected %q but got %q", i, fixture.expect, out)
}
}
}
func TestMediaTypeToExtension(t *testing.T) {
for mt, shouldPass := range map[string]bool{
"": false,
"application/gzip": true,

@ -72,6 +72,7 @@ type VerificationResult struct {
// InstallWithOptions installs a plugin with options.
func InstallWithOptions(i Installer, opts Options) (*VerificationResult, error) {
if err := os.MkdirAll(filepath.Dir(i.Path()), 0755); err != nil {
return nil, err
}

@ -185,4 +185,5 @@ func TestVCSInstallerUpdate(t *testing.T) {
} else if err.Error() != "plugin repo was modified" {
t.Fatalf("expected error for plugin modified, got (%v)", err)
}
}

@ -45,6 +45,7 @@ func peekAPIVersion(r io.Reader) (string, error) {
}
func loadMetadataLegacy(metadataData []byte) (*Metadata, error) {
var ml MetadataLegacy
d := yaml.NewDecoder(bytes.NewReader(metadataData))
// NOTE: No strict unmarshalling for legacy plugins - maintain backwards compatibility
@ -64,6 +65,7 @@ func loadMetadataLegacy(metadataData []byte) (*Metadata, error) {
}
func loadMetadataV1(metadataData []byte) (*Metadata, error) {
var mv1 MetadataV1
d := yaml.NewDecoder(bytes.NewReader(metadataData))
d.KnownFields(true)
@ -107,6 +109,7 @@ type prototypePluginManager struct {
}
func newPrototypePluginManager() (*prototypePluginManager, error) {
cc, err := wazero.NewCompilationCacheWithDir(helmpath.CachePath("wazero-build"))
if err != nil {
return nil, fmt.Errorf("failed to create wazero compilation cache: %w", err)
@ -226,6 +229,7 @@ func findPlugins(pluginsDirs []string, findFn findFunc, filterFn filterFunc) ([]
found = append(found, p)
}
}
}
return found, nil
@ -238,6 +242,7 @@ func makeDescriptorFilter(descriptor Descriptor) filterFunc {
// If name is specified, it must match
if descriptor.Name != "" && p.Metadata().Name != descriptor.Name {
return false
}
// If type is specified, it must match
if descriptor.Type != "" && p.Metadata().Type != descriptor.Type {

@ -62,6 +62,7 @@ name: "test-plugin"
}
func TestLoadDir(t *testing.T) {
makeMetadata := func(apiVersion string) Metadata {
usage := "hello [params]..."
if apiVersion == "legacy" {

@ -50,8 +50,7 @@ type MetadataLegacy struct {
PlatformCommand []PlatformCommand `yaml:"platformCommand"`
// Command is the plugin command, as a single string.
//
// Deprecated: Use PlatformCommand instead. Removed in subprocess/v1 plugins.
// DEPRECATED: Use PlatformCommand instead. Removed in subprocess/v1 plugins.
Command string `yaml:"command"`
// IgnoreFlags ignores any flags passed in from Helm
@ -61,8 +60,7 @@ type MetadataLegacy struct {
PlatformHooks PlatformHooks `yaml:"platformHooks"`
// Hooks are commands that will run on plugin events, as a single string.
//
// Deprecated: Use PlatformHooks instead. Removed in subprocess/v1 plugins.
// DEPRECATED: Use PlatformHooks instead. Removed in subprocess/v1 plugins.
Hooks Hooks `yaml:"hooks"`
// Downloaders field is used if the plugin supply downloader mechanism

@ -23,6 +23,7 @@ import (
)
func TestValidatePluginData(t *testing.T) {
// A mock plugin with no commands
mockNoCommand := mockSubprocessCLIPlugin(t, "foo")
mockNoCommand.metadata.RuntimeConfig = &RuntimeConfigSubprocess{

@ -28,6 +28,7 @@ func TestMakeOutputMessage(t *testing.T) {
ptm := pluginTypesIndex["getter/v1"]
outputType := reflect.Zero(ptm.outputType).Interface()
assert.IsType(t, schema.OutputMessageGetterV1{}, outputType)
}
func TestMakeConfig(t *testing.T) {

@ -99,6 +99,7 @@ type RuntimeExtismV1 struct {
var _ Runtime = (*RuntimeExtismV1)(nil)
func (r *RuntimeExtismV1) CreatePlugin(pluginDir string, metadata *Metadata) (Plugin, error) {
rc, ok := metadata.RuntimeConfig.(*RuntimeConfigExtismV1)
if !ok {
return nil, fmt.Errorf("invalid extism/v1 plugin runtime config type: %T", metadata.RuntimeConfig)
@ -138,6 +139,7 @@ func (p *ExtismV1PluginRuntime) Dir() string {
}
func (p *ExtismV1PluginRuntime) Invoke(ctx context.Context, input *Input) (*Output, error) {
var tmpDir string
if p.rc.FileSystem.CreateTempDir {
tmpDirInner, err := os.MkdirTemp(os.TempDir(), "helm-plugin-*")

@ -79,7 +79,7 @@ func TestRuntimeExtismV1InvokePlugin(t *testing.T) {
Name: "Phippy",
},
})
require.NoError(t, err)
require.Nil(t, err)
msg := output.Message.(schema.OutputMessageTestV1)
assert.Equal(t, "Hello, Phippy! (6)", msg.Greeting)

@ -48,6 +48,7 @@ func TestPrepareCommand(t *testing.T) {
}
func TestPrepareCommandExtraArgs(t *testing.T) {
cmdMain := "sh"
cmdArgs := []string{"-c", "echo \"test\""}
platformCommand := []PlatformCommand{

@ -26,6 +26,7 @@ import (
)
func TestSortManifests(t *testing.T) {
data := []struct {
name []string
path string
@ -182,6 +183,7 @@ metadata:
if !reflect.DeepEqual(expectedHooks, out.Events) {
t.Errorf("expected events: %v but got: %v", expectedHooks, out.Events)
}
}
}
if !found {

@ -54,6 +54,7 @@ func New(chartpath, cachepath string, registryClient *registry.Client) *Resolver
// Resolve resolves dependencies and returns a lock file with the resolution.
func (r *Resolver) Resolve(reqs []*chart.Dependency, repoNames map[string]string) (*chart.Lock, error) {
// Now we clone the dependencies, locking as we go.
locked := make([]*chart.Dependency, len(reqs))
missing := []string{}
@ -145,6 +146,7 @@ func (r *Resolver) Resolve(reqs []*chart.Dependency, repoNames map[string]string
Version: version,
},
}}
} else {
// Retrieve list of tags for repository
ref := fmt.Sprintf("%s/%s", strings.TrimPrefix(d.Repository, registry.OCIScheme+"://"), d.Name)

@ -238,6 +238,7 @@ func TestCopyDirFail_SrcIsNotDir(t *testing.T) {
if !errors.Is(err, errSrcNotDir) {
t.Fatalf("expected %v error for CopyDir(%s, %s), got %s", errSrcNotDir, srcdir, dstdir, err)
}
}
func TestCopyDirFail_DstExists(t *testing.T) {
@ -490,6 +491,7 @@ func setupInaccessibleDir(t *testing.T, op func(dir string) error) func() {
}
func TestIsDir(t *testing.T) {
var currentUID = os.Getuid()
if currentUID == 0 {
@ -542,6 +544,7 @@ func TestIsDir(t *testing.T) {
}
func TestIsSymlink(t *testing.T) {
var currentUID = os.Getuid()
if currentUID == 0 {

@ -18,13 +18,28 @@ package version
import (
"errors"
"fmt"
"runtime/debug"
"slices"
_ "k8s.io/client-go/kubernetes" // Force k8s.io/client-go to be included in the build
)
var (
// kubeClientVersionMajorOverride and kubeClientVersionMinorOverride can be
// set via -X ldflags to specify the k8s.io/client-go version in non-standard
// builds (e.g., Bazel) where debug.ReadBuildInfo() is not available.
// Example: -X helm.sh/helm/v4/internal/version.kubeClientVersionMajorOverride=0
// -X helm.sh/helm/v4/internal/version.kubeClientVersionMinorOverride=30
kubeClientVersionMajorOverride = ""
kubeClientVersionMinorOverride = ""
)
func K8sIOClientGoModVersion() (string, error) {
if kubeClientVersionMajorOverride != "" && kubeClientVersionMinorOverride != "" {
return fmt.Sprintf("v%s.%s.0", kubeClientVersionMajorOverride, kubeClientVersionMinorOverride), nil
}
info, ok := debug.ReadBuildInfo()
if !ok {
return "", errors.New("failed to read build info")

@ -77,6 +77,7 @@ 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)

@ -630,6 +630,7 @@ func GetVersionSet(client discovery.ServerResourcesInterface) (common.VersionSet
var ok bool
for _, r := range resources {
for _, rl := range r.APIResources {
// A Kind at a GroupVersion can show up more than once. We only want
// it displayed once in the final output.
id = path.Join(r.GroupVersion, rl.Kind)

@ -120,6 +120,7 @@ func (d *Dependency) dependencyStatus(chartpath string, dep *chart.Dependency, p
if r := statArchiveForStatus(archive, dep); r != "" {
return r
}
}
// End unnecessary code.

@ -119,6 +119,7 @@ func (m *Metadata) FormattedDepNames() string {
continue
}
depsNames = append(depsNames, ac.Name())
}
sort.StringSlice(depsNames).Sort()

@ -35,6 +35,7 @@ import (
func (cfg *Configuration) execHook(rl *release.Release, hook release.HookEvent,
waitStrategy kube.WaitStrategy, waitOptions []kube.WaitOption,
timeout time.Duration, serverSideApply bool) error {
shutdown, err := cfg.execHookWithDelayedShutdown(rl, hook, waitStrategy, waitOptions, timeout, serverSideApply)
if shutdown == nil {
return err
@ -58,6 +59,7 @@ func shutdownNoOp() error {
func (cfg *Configuration) execHookWithDelayedShutdown(rl *release.Release, hook release.HookEvent,
waitStrategy kube.WaitStrategy, waitOptions []kube.WaitOption, timeout time.Duration,
serverSideApply bool) (ExecuteShutdownFunc, error) {
executingHooks := []*release.Hook{}
for _, h := range rl.Hooks {
@ -177,6 +179,7 @@ func (x hookByWeight) Less(i, j int) bool {
// deleteHookByPolicy deletes a hook if the hook policy instructs it to
func (cfg *Configuration) deleteHookByPolicy(h *release.Hook, policy release.HookDeletePolicy,
waitStrategy kube.WaitStrategy, waitOptions []kube.WaitOption, timeout time.Duration) error {
// Never delete CustomResourceDefinitions; this could cause lots of
// cascading garbage collection.
if h.Kind == "CustomResourceDefinition" {
@ -211,6 +214,7 @@ func (cfg *Configuration) deleteHookByPolicy(h *release.Hook, policy release.Hoo
// deleteHooksByPolicy deletes all hooks if the hook policy instructs it to
func (cfg *Configuration) deleteHooksByPolicy(hooks []*release.Hook, policy release.HookDeletePolicy,
waitStrategy kube.WaitStrategy, waitOptions []kube.WaitOption, timeout time.Duration) error {
for _, h := range hooks {
if err := cfg.deleteHookByPolicy(h, policy, waitStrategy, waitOptions, timeout); err != nil {
return err

@ -737,6 +737,7 @@ func TestInstallRelease_RollbackOnFailure(t *testing.T) {
})
}
func TestInstallRelease_RollbackOnFailure_Interrupted(t *testing.T) {
is := assert.New(t)
instAction := installAction(t)
instAction.ReleaseName = "interrupted-release"
@ -766,6 +767,7 @@ func TestInstallRelease_RollbackOnFailure_Interrupted(t *testing.T) {
is.Equal(goroutines+1, instAction.getGoroutineCount()) // installation goroutine still is in background
time.Sleep(10 * time.Second) // wait for goroutine to finish
is.Equal(goroutines, instAction.getGoroutineCount())
}
func TestNameTemplate(t *testing.T) {
testCases := []nameTemplateTestCase{
@ -802,6 +804,7 @@ func TestNameTemplate(t *testing.T) {
}
for _, tc := range testCases {
n, err := TemplateName(tc.tpl)
if err != nil {
if tc.expectedErrorStr == "" {
@ -864,7 +867,7 @@ func TestInstallReleaseOutputDir(t *testing.T) {
test.AssertGoldenFile(t, filepath.Join(dir, "hello/templates/rbac"), "rbac.txt")
_, err = os.Stat(filepath.Join(dir, "hello/templates/empty"))
is.ErrorIs(err, fs.ErrNotExist)
is.True(errors.Is(err, fs.ErrNotExist))
}
func TestInstallOutputDirWithReleaseName(t *testing.T) {
@ -900,7 +903,7 @@ func TestInstallOutputDirWithReleaseName(t *testing.T) {
test.AssertGoldenFile(t, filepath.Join(newDir, "hello/templates/rbac"), "rbac.txt")
_, err = os.Stat(filepath.Join(newDir, "hello/templates/empty"))
is.ErrorIs(err, fs.ErrNotExist)
is.True(errors.Is(err, fs.ErrNotExist))
}
func TestNameAndChart(t *testing.T) {
@ -1165,7 +1168,7 @@ func TestInstallCRDs_AlreadyExist(t *testing.T) {
mockChart := buildChart(withFile(mockFile))
crdsToInstall := mockChart.CRDObjects()
assert.NoError(t, instAction.installCRDs(crdsToInstall))
assert.Nil(t, instAction.installCRDs(crdsToInstall))
}
func TestInstallCRDs_KubeClient_BuildError(t *testing.T) {
@ -1224,7 +1227,7 @@ func TestCheckDependencies(t *testing.T) {
dependency := chart.Dependency{Name: "hello"}
mockChart := buildChart(withDependency())
assert.NoError(t, CheckDependencies(mockChart, []ci.Dependency{&dependency}))
assert.Nil(t, CheckDependencies(mockChart, []ci.Dependency{&dependency}))
}
func TestCheckDependencies_MissingDependency(t *testing.T) {

@ -150,6 +150,7 @@ func TestValidateVersion(t *testing.T) {
if !errors.Is(err, tt.wantErr) {
t.Errorf("Expected {%v}, got {%v}", tt.wantErr, err)
}
}
})
}

@ -37,7 +37,7 @@ func TestWithCertFile(t *testing.T) {
certFile := "testdata/cert.pem"
opt := WithCertFile(certFile)
assert.NoError(t, opt(client))
assert.Nil(t, opt(client))
assert.Equal(t, certFile, client.certFile)
}
@ -47,7 +47,7 @@ func TestWithInsecure(t *testing.T) {
opt := WithInsecure(true)
assert.NoError(t, opt(client))
assert.Nil(t, opt(client))
assert.True(t, client.insecure)
}
@ -58,7 +58,7 @@ func TestWithKeyFile(t *testing.T) {
keyFile := "testdata/key.pem"
opt := WithKeyFile(keyFile)
assert.NoError(t, opt(client))
assert.Nil(t, opt(client))
assert.Equal(t, keyFile, client.keyFile)
}
@ -69,7 +69,7 @@ func TestWithCAFile(t *testing.T) {
caFile := "testdata/ca.pem"
opt := WithCAFile(caFile)
assert.NoError(t, opt(client))
assert.Nil(t, opt(client))
assert.Equal(t, caFile, client.caFile)
}
@ -79,6 +79,6 @@ func TestWithPlainHTTPLogin(t *testing.T) {
opt := WithPlainHTTPLogin(true)
assert.NoError(t, opt(client))
assert.Nil(t, opt(client))
assert.True(t, client.plainHTTP)
}

@ -40,6 +40,7 @@ func filterManifestsToKeep(manifests []releaseutil.Manifest) (keep, remaining []
if resourcePolicyType == kube.KeepPolicy {
keep = append(keep, m)
}
}
return keep, remaining
}

@ -88,73 +88,6 @@ func (u *Uninstall) Run(name string) (*releasei.UninstallReleaseResponse, error)
if err != nil {
return nil, err
}
// Verify ownership in dry-run mode to show what would actually be deleted
manifests := releaseutil.SplitManifests(r.Manifest)
_, files, err := releaseutil.SortManifests(manifests, nil, releaseutil.UninstallOrder)
if err == nil {
filesToKeep, filesToDelete := filterManifestsToKeep(files)
var builder strings.Builder
for _, file := range filesToDelete {
builder.WriteString("\n---\n" + file.Content)
}
resources, err := u.cfg.KubeClient.Build(strings.NewReader(builder.String()), false)
if err == nil && len(resources) > 0 {
ownedResources, unownedResources, unverifiableResources, err := verifyOwnershipBeforeDelete(resources, r.Name, r.Namespace)
if err == nil {
if len(unownedResources) > 0 {
u.cfg.Logger().Warn("dry-run: resources would be skipped because they are not owned by this release",
"release", r.Name,
"count", len(unownedResources))
for _, info := range unownedResources {
u.cfg.Logger().Warn("dry-run: would skip resource",
"kind", info.Mapping.GroupVersionKind.Kind,
"name", info.Name,
"namespace", info.Namespace)
}
}
if len(unverifiableResources) > 0 {
u.cfg.Logger().Warn("dry-run: resources would be skipped because their ownership could not be verified",
"release", r.Name,
"count", len(unverifiableResources))
for _, ur := range unverifiableResources {
u.cfg.Logger().Warn("dry-run: would skip resource (ownership could not be verified)",
"kind", ur.Info.Mapping.GroupVersionKind.Kind,
"name", ur.Info.Name,
"namespace", ur.Info.Namespace,
"error", ur.Err)
}
}
if len(ownedResources) > 0 {
u.cfg.Logger().Debug("dry-run: resources would be deleted",
"release", r.Name,
"count", len(ownedResources))
for _, info := range ownedResources {
u.cfg.Logger().Debug("dry-run: would delete resource",
"kind", info.Mapping.GroupVersionKind.Kind,
"name", info.Name,
"namespace", info.Namespace)
}
}
}
}
// Include kept resources in dry-run info
if len(filesToKeep) > 0 {
var kept strings.Builder
kept.WriteString("These resources were kept due to the resource policy:\n")
for _, f := range filesToKeep {
fmt.Fprintf(&kept, "[%s] %s\n", f.Head.Kind, f.Head.Metadata.Name)
}
res := &releasei.UninstallReleaseResponse{Release: r, Info: kept.String()}
return res, nil
}
}
return &releasei.UninstallReleaseResponse{Release: r}, nil
}
@ -220,6 +153,9 @@ func (u *Uninstall) Run(name string) (*releasei.UninstallReleaseResponse, error)
return nil, fmt.Errorf("failed to delete release: %s", name)
}
if kept != "" {
kept = "These resources were kept due to the resource policy:\n" + kept
}
res.Info = kept
if err := waiter.WaitForDelete(deletedResources, u.Timeout); err != nil {
@ -333,11 +269,8 @@ func (u *Uninstall) deleteRelease(rel *release.Release) (kube.ResourceList, stri
filesToKeep, filesToDelete := filterManifestsToKeep(files)
var kept strings.Builder
if len(filesToKeep) > 0 {
kept.WriteString("These resources were kept due to the resource policy:\n")
for _, f := range filesToKeep {
fmt.Fprintf(&kept, "[%s] %s\n", f.Head.Kind, f.Head.Metadata.Name)
}
for _, f := range filesToKeep {
fmt.Fprintf(&kept, "[%s] %s\n", f.Head.Kind, f.Head.Metadata.Name)
}
var builder strings.Builder
@ -349,69 +282,13 @@ func (u *Uninstall) deleteRelease(rel *release.Release) (kube.ResourceList, stri
if err != nil {
return nil, "", []error{fmt.Errorf("unable to build kubernetes objects for delete: %w", err)}
}
// Verify ownership before deleting resources
var ownedResources, unownedResources kube.ResourceList
var unverifiableResources []unverifiableResource
if len(resources) > 0 {
ownedResources, unownedResources, unverifiableResources, err = verifyOwnershipBeforeDelete(resources, rel.Name, rel.Namespace)
if err != nil {
return nil, "", []error{fmt.Errorf("unable to verify resource ownership: %w", err)}
}
// Log warnings for unowned resources
if len(unownedResources) > 0 {
for _, info := range unownedResources {
u.cfg.Logger().Warn("skipping delete of resource not owned by this release",
"kind", info.Mapping.GroupVersionKind.Kind,
"name", info.Name,
"namespace", info.Namespace,
"release", rel.Name)
}
if kept.Len() > 0 {
kept.WriteString("\n")
}
fmt.Fprintf(&kept, "%d resource(s) were not deleted because they are not owned by this release:\n", len(unownedResources))
for _, info := range unownedResources {
fmt.Fprintf(&kept, "[%s] %s\n", info.Mapping.GroupVersionKind.Kind, info.Name)
}
}
// Log warnings for resources whose ownership could not be verified
if len(unverifiableResources) > 0 {
for _, ur := range unverifiableResources {
u.cfg.Logger().Warn("skipping delete of resource because ownership could not be verified",
"kind", ur.Info.Mapping.GroupVersionKind.Kind,
"name", ur.Info.Name,
"namespace", ur.Info.Namespace,
"release", rel.Name,
"error", ur.Err)
}
if kept.Len() > 0 {
kept.WriteString("\n")
}
fmt.Fprintf(&kept, "%d resource(s) were not deleted because their ownership could not be verified:\n", len(unverifiableResources))
for _, ur := range unverifiableResources {
fmt.Fprintf(&kept, "[%s] %s: %s\n", ur.Info.Mapping.GroupVersionKind.Kind, ur.Info.Name, ur.Err)
}
}
// Delete only owned resources
if len(ownedResources) > 0 {
for _, info := range ownedResources {
u.cfg.Logger().Debug("deleting resource owned by this release",
"kind", info.Mapping.GroupVersionKind.Kind,
"name", info.Name,
"namespace", info.Namespace,
"release", rel.Name)
}
_, errs = u.cfg.KubeClient.Delete(ownedResources, parseCascadingFlag(u.DeletionPropagation, u.cfg.Logger()))
}
_, errs = u.cfg.KubeClient.Delete(resources, parseCascadingFlag(u.DeletionPropagation))
}
return ownedResources, kept.String(), errs
return resources, kept.String(), errs
}
func parseCascadingFlag(cascadingFlag string, logger *slog.Logger) v1.DeletionPropagation {
func parseCascadingFlag(cascadingFlag string) v1.DeletionPropagation {
switch cascadingFlag {
case "orphan":
return v1.DeletePropagationOrphan
@ -420,7 +297,7 @@ func parseCascadingFlag(cascadingFlag string, logger *slog.Logger) v1.DeletionPr
case "background":
return v1.DeletePropagationBackground
default:
logger.Debug("uninstall: given cascade value, defaulting to delete propagation background", "value", cascadingFlag)
slog.Debug("uninstall: given cascade value, defaulting to delete propagation background", "value", cascadingFlag)
return v1.DeletePropagationBackground
}
}

@ -17,10 +17,9 @@ limitations under the License.
package action
import (
"bytes"
"context"
"errors"
"io"
"log/slog"
"testing"
"github.com/stretchr/testify/assert"
@ -148,16 +147,9 @@ func TestUninstallRelease_Cascade(t *testing.T) {
}
}`
require.NoError(t, unAction.cfg.Releases.Create(rel))
// Create dummy resources with Mapping but no Client - this skips ownership verification
// (nil Client is treated as owned) and goes directly to delete
dummyResources := kube.ResourceList{
newDeploymentResource("secret", "", ""),
}
failer := unAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
failer.DeleteError = errors.New("Uninstall with cascade failed")
failer.DummyResources = dummyResources
failer.BuildDummy = true
unAction.cfg.KubeClient = failer
_, err := unAction.Run(rel.Name)
require.Error(t, err)
@ -178,177 +170,39 @@ func TestUninstallRun_UnreachableKubeClient(t *testing.T) {
assert.ErrorContains(t, err, "connection refused")
}
func TestUninstallRelease_OwnershipVerification(t *testing.T) {
func TestUninstall_WaitOptionsPassedDownstream(t *testing.T) {
is := assert.New(t)
// Create a buffer to capture log output
logBuffer := &bytes.Buffer{}
handler := slog.NewTextHandler(logBuffer, &slog.HandlerOptions{Level: slog.LevelDebug})
config := actionConfigFixture(t)
config.SetLogger(handler)
unAction := NewUninstall(config)
unAction.DisableHooks = true
unAction.DryRun = false
unAction.KeepHistory = true
rel := releaseStub()
rel.Name = "ownership-test"
rel.Namespace = "default"
rel.Manifest = `apiVersion: v1
kind: ConfigMap
metadata:
name: test-configmap
labels:
app.kubernetes.io/managed-by: Helm
annotations:
meta.helm.sh/release-name: ownership-test
meta.helm.sh/release-namespace: default
data:
key: value`
require.NoError(t, config.Releases.Create(rel))
// Create dummy resources with proper ownership metadata
labels := map[string]string{
"app.kubernetes.io/managed-by": "Helm",
}
annotations := map[string]string{
"meta.helm.sh/release-name": "ownership-test",
"meta.helm.sh/release-namespace": "default",
}
dummyResources := kube.ResourceList{
newDeploymentWithOwner("owned-deploy", "default", labels, annotations),
}
failer := config.KubeClient.(*kubefake.FailingKubeClient)
failer.DummyResources = dummyResources
resi, err := unAction.Run(rel.Name)
is.NoError(err)
is.NotNil(resi)
res, err := releaserToV1Release(resi.Release)
is.NoError(err)
is.Equal(common.StatusUninstalled, res.Info.Status)
// Verify log contains debug message about deleting owned resource
logOutput := logBuffer.String()
is.Contains(logOutput, "deleting resource owned by this release")
is.Contains(logOutput, "owned-deploy")
is.Contains(logOutput, "Deployment")
}
func TestUninstallRelease_OwnershipVerification_WithKeepPolicy(t *testing.T) {
is := assert.New(t)
// Create a buffer to capture log output
logBuffer := &bytes.Buffer{}
handler := slog.NewTextHandler(logBuffer, &slog.HandlerOptions{Level: slog.LevelWarn})
config := actionConfigFixture(t)
config.SetLogger(handler)
unAction := NewUninstall(config)
unAction := uninstallAction(t)
unAction.DisableHooks = true
unAction.DryRun = false
unAction.KeepHistory = true
rel := releaseStub()
rel.Name = "keep-and-ownership"
rel.Namespace = "default"
rel.Manifest = `apiVersion: v1
kind: Secret
metadata:
name: kept-secret
annotations:
helm.sh/resource-policy: keep
meta.helm.sh/release-name: keep-and-ownership
meta.helm.sh/release-namespace: default
labels:
app.kubernetes.io/managed-by: Helm
type: Opaque
data:
password: cGFzc3dvcmQ=
---
apiVersion: v1
kind: ConfigMap
metadata:
name: deleted-configmap
labels:
app.kubernetes.io/managed-by: Helm
annotations:
meta.helm.sh/release-name: keep-and-ownership
meta.helm.sh/release-namespace: default
data:
key: value`
require.NoError(t, config.Releases.Create(rel))
// Create dummy resources - one unowned to test logging
dummyResources := kube.ResourceList{
newDeploymentWithOwner("unowned-deploy", "default", nil, nil),
}
failer := config.KubeClient.(*kubefake.FailingKubeClient)
failer.DummyResources = dummyResources
res, err := unAction.Run(rel.Name)
is.NoError(err)
is.NotNil(res)
// Should contain info about kept resources
is.Contains(res.Info, "kept due to the resource policy")
// Verify log contains warning about skipped unowned resource
logOutput := logBuffer.String()
is.Contains(logOutput, "skipping delete of resource not owned by this release")
is.Contains(logOutput, "unowned-deploy")
}
func TestUninstallRelease_DryRun_OwnershipVerification(t *testing.T) {
is := assert.New(t)
// Create a buffer to capture log output
logBuffer := &bytes.Buffer{}
handler := slog.NewTextHandler(logBuffer, &slog.HandlerOptions{Level: slog.LevelWarn})
config := actionConfigFixture(t)
config.SetLogger(handler)
unAction.WaitStrategy = kube.StatusWatcherStrategy
unAction := NewUninstall(config)
unAction.DisableHooks = true
unAction.DryRun = true
// Use WithWaitContext as a marker WaitOption that we can track
ctx := context.Background()
unAction.WaitOptions = []kube.WaitOption{kube.WithWaitContext(ctx)}
rel := releaseStub()
rel.Name = "dryrun-ownership"
rel.Namespace = "default"
rel.Manifest = `apiVersion: v1
kind: ConfigMap
metadata:
name: test-configmap
labels:
app.kubernetes.io/managed-by: Helm
annotations:
meta.helm.sh/release-name: dryrun-ownership
meta.helm.sh/release-namespace: default
data:
key: value`
require.NoError(t, config.Releases.Create(rel))
rel.Name = "wait-options-uninstall"
rel.Manifest = `{
"apiVersion": "v1",
"kind": "Secret",
"metadata": {
"name": "secret"
},
"type": "Opaque",
"data": {
"password": "password"
}
}`
require.NoError(t, unAction.cfg.Releases.Create(rel))
// Create dummy resources - one unowned to test dry-run logging
dummyResources := kube.ResourceList{
newDeploymentWithOwner("dryrun-unowned-deploy", "default", nil, nil),
}
failer := config.KubeClient.(*kubefake.FailingKubeClient)
failer.DummyResources = dummyResources
// Access the underlying FailingKubeClient to check recorded options
failer := unAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
resi, err := unAction.Run(rel.Name)
is.NoError(err)
is.NotNil(resi)
is.NotNil(resi.Release)
res, err := releaserToV1Release(resi.Release)
_, err := unAction.Run(rel.Name)
is.NoError(err)
is.Equal("dryrun-ownership", res.Name)
// Verify log contains dry-run warning about resources that would be skipped
logOutput := logBuffer.String()
is.Contains(logOutput, "dry-run: would skip resource")
is.Contains(logOutput, "dryrun-unowned-deploy")
is.Contains(logOutput, "Deployment")
// Verify that WaitOptions were passed to GetWaiter
is.NotEmpty(failer.RecordedWaitOptions, "WaitOptions should be passed to GetWaiter")
}

@ -268,6 +268,7 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chartv2.Chart, vals map[str
return nil, nil, false, err
}
}
}
// determine if values will be reused

@ -446,6 +446,7 @@ func TestUpgradeRelease_Interrupted_Wait(t *testing.T) {
}
func TestUpgradeRelease_Interrupted_RollbackOnFailure(t *testing.T) {
is := assert.New(t)
req := require.New(t)
@ -715,7 +716,7 @@ func TestGetUpgradeServerSideValue(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
serverSideApply, err := getUpgradeServerSideValue(tt.actionServerSideOption, tt.releaseApplyMethod)
assert.NoError(t, err)
assert.Nil(t, err)
assert.Equal(t, tt.expectedServerSideApply, serverSideApply)
})
}
@ -740,6 +741,7 @@ func TestGetUpgradeServerSideValue(t *testing.T) {
assert.ErrorContains(t, err, tt.expectedErrorMsg)
})
}
}
func TestUpgradeRun_UnreachableKubeClient(t *testing.T) {

@ -93,9 +93,9 @@ func existingResourceConflict(resources kube.ResourceList, releaseName, releaseN
// Allow adoption of the resource if it is managed by Helm and is annotated with correct release name and namespace.
if err := checkOwnership(existing, releaseName, releaseNamespace); err != nil {
return fmt.Errorf("%s exists and cannot be imported into the current release: %w", resourceString(info), err)
return fmt.Errorf("%s exists and cannot be imported into the current release: %s", resourceString(info), err)
}
// Resources that are not found are skipped because they are already deleted and do not need deletion.
infoCopy := *info
requireUpdate.Append(&infoCopy)
return nil
@ -104,69 +104,6 @@ func existingResourceConflict(resources kube.ResourceList, releaseName, releaseN
return requireUpdate, err
}
// unverifiableResource pairs a resource with the error encountered while attempting
// to verify its ownership (for example, RBAC or network failures).
type unverifiableResource struct {
Info *resource.Info
Err error
}
// verifyOwnershipBeforeDelete checks that resources in the list are owned by the specified release.
// It returns three lists:
// - owned: resources confirmed to be owned by the release (safe to delete).
// - unowned: resources that exist but are not owned by the release (should be skipped).
// - unverifiable: resources whose ownership could not be determined due to a fetch
// error (e.g. RBAC or network issues), paired with the underlying error.
//
// Resources that are not found on the server are excluded from all returned lists,
// since they have already been deleted and require no further action.
func verifyOwnershipBeforeDelete(resources kube.ResourceList, releaseName, releaseNamespace string) (kube.ResourceList, kube.ResourceList, []unverifiableResource, error) {
var owned kube.ResourceList
var unowned kube.ResourceList
var unverifiable []unverifiableResource
err := resources.Visit(func(info *resource.Info, err error) error {
if err != nil {
return err
}
// If client is not available, skip verification (test scenario or build failure)
if info.Client == nil {
infoCopy := *info
owned.Append(&infoCopy)
return nil
}
helper := resource.NewHelper(info.Client, info.Mapping)
existing, err := helper.Get(info.Namespace, info.Name)
if err != nil {
if apierrors.IsNotFound(err) {
// Resource already deleted; nothing to do.
return nil
}
// Cannot fetch resource (network/permission issue); ownership unverifiable.
infoCopy := *info
unverifiable = append(unverifiable, unverifiableResource{Info: &infoCopy, Err: err})
return nil
}
// Verify ownership of the existing resource
if err := checkOwnership(existing, releaseName, releaseNamespace); err != nil {
// Resource not owned by this release, cannot delete
infoCopy := *info
unowned.Append(&infoCopy)
return nil
}
// Resource is owned by this release, can delete
infoCopy := *info
owned.Append(&infoCopy)
return nil
})
return owned, unowned, unverifiable, err
}
func checkOwnership(obj runtime.Object, releaseName, releaseNamespace string) error {
lbls, err := accessor.Labels(obj)
if err != nil {
@ -179,13 +116,13 @@ func checkOwnership(obj runtime.Object, releaseName, releaseNamespace string) er
var errs []error
if err := requireValue(lbls, appManagedByLabel, appManagedByHelm); err != nil {
errs = append(errs, fmt.Errorf("label validation error: %w", err))
errs = append(errs, fmt.Errorf("label validation error: %s", err))
}
if err := requireValue(annos, helmReleaseNameAnnotation, releaseName); err != nil {
errs = append(errs, fmt.Errorf("annotation validation error: %w", err))
errs = append(errs, fmt.Errorf("annotation validation error: %s", err))
}
if err := requireValue(annos, helmReleaseNamespaceAnnotation, releaseNamespace); err != nil {
errs = append(errs, fmt.Errorf("annotation validation error: %w", err))
errs = append(errs, fmt.Errorf("annotation validation error: %s", err))
}
if len(errs) > 0 {
@ -217,7 +154,7 @@ func setMetadataVisitor(releaseName, releaseNamespace string, forceOwnership boo
if !forceOwnership {
if err := checkOwnership(info.Object, releaseName, releaseNamespace); err != nil {
return fmt.Errorf("%s cannot be owned: %w", resourceString(info), err)
return fmt.Errorf("%s cannot be owned: %s", resourceString(info), err)
}
}
@ -225,7 +162,7 @@ func setMetadataVisitor(releaseName, releaseNamespace string, forceOwnership boo
appManagedByLabel: appManagedByHelm,
}); err != nil {
return fmt.Errorf(
"%s labels could not be updated: %w",
"%s labels could not be updated: %s",
resourceString(info), err,
)
}
@ -235,7 +172,7 @@ func setMetadataVisitor(releaseName, releaseNamespace string, forceOwnership boo
helmReleaseNamespaceAnnotation: releaseNamespace,
}); err != nil {
return fmt.Errorf(
"%s annotations could not be updated: %w",
"%s annotations could not be updated: %s",
resourceString(info), err,
)
}

@ -211,98 +211,6 @@ func TestCheckOwnership(t *testing.T) {
assert.EqualError(t, err, `invalid ownership metadata; label validation error: key "app.kubernetes.io/managed-by" must equal "Helm": current value is "helm"`)
}
func TestVerifyOwnershipBeforeDelete(t *testing.T) {
var (
releaseName = "rel-a"
releaseNamespace = "ns-a"
labels = map[string]string{
appManagedByLabel: appManagedByHelm,
}
annotations = map[string]string{
helmReleaseNameAnnotation: releaseName,
helmReleaseNamespaceAnnotation: releaseNamespace,
}
wrongAnnotations = map[string]string{
helmReleaseNameAnnotation: "rel-b",
helmReleaseNamespaceAnnotation: releaseNamespace,
}
)
// Test all resources properly owned
t.Run("all resources owned", func(t *testing.T) {
owned1 := newDeploymentWithOwner("owned1", "ns-a", labels, annotations)
owned2 := newDeploymentWithOwner("owned2", "ns-a", labels, annotations)
resources := kube.ResourceList{owned1, owned2}
ownedList, unownedList, _, err := verifyOwnershipBeforeDelete(resources, releaseName, releaseNamespace)
assert.NoError(t, err)
assert.Len(t, ownedList, 2)
assert.Len(t, unownedList, 0)
})
// Test mix of owned and unowned resources
t.Run("mixed ownership", func(t *testing.T) {
owned := newDeploymentWithOwner("owned", "ns-a", labels, annotations)
unowned := newDeploymentWithOwner("unowned", "ns-a", labels, wrongAnnotations)
resources := kube.ResourceList{owned, unowned}
ownedList, unownedList, _, err := verifyOwnershipBeforeDelete(resources, releaseName, releaseNamespace)
assert.NoError(t, err)
assert.Len(t, ownedList, 1)
assert.Len(t, unownedList, 1)
assert.Equal(t, "owned", ownedList[0].Name)
assert.Equal(t, "unowned", unownedList[0].Name)
})
// Test resource not found (should be skipped - not in either list)
t.Run("resource not found", func(t *testing.T) {
missing := newMissingDeployment("missing", "ns-a")
resources := kube.ResourceList{missing}
ownedList, unownedList, _, err := verifyOwnershipBeforeDelete(resources, releaseName, releaseNamespace)
assert.NoError(t, err)
assert.Len(t, ownedList, 0)
assert.Len(t, unownedList, 0)
})
// Test resource with no ownership metadata
t.Run("no ownership metadata", func(t *testing.T) {
noMeta := newDeploymentWithOwner("no-meta", "ns-a", nil, nil)
resources := kube.ResourceList{noMeta}
ownedList, unownedList, _, err := verifyOwnershipBeforeDelete(resources, releaseName, releaseNamespace)
assert.NoError(t, err)
assert.Len(t, ownedList, 0)
assert.Len(t, unownedList, 1)
})
// Test resource owned by different release
t.Run("owned by different release", func(t *testing.T) {
otherRelease := newDeploymentWithOwner("other", "ns-a", labels, wrongAnnotations)
resources := kube.ResourceList{otherRelease}
ownedList, unownedList, _, err := verifyOwnershipBeforeDelete(resources, releaseName, releaseNamespace)
assert.NoError(t, err)
assert.Len(t, ownedList, 0)
assert.Len(t, unownedList, 1)
})
// Test mixed scenario: owned, unowned, and missing resources
t.Run("mixed with missing resources", func(t *testing.T) {
owned := newDeploymentWithOwner("owned", "ns-a", labels, annotations)
unowned := newDeploymentWithOwner("unowned", "ns-a", labels, wrongAnnotations)
missing := newMissingDeployment("missing", "ns-a")
resources := kube.ResourceList{owned, unowned, missing}
ownedList, unownedList, _, err := verifyOwnershipBeforeDelete(resources, releaseName, releaseNamespace)
assert.NoError(t, err)
assert.Len(t, ownedList, 1)
assert.Len(t, unownedList, 1)
assert.Equal(t, "owned", ownedList[0].Name)
assert.Equal(t, "unowned", unownedList[0].Name)
})
}
func TestSetMetadataVisitor(t *testing.T) {
var (
err error
@ -365,6 +273,7 @@ func TestValidateNameAndGenerateName(t *testing.T) {
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
skip, err := validateNameAndGenerateName(tc.info)
if tc.wantErr {

@ -17,6 +17,7 @@ package common
import (
"fmt"
"log/slog"
"slices"
"strconv"
"strings"
@ -152,7 +153,10 @@ func makeDefaultCapabilities() (*Capabilities, error) {
vstr, err := helmversion.K8sIOClientGoModVersion()
if err != nil {
return nil, fmt.Errorf("failed to retrieve k8s.io/client-go version: %w", err)
// Build info may be unavailable when compiled with toolchains other
// than "go build" (e.g. Bazel). Fall back to a safe default.
slog.Warn("failed to retrieve k8s.io/client-go version, falling back to default Kubernetes version", slog.Any("error", err))
return newCapabilities(kubeVersionMajorTesting, kubeVersionMinorTesting)
}
v, err := semver.NewVersion(vstr)
@ -167,6 +171,7 @@ func makeDefaultCapabilities() (*Capabilities, error) {
}
func newCapabilities(kubeVersionMajor, kubeVersionMinor uint64) (*Capabilities, error) {
version := fmt.Sprintf("v%d.%d.0", kubeVersionMajor, kubeVersionMinor)
return &Capabilities{
KubeVersion: KubeVersion{

@ -666,6 +666,7 @@ func TestMergeTables(t *testing.T) {
}
func TestCoalesceValuesWarnings(t *testing.T) {
c := withDeps(&chart.Chart{
Metadata: &chart.Metadata{Name: "level1"},
Values: map[string]any{
@ -723,6 +724,7 @@ func TestCoalesceValuesWarnings(t *testing.T) {
assert.Contains(t, warnings, "warning: skipped value for level1.level2.level3.boat: Not a table.")
assert.Contains(t, warnings, "warning: destination for level1.level2.level3.spear.tip is a table. Ignoring non-table value (true)")
assert.Contains(t, warnings, "warning: cannot overwrite table with non table for level1.level2.level3.spear.sail (map[cotton:true])")
}
func TestConcatPrefix(t *testing.T) {

@ -25,6 +25,7 @@ import (
)
func TestToRenderValues(t *testing.T) {
chartValues := map[string]any{
"name": "al Rashid",
"where": map[string]any{

@ -100,6 +100,7 @@ func LoadDir(dir string) (chart.Charter, error) {
default:
return nil, errors.New("unsupported chart version")
}
}
// FileLoader loads a chart from a file

@ -143,7 +143,6 @@ func (ch *Chart) AppVersion() string {
}
// CRDs returns a list of File objects in the 'crds/' directory of a Helm chart.
//
// Deprecated: use CRDObjects()
func (ch *Chart) CRDs() []*common.File {
files := []*common.File{}

@ -104,7 +104,7 @@ func TestMetadata(t *testing.T) {
is.Equal("foo.yaml", chrt.Name())
is.Equal("1.0.0", chrt.AppVersion())
is.NoError(chrt.Validate())
is.Equal(nil, chrt.Validate())
}
func TestIsRoot(t *testing.T) {

@ -44,6 +44,7 @@ func WithSkipSchemaValidation(skipSchemaValidation bool) LinterOption {
}
func RunAll(baseDir string, values map[string]any, namespace string, options ...LinterOption) support.Linter {
chartDir, _ := filepath.Abs(baseDir)
lo := linterOptions{}

@ -62,6 +62,7 @@ func TemplateLinterSkipSchemaValidation(skipSchemaValidation bool) TemplateLinte
}
func newTemplateLinter(linter *support.Linter, namespace string, values map[string]any, options ...TemplateLinterOption) templateLinter {
result := templateLinter{
linter: linter,
values: values,

@ -257,6 +257,7 @@ data:
//
// See https://github.com/helm/helm/issues/7483
func TestStrictTemplateParsingMapError(t *testing.T) {
ch := chart.Chart{
Metadata: &chart.Metadata{
Name: "regression7483",
@ -389,6 +390,7 @@ func TestValidateTopIndentLevel(t *testing.T) {
t.Errorf("Expected %t for %q", shouldFail, doc)
}
}
}
// TestEmptyWithCommentsManifests checks the lint is not failing against empty manifests that contains only comments
@ -484,4 +486,5 @@ func TestIsYamlFileExtension(t *testing.T) {
t.Errorf("isYamlFileExtension(%s) = %v; want %v", test.filename, result, test.expected)
}
}
}

@ -390,6 +390,7 @@ icon: https://example.com/64x64.png
if text.String() != "" {
t.Errorf("Expected no message to Stderr, got %s", text.String())
}
}
// Packaging the chart on a Windows machine will produce an
@ -659,6 +660,7 @@ func verifyChart(t *testing.T, c *chart.Chart) {
t.Errorf("Expected %s version %s, got %s", dep.Name(), exp["version"], dep.Metadata.Version)
}
}
}
func verifyDependencies(t *testing.T, c *chart.Chart) {

@ -701,6 +701,7 @@ func CreateFrom(chartfile *chart.Metadata, dest, src string) error {
// error. In such a case, this will attempt to clean up by removing the
// new chart directory.
func Create(name, dir string) (string, error) {
// Sanity-check the name of a chart so user doesn't create one that causes problems.
if err := validateChartName(name); err != nil {
return "", err

@ -459,6 +459,7 @@ func TestDependentChartAliases(t *testing.T) {
if aliasChart := getAliasDependency(c.Dependencies(), req[2]); aliasChart != nil {
t.Fatalf("expected no chart but got %s", aliasChart.Name())
}
}
func TestDependentChartWithSubChartsAbsentInDependency(t *testing.T) {

@ -79,6 +79,7 @@ func ValidateReleaseName(name string) error {
// This case is preserved for backwards compatibility
if name == "" {
return errMissingName
}
if len(name) > maxReleaseNameLen || !validName.MatchString(name) {
return errInvalidName

@ -51,6 +51,7 @@ func checkFileCompletion(t *testing.T, cmdName string, shouldBePerformed bool) {
if shouldBePerformed {
t.Errorf("Unexpected directive ShellCompDirectiveNoFileComp when completing '%s'", cmdName)
} else {
t.Errorf("Did not receive directive ShellCompDirectiveNoFileComp when completing '%s'", cmdName)
}
t.Log(out)

@ -55,7 +55,7 @@ func newDependencyBuildCmd(out io.Writer) *cobra.Command {
if len(args) > 0 {
chartpath = filepath.Clean(args[0])
}
registryClient, err := newRegistryClient(out, client.CertFile, client.KeyFile, client.CaFile,
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
client.InsecureSkipTLSVerify, client.PlainHTTP, client.Username, client.Password)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)

@ -58,7 +58,7 @@ func newDependencyUpdateCmd(_ *action.Configuration, out io.Writer) *cobra.Comma
if len(args) > 0 {
chartpath = filepath.Clean(args[0])
}
registryClient, err := newRegistryClient(out, client.CertFile, client.KeyFile, client.CaFile,
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
client.InsecureSkipTLSVerify, client.PlainHTTP, client.Username, client.Password)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)

@ -220,6 +220,7 @@ func (p *postRendererArgsSlice) Type() string {
}
func (p *postRendererArgsSlice) Set(val string) error {
// a post-renderer defined by a user may accept empty arguments
p.options.args = append(p.options.args, val)

@ -77,6 +77,7 @@ func newGetMetadataCmd(cfg *action.Configuration, out io.Writer) *cobra.Command
}
func (w metadataWriter) WriteTable(out io.Writer) error {
formatApplyMethod := func(applyMethod string) string {
switch applyMethod {
case "":

@ -43,6 +43,7 @@ func addDryRunFlag(cmd *cobra.Command) {
// Determine the `action.DryRunStrategy` given -dry-run=<value>` flag (or absence of)
// Legacy usage of the flag: boolean values, and `--dry-run` (without value) are supported, and log warnings emitted
func cmdGetDryRunFlagStrategy(cmd *cobra.Command, isTemplate bool) (action.DryRunStrategy, error) {
f := cmd.Flag("dry-run")
v := f.Value.String()

@ -157,6 +157,7 @@ func resetEnv() func() {
}
func TestCmdGetDryRunFlagStrategy(t *testing.T) {
type testCaseExpectedLog struct {
Level string
Msg string
@ -273,6 +274,7 @@ func TestCmdGetDryRunFlagStrategy(t *testing.T) {
}
for name, tc := range testCases {
logBuf := new(bytes.Buffer)
logger := slog.New(slog.NewJSONHandler(logBuf, nil))
slog.SetDefault(logger)
@ -288,14 +290,14 @@ func TestCmdGetDryRunFlagStrategy(t *testing.T) {
if tc.ExpectedError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
assert.Nil(t, err)
assert.Equal(t, tc.ExpectedStrategy, dryRunStrategy)
}
if tc.ExpectedLog != nil {
logResult := map[string]string{}
err = json.Unmarshal(logBuf.Bytes(), &logResult)
require.NoError(t, err)
require.Nil(t, err)
assert.Equal(t, tc.ExpectedLog.Level, logResult["level"])
assert.Equal(t, tc.ExpectedLog.Msg, logResult["msg"])

@ -225,6 +225,7 @@ func getReleaseHistory(rls []*release.Release) (history releaseHistory) {
}
if !r.Info.LastDeployed.IsZero() {
rInfo.Updated = r.Info.LastDeployed
}
history = append(history, rInfo)
}

@ -143,7 +143,7 @@ func newInstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
return compInstall(args, toComplete, client)
},
RunE: func(cmd *cobra.Command, args []string) error {
registryClient, err := newRegistryClient(out, client.CertFile, client.KeyFile, client.CaFile,
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
client.InsecureSkipTLSVerify, client.PlainHTTP, client.Username, client.Password)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)

@ -59,7 +59,7 @@ func newLintCmd(out io.Writer) *cobra.Command {
if kubeVersion != "" {
parsedKubeVersion, err := common.ParseKubeVersion(kubeVersion)
if err != nil {
return fmt.Errorf("invalid kube version '%s': %w", kubeVersion, err)
return fmt.Errorf("invalid kube version '%s': %s", kubeVersion, err)
}
client.KubeVersion = parsedKubeVersion
}

@ -60,6 +60,7 @@ func TestLintCmdWithQuietFlag(t *testing.T) {
wantError: true,
}}
runTestCmd(t, tests)
}
func TestLintCmdWithKubeVersionFlag(t *testing.T) {

@ -333,6 +333,7 @@ func loadFile(path string) (*pluginCommand, error) {
// to obtain the dynamic completion choices. It must pass all the flags and sub-commands
// specified in the command-line to the plugin.complete executable (except helm's global flags)
func pluginDynamicComp(plug plugin.Plugin, cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
subprocessPlug, ok := plug.(*plugin.SubprocessPluginRuntime)
if !ok {
// Completion only supported for subprocess plugins (TODO: fix this)

@ -75,7 +75,7 @@ func newPackageCmd(out io.Writer) *cobra.Command {
return err
}
registryClient, err := newRegistryClient(out, client.CertFile, client.KeyFile, client.CaFile,
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
client.InsecureSkipTLSVerify, client.PlainHTTP, client.Username, client.Password)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)

@ -70,7 +70,7 @@ func (o *pluginUninstallOptions) run(out io.Writer) error {
for _, name := range o.names {
if found := findPlugin(plugins, name); found != nil {
if err := uninstallPlugin(found); err != nil {
errorPlugins = append(errorPlugins, fmt.Errorf("failed to uninstall plugin %s, got error (%w)", name, err))
errorPlugins = append(errorPlugins, fmt.Errorf("failed to uninstall plugin %s, got error (%v)", name, err))
} else {
fmt.Fprintf(out, "Uninstalled plugin: %s\n", name)
}

@ -71,7 +71,7 @@ func (o *pluginUpdateOptions) run(out io.Writer) error {
for _, name := range o.names {
if found := findPlugin(plugins, name); found != nil {
if err := updatePlugin(found); err != nil {
errorPlugins = append(errorPlugins, fmt.Errorf("failed to update plugin %s, got error (%w)", name, err))
errorPlugins = append(errorPlugins, fmt.Errorf("failed to update plugin %s, got error (%v)", name, err))
} else {
fmt.Fprintf(out, "Updated plugin: %s\n", name)
}

@ -65,7 +65,7 @@ func newPullCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
client.Version = ">0.0.0-0"
}
registryClient, err := newRegistryClient(out, client.CertFile, client.KeyFile, client.CaFile,
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
client.InsecureSkipTLSVerify, client.PlainHTTP, client.Username, client.Password)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)

@ -260,6 +260,7 @@ func TestPullCmd(t *testing.T) {
if out != outString {
t.Errorf("%q: expected verification output %q, got %q", tt.name, outString, out)
}
}
ef := filepath.Join(outdir, tt.expectFile)

@ -71,7 +71,7 @@ func newPushCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
},
RunE: func(_ *cobra.Command, args []string) error {
registryClient, err := newRegistryClient(
out, o.certFile, o.keyFile, o.caFile, o.insecureSkipTLSVerify, o.plainHTTP, o.username, o.password,
o.certFile, o.keyFile, o.caFile, o.insecureSkipTLSVerify, o.plainHTTP, o.username, o.password,
)
if err != nil {

@ -28,6 +28,7 @@ import (
)
func TestRepoIndexCmd(t *testing.T) {
dir := t.TempDir()
comp := filepath.Join(dir, "compressedchart-0.1.0.tgz")

@ -103,6 +103,7 @@ func TestRepoRemove(t *testing.T) {
cacheIndex, cacheChart := createCacheFiles(rootDir, repoName)
cacheFiles[repoName] = []string{cacheIndex, cacheChart}
}
// Create repo remove command

@ -257,7 +257,7 @@ func newRootCmdWithConfig(actionConfig *action.Configuration, out io.Writer, arg
log.Fatal(err)
}
registryClient, err := newDefaultRegistryClient(out, false, "", "")
registryClient, err := newDefaultRegistryClient(false, "", "")
if err != nil {
return nil, err
}
@ -355,6 +355,7 @@ func hookOutputWriter(_, _, _ string) io.Writer {
}
func checkForExpiredRepos(repofile string) {
expiredRepos := []struct {
name string
old string
@ -398,30 +399,31 @@ func checkForExpiredRepos(repofile string) {
)
}
}
}
func newRegistryClient(
out io.Writer, certFile, keyFile, caFile string, insecureSkipTLSVerify, plainHTTP bool, username, password string,
certFile, keyFile, caFile string, insecureSkipTLSVerify, plainHTTP bool, username, password string,
) (*registry.Client, error) {
if certFile != "" && keyFile != "" || caFile != "" || insecureSkipTLSVerify {
registryClient, err := newRegistryClientWithTLS(out, certFile, keyFile, caFile, insecureSkipTLSVerify, username, password)
registryClient, err := newRegistryClientWithTLS(certFile, keyFile, caFile, insecureSkipTLSVerify, username, password)
if err != nil {
return nil, err
}
return registryClient, nil
}
registryClient, err := newDefaultRegistryClient(out, plainHTTP, username, password)
registryClient, err := newDefaultRegistryClient(plainHTTP, username, password)
if err != nil {
return nil, err
}
return registryClient, nil
}
func newDefaultRegistryClient(out io.Writer, plainHTTP bool, username, password string) (*registry.Client, error) {
func newDefaultRegistryClient(plainHTTP bool, username, password string) (*registry.Client, error) {
opts := []registry.ClientOption{
registry.ClientOptDebug(settings.Debug),
registry.ClientOptEnableCache(true),
registry.ClientOptWriter(out),
registry.ClientOptWriter(os.Stderr),
registry.ClientOptCredentialsFile(settings.RegistryConfig),
registry.ClientOptBasicAuth(username, password),
}
@ -438,7 +440,7 @@ func newDefaultRegistryClient(out io.Writer, plainHTTP bool, username, password
}
func newRegistryClientWithTLS(
out io.Writer, certFile, keyFile, caFile string, insecureSkipTLSVerify bool, username, password string,
certFile, keyFile, caFile string, insecureSkipTLSVerify bool, username, password string,
) (*registry.Client, error) {
tlsConf, err := tlsutil.NewTLSConfig(
tlsutil.WithInsecureSkipVerify(insecureSkipTLSVerify),
@ -454,7 +456,7 @@ func newRegistryClientWithTLS(
registryClient, err := registry.NewClient(
registry.ClientOptDebug(settings.Debug),
registry.ClientOptEnableCache(true),
registry.ClientOptWriter(out),
registry.ClientOptWriter(os.Stderr),
registry.ClientOptCredentialsFile(settings.RegistryConfig),
registry.ClientOptHTTPClient(&http.Client{
Transport: &http.Transport{

@ -29,6 +29,7 @@ Use search subcommands to search different locations for charts.
`
func newSearchCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "search [keyword]",
Short: "search for a keyword in charts",

@ -123,6 +123,7 @@ func (i *Index) Search(term string, threshold int, regexp bool) ([]*Result, erro
// calcScore calculates a score for a match.
func (i *Index) calcScore(index int, matchline string) int {
// This is currently tied to the fact that sep is a single char.
splits := []int{}
s := rune(sep[0])

@ -149,6 +149,7 @@ func TestAddRepo_Sort(t *testing.T) {
}
func TestSearchByName(t *testing.T) {
tests := []struct {
name string
query string
@ -244,6 +245,7 @@ func TestSearchByName(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
charts, err := i.Search(tt.query, 100, tt.regexp)
if err != nil {
if tt.fail {
@ -272,6 +274,7 @@ func TestSearchByName(t *testing.T) {
t.Errorf("[%d]: Expected name %q, got %q", i, ex.Name, got.Name)
}
}
})
}
}

@ -196,4 +196,5 @@ func (h *hubSearchWriter) encodeByFormat(out io.Writer, format output.Format) er
// WriteJSON and WriteYAML, we shouldn't get invalid types
return nil
}
}

@ -24,6 +24,7 @@ import (
)
func TestSearchHubCmd(t *testing.T) {
// Setup a mock search service
var searchResult = `{"data":[{"id":"stable/phpmyadmin","type":"chart","attributes":{"name":"phpmyadmin","repo":{"name":"stable","url":"https://charts.helm.sh/stable"},"description":"phpMyAdmin is an mysql administration frontend","home":"https://www.phpmyadmin.net/","keywords":["mariadb","mysql","phpmyadmin"],"maintainers":[{"name":"Bitnami","email":"containers@bitnami.com"}],"sources":["https://github.com/bitnami/bitnami-docker-phpmyadmin"],"icon":""},"links":{"self":"/v1/charts/stable/phpmyadmin"},"relationships":{"latestChartVersion":{"data":{"version":"3.0.0","app_version":"4.9.0-1","created":"2019-08-08T17:57:31.38Z","digest":"119c499251bffd4b06ff0cd5ac98c2ce32231f84899fb4825be6c2d90971c742","urls":["https://charts.helm.sh/stable/phpmyadmin-3.0.0.tgz"],"readme":"/v1/assets/stable/phpmyadmin/versions/3.0.0/README.md","values":"/v1/assets/stable/phpmyadmin/versions/3.0.0/values.yaml"},"links":{"self":"/v1/charts/stable/phpmyadmin/versions/3.0.0"}}}},{"id":"bitnami/phpmyadmin","type":"chart","attributes":{"name":"phpmyadmin","repo":{"name":"bitnami","url":"https://charts.bitnami.com"},"description":"phpMyAdmin is an mysql administration frontend","home":"https://www.phpmyadmin.net/","keywords":["mariadb","mysql","phpmyadmin"],"maintainers":[{"name":"Bitnami","email":"containers@bitnami.com"}],"sources":["https://github.com/bitnami/bitnami-docker-phpmyadmin"],"icon":""},"links":{"self":"/v1/charts/bitnami/phpmyadmin"},"relationships":{"latestChartVersion":{"data":{"version":"3.0.0","app_version":"4.9.0-1","created":"2019-08-08T18:34:13.341Z","digest":"66d77cf6d8c2b52c488d0a294cd4996bd5bad8dc41d3829c394498fb401c008a","urls":["https://charts.bitnami.com/bitnami/phpmyadmin-3.0.0.tgz"],"readme":"/v1/assets/bitnami/phpmyadmin/versions/3.0.0/README.md","values":"/v1/assets/bitnami/phpmyadmin/versions/3.0.0/values.yaml"},"links":{"self":"/v1/charts/bitnami/phpmyadmin/versions/3.0.0"}}}}]}`
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
@ -53,6 +54,7 @@ func TestSearchHubCmd(t *testing.T) {
}
func TestSearchHubListRepoCmd(t *testing.T) {
// Setup a mock search service
var searchResult = `{"data":[{"id":"stable/phpmyadmin","type":"chart","attributes":{"name":"phpmyadmin","repo":{"name":"stable","url":"https://charts.helm.sh/stable"},"description":"phpMyAdmin is an mysql administration frontend","home":"https://www.phpmyadmin.net/","keywords":["mariadb","mysql","phpmyadmin"],"maintainers":[{"name":"Bitnami","email":"containers@bitnami.com"}],"sources":["https://github.com/bitnami/bitnami-docker-phpmyadmin"],"icon":""},"links":{"self":"/v1/charts/stable/phpmyadmin"},"relationships":{"latestChartVersion":{"data":{"version":"3.0.0","app_version":"4.9.0-1","created":"2019-08-08T17:57:31.38Z","digest":"119c499251bffd4b06ff0cd5ac98c2ce32231f84899fb4825be6c2d90971c742","urls":["https://charts.helm.sh/stable/phpmyadmin-3.0.0.tgz"],"readme":"/v1/assets/stable/phpmyadmin/versions/3.0.0/README.md","values":"/v1/assets/stable/phpmyadmin/versions/3.0.0/values.yaml"},"links":{"self":"/v1/charts/stable/phpmyadmin/versions/3.0.0"}}}},{"id":"bitnami/phpmyadmin","type":"chart","attributes":{"name":"phpmyadmin","repo":{"name":"bitnami","url":"https://charts.bitnami.com"},"description":"phpMyAdmin is an mysql administration frontend","home":"https://www.phpmyadmin.net/","keywords":["mariadb","mysql","phpmyadmin"],"maintainers":[{"name":"Bitnami","email":"containers@bitnami.com"}],"sources":["https://github.com/bitnami/bitnami-docker-phpmyadmin"],"icon":""},"links":{"self":"/v1/charts/bitnami/phpmyadmin"},"relationships":{"latestChartVersion":{"data":{"version":"3.0.0","app_version":"4.9.0-1","created":"2019-08-08T18:34:13.341Z","digest":"66d77cf6d8c2b52c488d0a294cd4996bd5bad8dc41d3829c394498fb401c008a","urls":["https://charts.bitnami.com/bitnami/phpmyadmin-3.0.0.tgz"],"readme":"/v1/assets/bitnami/phpmyadmin/versions/3.0.0/README.md","values":"/v1/assets/bitnami/phpmyadmin/versions/3.0.0/values.yaml"},"links":{"self":"/v1/charts/bitnami/phpmyadmin/versions/3.0.0"}}}}]}`
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {

@ -84,7 +84,7 @@ func newShowCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
ValidArgsFunction: validArgsFunc,
RunE: func(_ *cobra.Command, args []string) error {
client.OutputFormat = action.ShowAll
err := addRegistryClient(out, client)
err := addRegistryClient(client)
if err != nil {
return err
}
@ -105,7 +105,7 @@ func newShowCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
ValidArgsFunction: validArgsFunc,
RunE: func(_ *cobra.Command, args []string) error {
client.OutputFormat = action.ShowValues
err := addRegistryClient(out, client)
err := addRegistryClient(client)
if err != nil {
return err
}
@ -126,7 +126,7 @@ func newShowCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
ValidArgsFunction: validArgsFunc,
RunE: func(_ *cobra.Command, args []string) error {
client.OutputFormat = action.ShowChart
err := addRegistryClient(out, client)
err := addRegistryClient(client)
if err != nil {
return err
}
@ -147,7 +147,7 @@ func newShowCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
ValidArgsFunction: validArgsFunc,
RunE: func(_ *cobra.Command, args []string) error {
client.OutputFormat = action.ShowReadme
err := addRegistryClient(out, client)
err := addRegistryClient(client)
if err != nil {
return err
}
@ -168,7 +168,7 @@ func newShowCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
ValidArgsFunction: validArgsFunc,
RunE: func(_ *cobra.Command, args []string) error {
client.OutputFormat = action.ShowCRDs
err := addRegistryClient(out, client)
err := addRegistryClient(client)
if err != nil {
return err
}
@ -225,8 +225,8 @@ func runShow(args []string, client *action.Show) (string, error) {
return client.Run(cp)
}
func addRegistryClient(out io.Writer, client *action.Show) error {
registryClient, err := newRegistryClient(out, client.CertFile, client.KeyFile, client.CaFile,
func addRegistryClient(client *action.Show) error {
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
client.InsecureSkipTLSVerify, client.PlainHTTP, client.Username, client.Password)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)

@ -85,7 +85,7 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
client.KubeVersion = parsedKubeVersion
}
registryClient, err := newRegistryClient(out, client.CertFile, client.KeyFile, client.CaFile,
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
client.InsecureSkipTLSVerify, client.PlainHTTP, client.Username, client.Password)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)
@ -113,7 +113,6 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
}
return err
}
installErr := err
// We ignore a potential error here because, when the --debug flag was specified,
// we always want to print the YAML, even if it is not valid. The error is still returned afterwards.
@ -143,6 +142,7 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
return err
}
}
}
}
@ -187,10 +187,6 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
missing = false
}
if missing {
if installErr != nil && settings.Debug {
// assume the manifest itself is too malformed to be rendered
return installErr
}
return fmt.Errorf("could not find template %s in chart", f)
}
}
@ -202,7 +198,7 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
}
}
return installErr
return err
},
}

@ -133,12 +133,6 @@ func TestTemplateCmd(t *testing.T) {
wantError: true,
golden: "output/template-with-invalid-yaml-debug.txt",
},
{
name: "chart with template with invalid template expression (--debug, --show-only)",
cmd: fmt.Sprintf("template '%s' --debug --show-only %s", "testdata/testcharts/chart-with-template-with-invalid-template-expr", "templates/alpine-pod.yaml"),
wantError: true,
golden: "output/template-with-invalid-template-expr-debug-show-only.txt",
},
{
name: "template skip-tests",
cmd: fmt.Sprintf(`template '%s' --skip-tests`, chartPath),

@ -1,3 +0,0 @@
Error: chart-with-template-with-invalid-template-expr/templates/alpine-pod.yaml:7:38
executing "chart-with-template-with-invalid-template-expr/templates/alpine-pod.yaml" at <b64enc>:
invalid value; expected string

@ -1,8 +0,0 @@
apiVersion: v1
description: Deploy a basic Alpine Linux pod
home: https://helm.sh/helm
name: chart-with-template-with-invalid-template-expr
sources:
- https://github.com/helm/helm
version: 0.1.0
type: application

@ -1,13 +0,0 @@
#Alpine: A simple Helm chart
Run a single pod of Alpine Linux.
This example was generated using the command `helm create alpine`.
The `templates/` directory contains a very simple pod resource with a
couple of parameters.
The `values.yaml` file contains the default values for the
`alpine-pod.yaml` template.
You can install this example using `helm install ./alpine`.

@ -1,9 +0,0 @@
apiVersion: v1
kind: Pod
metadata:
name: "{{.Release.Name}}-{{.Values.Name}}"
spec:
containers:
- name: {{ .Values.nonExistentKey | b64enc }}
image: "alpine:3.9"
command: ["/bin/sleep","9000"]

@ -59,6 +59,7 @@ func newUninstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
return validationErr
}
for i := range args {
res, err := client.Run(args[i])
if err != nil {
return err

@ -105,7 +105,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
client.Namespace = settings.Namespace()
registryClient, err := newRegistryClient(out, client.CertFile, client.KeyFile, client.CaFile,
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
client.InsecureSkipTLSVerify, client.PlainHTTP, client.Username, client.Password)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)

@ -34,6 +34,7 @@ import (
)
func TestUpgradeCmd(t *testing.T) {
tmpChart := t.TempDir()
cfile := &chart.Chart{
Metadata: &chart.Metadata{
@ -221,6 +222,7 @@ func TestUpgradeWithValue(t *testing.T) {
if !strings.Contains(updatedRel.Manifest, "drink: tea") {
t.Errorf("The value is not set correctly. manifest: %s", updatedRel.Manifest)
}
}
func TestUpgradeWithStringValue(t *testing.T) {
@ -251,9 +253,11 @@ func TestUpgradeWithStringValue(t *testing.T) {
if !strings.Contains(updatedRel.Manifest, "drink: coffee") {
t.Errorf("The value is not set correctly. manifest: %s", updatedRel.Manifest)
}
}
func TestUpgradeInstallWithSubchartNotes(t *testing.T) {
releaseName := "wacky-bunny-v1"
relMock, ch, _ := prepareMockRelease(t, releaseName)
@ -285,9 +289,11 @@ func TestUpgradeInstallWithSubchartNotes(t *testing.T) {
if !strings.Contains(upgradedRel.Info.Notes, "SUBCHART NOTES") {
t.Errorf("The subchart notes are not set correctly. NOTES: %s", upgradedRel.Info.Notes)
}
}
func TestUpgradeWithValuesFile(t *testing.T) {
releaseName := "funny-bunny-v4"
relMock, ch, chartPath := prepareMockRelease(t, releaseName)
@ -315,9 +321,11 @@ func TestUpgradeWithValuesFile(t *testing.T) {
if !strings.Contains(updatedRel.Manifest, "drink: beer") {
t.Errorf("The value is not set correctly. manifest: %s", updatedRel.Manifest)
}
}
func TestUpgradeWithValuesFromStdin(t *testing.T) {
releaseName := "funny-bunny-v5"
relMock, ch, chartPath := prepareMockRelease(t, releaseName)
@ -353,6 +361,7 @@ func TestUpgradeWithValuesFromStdin(t *testing.T) {
}
func TestUpgradeInstallWithValuesFromStdin(t *testing.T) {
releaseName := "funny-bunny-v6"
_, _, chartPath := prepareMockRelease(t, releaseName)
@ -383,6 +392,7 @@ func TestUpgradeInstallWithValuesFromStdin(t *testing.T) {
if !strings.Contains(updatedRel.Manifest, "drink: beer") {
t.Errorf("The value is not set correctly. manifest: %s", updatedRel.Manifest)
}
}
func prepareMockRelease(t *testing.T, releaseName string) (func(n string, v int, ch *chart.Chart) *release.Release, *chart.Chart, string) {

@ -22,6 +22,7 @@ import (
)
func TestVerifyCmd(t *testing.T) {
statExe := "stat"
statPathMsg := "no such file or directory"
statFileMsg := statPathMsg

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save