diff --git a/cmd/hauler/cli/store.go b/cmd/hauler/cli/store.go index 87da7a5..17cccfd 100644 --- a/cmd/hauler/cli/store.go +++ b/cmd/hauler/cli/store.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/spf13/cobra" - "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v4/pkg/action" "hauler.dev/go/hauler/v2/cmd/hauler/cli/store" "hauler.dev/go/hauler/v2/internal/flags" diff --git a/cmd/hauler/cli/store/add.go b/cmd/hauler/cli/store/add.go index 8cb9762..a281131 100644 --- a/cmd/hauler/cli/store/add.go +++ b/cmd/hauler/cli/store/add.go @@ -11,9 +11,11 @@ import ( "github.com/google/go-containerregistry/pkg/name" ocispec "github.com/opencontainers/image-spec/specs-go/v1" - helmchart "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chartutil" - "helm.sh/helm/v3/pkg/engine" + "helm.sh/helm/v4/pkg/chart/common" + commonutil "helm.sh/helm/v4/pkg/chart/common/util" + helmchart "helm.sh/helm/v4/pkg/chart/v2" + "helm.sh/helm/v4/pkg/chart/v2/util" + "helm.sh/helm/v4/pkg/engine" "k8s.io/apimachinery/pkg/util/yaml" "hauler.dev/go/hauler/v2/internal/flags" @@ -462,9 +464,13 @@ func storeChart(ctx context.Context, s *store.Layout, cfg v1.Chart, opts *flags. defer os.RemoveAll(tempDir) chartPath := chrt.Path() - if strings.HasSuffix(chartPath, ".tgz") { + chartPathInfo, err := os.Stat(chartPath) + if err != nil { + return fmt.Errorf("failed to stat chart path '%s': %w", chartPath, err) + } + if !chartPathInfo.IsDir() { l.Debugf("%sextracting chart archive [%s]", prefix, filepath.Base(chartPath)) - if err := chartutil.ExpandFile(tempDir, chartPath); err != nil { + if err := util.ExpandFile(tempDir, chartPath); err != nil { return fmt.Errorf("failed to extract chart: %w", err) } @@ -478,20 +484,20 @@ func storeChart(ctx context.Context, s *store.Layout, cfg v1.Chart, opts *flags. // add-images if opts.AddImages { - userValues := chartutil.Values{} + userValues := common.Values{} if opts.HelmValues != "" { - userValues, err = chartutil.ReadValuesFile(opts.HelmValues) + userValues, err = common.ReadValuesFile(opts.HelmValues) if err != nil { return fmt.Errorf("failed to read helm values file [%s]: %w", opts.HelmValues, err) } } // set helm default capabilities - caps := chartutil.DefaultCapabilities.Copy() + caps := common.DefaultCapabilities.Copy() // only parse and override if provided kube version if opts.KubeVersion != "" { - kubeVersion, err := chartutil.ParseKubeVersion(opts.KubeVersion) + kubeVersion, err := common.ParseKubeVersion(opts.KubeVersion) if err != nil { l.Warnf("%sinvalid kube-version [%s], using default kubernetes version", prefix, opts.KubeVersion) } else { @@ -499,7 +505,7 @@ func storeChart(ctx context.Context, s *store.Layout, cfg v1.Chart, opts *flags. } } - values, err := chartutil.ToRenderValues(c, userValues, chartutil.ReleaseOptions{Namespace: "hauler"}, caps) + values, err := commonutil.ToRenderValues(c, userValues, common.ReleaseOptions{Namespace: "hauler"}, caps) if err != nil { return err } diff --git a/cmd/hauler/cli/store/add_test.go b/cmd/hauler/cli/store/add_test.go index cfed0c6..f7a98f0 100644 --- a/cmd/hauler/cli/store/add_test.go +++ b/cmd/hauler/cli/store/add_test.go @@ -13,8 +13,8 @@ import ( "github.com/google/go-containerregistry/pkg/registry" "github.com/google/go-containerregistry/pkg/v1/remote" ocispec "github.com/opencontainers/image-spec/specs-go/v1" - helmchart "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chartutil" + helmchart "helm.sh/helm/v4/pkg/chart/v2" + "helm.sh/helm/v4/pkg/chart/v2/util" "hauler.dev/go/hauler/v2/internal/flags" v1 "hauler.dev/go/hauler/v2/pkg/apis/hauler.cattle.io/v1" @@ -767,9 +767,9 @@ func seedChartWithImages(t *testing.T, dir string, images []string) string { }, } - saved, err := chartutil.Save(c, dir) + saved, err := util.Save(c, dir) if err != nil { - t.Fatalf("seedChartWithImages: chartutil.Save: %v", err) + t.Fatalf("seedChartWithImages: util.Save: %v", err) } return saved } diff --git a/cmd/hauler/cli/store/sync.go b/cmd/hauler/cli/store/sync.go index f70845b..c4e73c8 100644 --- a/cmd/hauler/cli/store/sync.go +++ b/cmd/hauler/cli/store/sync.go @@ -16,7 +16,7 @@ import ( "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/mitchellh/go-homedir" ocispec "github.com/opencontainers/image-spec/specs-go/v1" - "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v4/pkg/action" "k8s.io/apimachinery/pkg/util/yaml" "hauler.dev/go/hauler/v2/internal/flags" diff --git a/cmd/hauler/cli/store/testhelpers_test.go b/cmd/hauler/cli/store/testhelpers_test.go index fb78795..b44f59d 100644 --- a/cmd/hauler/cli/store/testhelpers_test.go +++ b/cmd/hauler/cli/store/testhelpers_test.go @@ -25,7 +25,7 @@ import ( digest "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/rs/zerolog" - "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v4/pkg/action" "hauler.dev/go/hauler/v2/internal/flags" "hauler.dev/go/hauler/v2/pkg/consts" diff --git a/go.mod b/go.mod index 93a14c9..b2487c0 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/spf13/cobra v1.10.2 golang.org/x/sync v0.21.0 gopkg.in/yaml.v3 v3.0.1 - helm.sh/helm/v3 v3.21.2 + helm.sh/helm/v4 v4.2.2 k8s.io/apimachinery v0.36.2 ) @@ -56,6 +56,7 @@ require ( github.com/Masterminds/sprig/v3 v3.3.0 // indirect github.com/Masterminds/squirrel v1.5.4 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/ProtonMail/go-crypto v1.4.1 // indirect github.com/STARRY-S/zip v0.2.3 // indirect github.com/ThalesIgnite/crypto11 v1.2.5 // indirect github.com/agnivade/levenshtein v1.2.1 // indirect @@ -107,6 +108,7 @@ require ( github.com/clbanning/mxj/v2 v2.7.0 // indirect github.com/clipperhouse/displaywidth v0.10.0 // indirect github.com/clipperhouse/uax29/v2 v2.6.0 // indirect + github.com/cloudflare/circl v1.6.3 // indirect github.com/cockroachdb/apd/v3 v3.2.1 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/containerd/log v0.1.0 // indirect @@ -129,16 +131,19 @@ require ( github.com/docker/go-units v0.5.0 // indirect github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect github.com/dustin/go-humanize v1.0.1 // indirect + github.com/dylibso/observe-sdk/go v0.0.0-20240819160327-2d926c5d788a // indirect github.com/emicklei/go-restful/v3 v3.13.0 // indirect github.com/emicklei/proto v1.14.3 // indirect - github.com/evanphx/json-patch v5.9.11+incompatible // indirect + github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect - github.com/fatih/color v1.18.0 // indirect + github.com/extism/go-sdk v1.7.1 // indirect + github.com/fatih/color v1.19.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fluxcd/cli-utils v1.2.1 // indirect github.com/fsnotify/fsnotify v1.10.1 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-chi/chi/v5 v5.3.0 // indirect - github.com/go-errors/errors v1.4.2 // indirect + github.com/go-errors/errors v1.5.1 // indirect github.com/go-gorp/gorp/v3 v3.1.0 // indirect github.com/go-jose/go-jose/v4 v4.1.4 // indirect github.com/go-logr/logr v1.4.3 // indirect @@ -169,6 +174,7 @@ require ( github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/goccy/go-json v0.10.6 // indirect + github.com/gofrs/flock v0.13.0 // indirect github.com/golang-jwt/jwt/v4 v4.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.1.3 // indirect @@ -183,13 +189,12 @@ require ( github.com/googleapis/gax-go/v2 v2.22.0 // indirect github.com/gosuri/uitable v0.0.4 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-retryablehttp v0.7.8 // indirect github.com/hashicorp/golang-lru/arc/v2 v2.0.5 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/huandu/xstrings v1.5.0 // indirect + github.com/ianlancetaylor/demangle v0.0.0-20240805132620-81f5be970eca // indirect github.com/in-toto/attestation v1.2.0 // indirect github.com/in-toto/in-toto-golang v0.11.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -279,6 +284,8 @@ require ( github.com/subosito/gotenv v1.6.0 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/tchap/go-patricia/v2 v2.3.3 // indirect + github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834 // indirect + github.com/tetratelabs/wazero v1.12.0 // indirect github.com/thales-e-security/pool v0.0.2 // indirect github.com/theupdateframework/go-tuf v0.7.0 // indirect github.com/theupdateframework/go-tuf/v2 v2.4.2 // indirect @@ -353,6 +360,7 @@ require ( k8s.io/kubectl v0.36.2 // indirect k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 // indirect oras.land/oras-go/v2 v2.6.1 // indirect + sigs.k8s.io/controller-runtime v0.24.1 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/kustomize/api v0.21.1 // indirect sigs.k8s.io/kustomize/kyaml v0.21.1 // indirect diff --git a/go.sum b/go.sum index 0604b85..9b94aff 100644 --- a/go.sum +++ b/go.sum @@ -111,6 +111,8 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Microsoft/hcsshim v0.11.7 h1:vl/nj3Bar/CvJSYo7gIQPyRWc9f3c6IeSNavBTSZNZQ= github.com/Microsoft/hcsshim v0.11.7/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA09d4bExKcU= +github.com/ProtonMail/go-crypto v1.4.1 h1:9RfcZHqEQUvP8RzecWEUafnZVtEvrBVL9BiF67IQOfM= +github.com/ProtonMail/go-crypto v1.4.1/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo= github.com/STARRY-S/zip v0.2.3 h1:luE4dMvRPDOWQdeDdUxUoZkzUIpTccdKdhHHsQJ1fm4= github.com/STARRY-S/zip v0.2.3/go.mod h1:lqJ9JdeRipyOQJrYSOtpNAiaesFO6zVDsE8GIGFaoSk= github.com/ThalesIgnite/crypto11 v1.2.5 h1:1IiIIEqYmBvUYFeMnHqRft4bwf/O36jryEUpY+9ef8E= @@ -260,6 +262,8 @@ github.com/clipperhouse/displaywidth v0.10.0 h1:GhBG8WuerxjFQQYeuZAeVTuyxuX+Urai github.com/clipperhouse/displaywidth v0.10.0/go.mod h1:XqJajYsaiEwkxOj4bowCTMcT1SgvHo9flfF3jQasdbs= github.com/clipperhouse/uax29/v2 v2.6.0 h1:z0cDbUV+aPASdFb2/ndFnS9ts/WNXgTNNGFoKXuhpos= github.com/clipperhouse/uax29/v2 v2.6.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= +github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8= +github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg= github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc= @@ -340,6 +344,8 @@ github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707/go.mod h1:qssHWj6 github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/dylibso/observe-sdk/go v0.0.0-20240819160327-2d926c5d788a h1:UwSIFv5g5lIvbGgtf3tVwC7Ky9rmMFBp0RMs+6f6YqE= +github.com/dylibso/observe-sdk/go v0.0.0-20240819160327-2d926c5d788a/go.mod h1:C8DzXehI4zAbrdlbtOByKX6pfivJTBiV9Jjqv56Yd9Q= github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emicklei/proto v1.14.3 h1:zEhlzNkpP8kN6utonKMzlPfIvy82t5Kb9mufaJxSe1Q= @@ -348,14 +354,18 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8= -github.com/evanphx/json-patch v5.9.11+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= +github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2Wvf/TIe2xdyJxTlb6obmF18d8QdkxNDu4= github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc= -github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= -github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/extism/go-sdk v1.7.1 h1:lWJos6uY+tRFdlIHR+SJjwFDApY7OypS/2nMhiVQ9Sw= +github.com/extism/go-sdk v1.7.1/go.mod h1:IT+Xdg5AZM9hVtpFUA+uZCJMge/hbvshl8bwzLtFyKA= +github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w= +github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE= 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 v1.2.1 h1:ug9CicKW7H9QXnvNDapTSKuryZvWcu4Nw7pRvQa6jDY= +github.com/fluxcd/cli-utils v1.2.1/go.mod h1:cky6M6eHvTQkoPtsuFYLIgAMYdpTCSLoor4IA6vueSw= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/foxcpp/go-mockdns v1.2.0 h1:omK3OrHRD1IWJz1FuFBCFquhXslXoF17OvBS6JPzZF0= @@ -371,8 +381,8 @@ github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sa github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/go-chi/chi/v5 v5.3.0 h1:halUjDxhshgXHMrao5bB8eNBXo/rnzwr8m5m36glehM= github.com/go-chi/chi/v5 v5.3.0/go.mod h1:R+tYY2hNuVUUjxoPtqUdgBqevM9s9njzkTLutVsOCto= -github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= -github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= +github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= @@ -387,6 +397,8 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= +github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= github.com/go-openapi/analysis v0.25.2 h1:I0vy4n3alz+DHTiN1PRhCb7QZxkK6g5YmswZKv2TKuw= github.com/go-openapi/analysis v0.25.2/go.mod h1:Uhs1t/2XR10EnwONYILGEzw8gcfGIG5Xk5K2AxnhqDo= github.com/go-openapi/errors v0.22.7 h1:JLFBGC0Apwdzw3484MmBqspjPbwa2SHvpDm0u5aGhUA= @@ -461,6 +473,8 @@ github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU= github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ= github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c= +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= @@ -559,7 +573,6 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDa github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk= github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= @@ -595,6 +608,8 @@ github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20240805132620-81f5be970eca h1:T54Ema1DU8ngI+aef9ZhAhNGQhcRTrWxVeG07F+c/Rw= +github.com/ianlancetaylor/demangle v0.0.0-20240805132620-81f5be970eca/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= github.com/in-toto/attestation v1.2.0 h1:aPRUZ3azbqD7yEBD5fP3TD8Dszf+YHo284SOcpahjQk= github.com/in-toto/attestation v1.2.0/go.mod h1:r79G45gOmzPismgObLSL+rZTFxUgZLOQJI6LofTZgXk= github.com/in-toto/in-toto-golang v0.11.0 h1:nfidMYBFx+E0lnmX5KUnN2Pdm8zdNKal1ayjJuzzRoA= @@ -774,8 +789,6 @@ github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0 github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= -github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= @@ -925,6 +938,10 @@ github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDd github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/tchap/go-patricia/v2 v2.3.3 h1:xfNEsODumaEcCcY3gI0hYPZ/PcpVv5ju6RMAhgwZDDc= github.com/tchap/go-patricia/v2 v2.3.3/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= +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/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gtvVDbmPg= github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU= github.com/theupdateframework/go-tuf v0.7.0 h1:CqbQFrWo1ae3/I0UCblSbczevCCbS31Qvs5LdxRWqRI= @@ -1353,8 +1370,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= -helm.sh/helm/v3 v3.21.2 h1:O8ktw30zQZm4tNRavKRN44rotcHICdbSU3e6gS7CCbQ= -helm.sh/helm/v3 v3.21.2/go.mod h1:Da+9m67Mzg42rQNrWAj0RvUd4tDT4dL8mrxUwvRgYWo= +helm.sh/helm/v4 v4.2.2 h1:E2zSCA2uUm9PNiZsSC/BioDVGsYk7nF2jNJFg/i+Dng= +helm.sh/helm/v4 v4.2.2/go.mod h1:dp3ihfy1AhCLKANDaPETmVWhqPkOmwvJtpK/biHfopE= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1389,6 +1406,8 @@ pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/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= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/kustomize/api v0.21.1 h1:lzqbzvz2CSvsjIUZUBNFKtIMsEw7hVLJp0JeSIVmuJs= diff --git a/internal/flags/add.go b/internal/flags/add.go index 7613df0..9c8f45b 100644 --- a/internal/flags/add.go +++ b/internal/flags/add.go @@ -2,7 +2,7 @@ package flags import ( "github.com/spf13/cobra" - "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v4/pkg/action" ) type AddImageOpts struct { @@ -70,7 +70,7 @@ func (o *AddChartOpts) AddFlags(cmd *cobra.Command) { f.StringVar(&o.ChartOpts.Password, "password", "", "(Optional) Password to use for authentication") f.StringVar(&o.ChartOpts.CertFile, "cert-file", "", "(Optional) Location of the TLS Certificate to use for client authentication") f.StringVar(&o.ChartOpts.KeyFile, "key-file", "", "(Optional) Location of the TLS Key to use for client authentication") - f.BoolVar(&o.ChartOpts.InsecureSkipTLSverify, "insecure-skip-tls-verify", false, "(Optional) Skip TLS certificate verification") + f.BoolVar(&o.ChartOpts.InsecureSkipTLSVerify, "insecure-skip-tls-verify", false, "(Optional) Skip TLS certificate verification") f.StringVar(&o.ChartOpts.CaFile, "ca-file", "", "(Optional) Location of CA Bundle to enable certification verification") f.StringVar(&o.Rewrite, "rewrite", "", "(Optional) Rewrite artifact path to specified string") diff --git a/pkg/content/chart/chart.go b/pkg/content/chart/chart.go index b83741c..1ab2712 100644 --- a/pkg/content/chart/chart.go +++ b/pkg/content/chart/chart.go @@ -4,10 +4,13 @@ import ( "archive/tar" "bytes" "compress/gzip" + "crypto/tls" + "crypto/x509" "encoding/json" "fmt" "io" "io/fs" + "net/http" "net/url" "os" "path/filepath" @@ -17,12 +20,11 @@ import ( gtypes "github.com/google/go-containerregistry/pkg/v1/types" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "hauler.dev/go/hauler/v2/pkg/artifacts" - "hauler.dev/go/hauler/v2/pkg/log" - "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chart/loader" - "helm.sh/helm/v3/pkg/cli" - "helm.sh/helm/v3/pkg/registry" + "helm.sh/helm/v4/pkg/action" + "helm.sh/helm/v4/pkg/chart/v2" + "helm.sh/helm/v4/pkg/chart/v2/loader" + "helm.sh/helm/v4/pkg/cli" + "helm.sh/helm/v4/pkg/registry" "hauler.dev/go/hauler/v2/pkg/consts" "hauler.dev/go/hauler/v2/pkg/layer" @@ -43,7 +45,7 @@ type Chart struct { func NewChart(name string, opts *action.ChartPathOptions) (*Chart, error) { chartRef := name actionConfig := new(action.Configuration) - if err := actionConfig.Init(settings.RESTClientGetter(), settings.Namespace(), os.Getenv("HELM_DRIVER"), log.NewLogger(os.Stdout).Debugf); err != nil { + if err := actionConfig.Init(settings.RESTClientGetter(), settings.Namespace(), os.Getenv("HELM_DRIVER")); err != nil { return nil, err } @@ -51,7 +53,7 @@ func NewChart(name string, opts *action.ChartPathOptions) (*Chart, error) { client.ChartPathOptions.Version = opts.Version registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile, - client.InsecureSkipTLSverify, client.PlainHTTP) + client.InsecureSkipTLSVerify, client.PlainHTTP) if err != nil { return nil, fmt.Errorf("missing registry client: %w", err) } @@ -148,7 +150,7 @@ func (h *Chart) configDescriptor() (gv1.Descriptor, error) { }, nil } -func (h *Chart) Load() (*chart.Chart, error) { +func (h *Chart) Load() (*v2.Chart, error) { return loader.Load(h.path) } @@ -234,8 +236,25 @@ func (h *Chart) chartData() (gv1.Layer, error) { chartdata = data } + // title defaults to the downloaded file's basename. Helm v4's + // ChartPathOptions.LocateChart downloads any non-local chart (HTTP repo or + // OCI) into a content-addressed cache and returns a hash-named path (e.g. + // ".chart", using the literal extension defined by + // downloader.CacheChart) rather than a human-readable filename, so + // filepath.Base(h.path) is meaningless for those sources. In that specific + // case only, prefer the canonical "-.tgz" form derived from + // the chart's own Chart.yaml metadata. Genuinely local archives (".tgz" or + // any other extension a user's file might carry) keep their real filename, + // even if it doesn't follow the "-.tgz" convention. + title := filepath.Base(h.path) + if !info.IsDir() && filepath.Ext(h.path) == ".chart" { + if ch, err := loader.Load(h.path); err == nil && ch.Metadata != nil && ch.Metadata.Name != "" && ch.Metadata.Version != "" { + title = fmt.Sprintf("%s-%s.tgz", ch.Metadata.Name, ch.Metadata.Version) + } + } + annotations := make(map[string]string) - annotations[ocispec.AnnotationTitle] = filepath.Base(h.path) + annotations[ocispec.AnnotationTitle] = title opener := func() layer.Opener { return func() (io.ReadCloser, error) { @@ -287,14 +306,27 @@ func newDefaultRegistryClient(plainHTTP bool) (*registry.Client, error) { return registryClient, nil } +// newRegistryClientWithTLS builds a registry client backed by an HTTP client with a custom +// TLS config. Helm v4 removed the registry.NewRegistryClientWithTLS convenience wrapper (it +// delegated to helm's internal/tlsutil package, which is not importable outside the helm +// module), so the TLS config construction is inlined here to match its prior behavior. func newRegistryClientWithTLS(certFile, keyFile, caFile string, insecureSkipTLSverify bool) (*registry.Client, error) { - // create a new registry client - registryClient, err := registry.NewRegistryClientWithTLS( - io.Discard, - certFile, keyFile, caFile, - insecureSkipTLSverify, - settings.RegistryConfig, - settings.Debug, + tlsConf, err := newTLSConfig(certFile, keyFile, caFile, insecureSkipTLSverify) + if err != nil { + return nil, fmt.Errorf("can't create TLS config for client: %w", err) + } + + registryClient, err := registry.NewClient( + registry.ClientOptDebug(settings.Debug), + registry.ClientOptEnableCache(true), + registry.ClientOptWriter(io.Discard), + registry.ClientOptCredentialsFile(settings.RegistryConfig), + registry.ClientOptHTTPClient(&http.Client{ + Transport: &http.Transport{ + TLSClientConfig: tlsConf, + Proxy: http.ProxyFromEnvironment, + }, + }), ) if err != nil { return nil, err @@ -302,6 +334,44 @@ func newRegistryClientWithTLS(certFile, keyFile, caFile string, insecureSkipTLSv return registryClient, nil } +// newTLSConfig constructs a *tls.Config from the given cert/key/CA files, mirroring the +// behavior of helm's internal tlsutil.NewTLSConfig. +func newTLSConfig(certFile, keyFile, caFile string, insecureSkipTLSverify bool) (*tls.Config, error) { + config := &tls.Config{ + InsecureSkipVerify: insecureSkipTLSverify, + } + + if certFile != "" && keyFile != "" { + certPEMBlock, err := os.ReadFile(certFile) + if err != nil { + return nil, fmt.Errorf("unable to read cert file: %q: %w", certFile, err) + } + keyPEMBlock, err := os.ReadFile(keyFile) + if err != nil { + return nil, fmt.Errorf("unable to read key file: %q: %w", keyFile, err) + } + cert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock) + if err != nil { + return nil, fmt.Errorf("unable to load cert from key pair: %w", err) + } + config.Certificates = []tls.Certificate{cert} + } + + if caFile != "" { + caPEMBlock, err := os.ReadFile(caFile) + if err != nil { + return nil, fmt.Errorf("can't read CA file: %q: %w", caFile, err) + } + cp := x509.NewCertPool() + if !cp.AppendCertsFromPEM(caPEMBlock) { + return nil, fmt.Errorf("failed to append certificates from pem block") + } + config.RootCAs = cp + } + + return config, nil +} + // path returns the local filesystem path to the chart archive or directory func (h *Chart) Path() string { return h.path diff --git a/pkg/content/chart/chart_test.go b/pkg/content/chart/chart_test.go index b6a65bc..eff9304 100644 --- a/pkg/content/chart/chart_test.go +++ b/pkg/content/chart/chart_test.go @@ -7,7 +7,7 @@ import ( v1 "github.com/google/go-containerregistry/pkg/v1" ocispec "github.com/opencontainers/image-spec/specs-go/v1" - "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v4/pkg/action" "hauler.dev/go/hauler/v2/pkg/consts" "hauler.dev/go/hauler/v2/pkg/content/chart" @@ -49,6 +49,30 @@ func TestNewChart(t *testing.T) { }, wantErr: false, }, + { + // Local chart archives keep the caller-provided filename as the OCI + // title annotation even when it doesn't follow the "-.tgz" + // convention (e.g. a user-renamed or repackaged chart file). Only + // Helm v4's content-addressed cache paths (".chart" extension) should + // fall back to a metadata-derived title. + name: "should preserve real filename for local archive with mismatched name", + args: args{ + name: "mismatched-filename-chart.tgz", + opts: &action.ChartPathOptions{RepoURL: "../../../testdata"}, + }, + want: v1.Descriptor{ + MediaType: consts.ChartLayerMediaType, + Size: 14970, + Digest: v1.Hash{ + Algorithm: "sha256", + Hex: "0905de044a6e57cf3cd27bfc8482753049920050b10347ae2315599bd982a0e3", + }, + Annotations: map[string]string{ + ocispec.AnnotationTitle: "mismatched-filename-chart.tgz", + }, + }, + wantErr: false, + }, // TODO: This isn't matching digests b/c of file timestamps not being respected // { // name: "should create from a chart directory", diff --git a/testdata/mismatched-filename-chart.tgz b/testdata/mismatched-filename-chart.tgz new file mode 100644 index 0000000..d596e49 Binary files /dev/null and b/testdata/mismatched-filename-chart.tgz differ