diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 99481d611..5971ada24 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -21,6 +21,6 @@ jobs: go-version: '1.23' check-latest: true - name: golangci-lint - uses: golangci/golangci-lint-action@e60da84bfae8c7920a47be973d75e15710aa8bd7 #pin@6.3.0 + uses: golangci/golangci-lint-action@2226d7cb06a077cd73e56eedd38eecad18e5d837 #pin@6.5.0 with: version: v1.62 diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 7f568cf9d..f89fcd98c 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -33,7 +33,7 @@ jobs: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0 + uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1 with: results_file: results.sarif results_format: sarif @@ -55,7 +55,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 with: name: SARIF file path: results.sarif diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0c2f88453..8ab93403d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -276,12 +276,26 @@ Like any good open source project, we use Pull Requests (PRs) to track code chan or explicitly request another OWNER do that for them. - If the owner of a PR is _not_ listed in `OWNERS`, any core maintainer may merge the PR. -#### Documentation PRs +### Documentation PRs Documentation PRs should be made on the docs repo: . Keeping Helm's documentation up to date is highly desirable, and is recommended for all user facing changes. Accurate and helpful documentation is critical for effectively communicating Helm's behavior to a wide audience. Small, ad-hoc changes/PRs to Helm which introduce user facing changes, which would benefit from documentation changes, should apply the `docs needed` label. Larger changes associated with a HIP should track docs via that HIP. The `docs needed` label doesn't block PRs, and maintainers/PR reviewers should apply discretion judging in whether the `docs needed` label should be applied. +### Profiling PRs + +If your contribution requires profiling to check memory and/or CPU usage, you can set `HELM_PPROF_CPU_PROFILE=/path/to/cpu.prof` and/or `HELM_PPROF_MEM_PROFILE=/path/to/mem.prof` environment variables to collect runtime profiling data for analysis. You can use Golang's [pprof](https://github.com/google/pprof/blob/main/doc/README.md) tool to inspect the results. + +Example analysing collected profiling data +``` +HELM_PPROF_CPU_PROFILE=cpu.prof HELM_PPROF_MEM_PROFILE=mem.prof helm show all bitnami/nginx + +# Visualize graphs. You need to have installed graphviz package in your system +go tool pprof -http=":8000" cpu.prof + +go tool pprof -http=":8001" mem.prof +``` + ## The Triager Each week, one of the core maintainers will serve as the designated "triager" starting after the diff --git a/KEYS b/KEYS index f14c596c7..e772fff40 100644 --- a/KEYS +++ b/KEYS @@ -1037,3 +1037,24 @@ nk38BkgHg3LHjCbCNEVkSK2TMT69A58iwpY9WUQlphsiz4WBpafSPbv/jSlsm7uK TNWtbFGBRpJyEg== =w141 -----END PGP PUBLIC KEY BLOCK----- +pub ed25519 2024-07-09 [SC] + 7FEC81FACC7FFB2A010ADD13C2D40F4D8196E874 +uid [ultimate] Robert Sirchia (I like turtles.) +sig 3 C2D40F4D8196E874 2024-07-09 [self-signature] +sub cv25519 2024-07-09 [E] +sig C2D40F4D8196E874 2024-07-09 [self-signature] + +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEZo2C6xYJKwYBBAHaRw8BAQdA8kCWaI+FlCabcTw8EVeiMkokyWDalgl/Inbn +ACcGN1e0N1JvYmVydCBTaXJjaGlhIChJIGxpa2UgdHVydGxlcy4pIDxyc2lyY2hp +YUBvdXRsb29rLmNvbT6IkwQTFgoAOxYhBH/sgfrMf/sqAQrdE8LUD02Bluh0BQJm +jYLrAhsDBQsJCAcCAiICBhUKCQgLAgQWAgMBAh4HAheAAAoJEMLUD02Bluh0dyYA +/i7RB6m3MXNA8ei7GD8uQVpLfCRgEFsqSS/AzAOu8NGhAQCbw1kWL3AUll7KKtiQ +UE96nhCk+HnkQeVkWYS+MZ1tALg4BGaNgusSCisGAQQBl1UBBQEBB0CCA6Au4krL +YinQq9aAs29fFeRu/ye3PqQuz5jZ2r1ScAMBCAeIeAQYFgoAIBYhBH/sgfrMf/sq +AQrdE8LUD02Bluh0BQJmjYLrAhsMAAoJEMLUD02Bluh0KH4BAMSwEIGkoQl10LN3 +K6V08VpFmniENmCDHshXYq0gGiTDAP9FsXl2UtmFU5xuYxH4fRKIxgmxJRAFMWI8 +u3Rdu/s+DQ== +=smBO +-----END PGP PUBLIC KEY BLOCK----- diff --git a/Makefile b/Makefile index f1fcbfb08..d2c82b033 100644 --- a/Makefile +++ b/Makefile @@ -65,8 +65,8 @@ K8S_MODULES_MINOR_VER=$(word 2,$(K8S_MODULES_VER)) LDFLAGS += -X helm.sh/helm/v4/pkg/lint/rules.k8sVersionMajor=$(K8S_MODULES_MAJOR_VER) LDFLAGS += -X helm.sh/helm/v4/pkg/lint/rules.k8sVersionMinor=$(K8S_MODULES_MINOR_VER) -LDFLAGS += -X helm.sh/helm/v4/pkg/chartutil.k8sVersionMajor=$(K8S_MODULES_MAJOR_VER) -LDFLAGS += -X helm.sh/helm/v4/pkg/chartutil.k8sVersionMinor=$(K8S_MODULES_MINOR_VER) +LDFLAGS += -X helm.sh/helm/v4/pkg/chart/util.k8sVersionMajor=$(K8S_MODULES_MAJOR_VER) +LDFLAGS += -X helm.sh/helm/v4/pkg/chart/util.k8sVersionMinor=$(K8S_MODULES_MINOR_VER) .PHONY: all all: build diff --git a/cmd/helm/create.go b/cmd/helm/create.go index 29beca303..a18f2c915 100644 --- a/cmd/helm/create.go +++ b/cmd/helm/create.go @@ -25,7 +25,7 @@ import ( "helm.sh/helm/v4/cmd/helm/require" "helm.sh/helm/v4/pkg/chart" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/helmpath" ) diff --git a/cmd/helm/create_test.go b/cmd/helm/create_test.go index 88d9c315a..76fce804c 100644 --- a/cmd/helm/create_test.go +++ b/cmd/helm/create_test.go @@ -25,7 +25,7 @@ import ( "helm.sh/helm/v4/internal/test/ensure" "helm.sh/helm/v4/pkg/chart" "helm.sh/helm/v4/pkg/chart/loader" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/helmpath" ) diff --git a/cmd/helm/dependency_build_test.go b/cmd/helm/dependency_build_test.go index 189378ce5..76c01d911 100644 --- a/cmd/helm/dependency_build_test.go +++ b/cmd/helm/dependency_build_test.go @@ -22,18 +22,18 @@ import ( "strings" "testing" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/provenance" "helm.sh/helm/v4/pkg/repo" "helm.sh/helm/v4/pkg/repo/repotest" ) func TestDependencyBuildCmd(t *testing.T) { - srv, err := repotest.NewTempServerWithCleanup(t, "testdata/testcharts/*.tgz") + srv := repotest.NewTempServer( + t, + repotest.WithChartSourceGlob("testdata/testcharts/*.tgz"), + ) defer srv.Stop() - if err != nil { - t.Fatal(err) - } rootDir := srv.Root() srv.LinkIndices() diff --git a/cmd/helm/dependency_update_test.go b/cmd/helm/dependency_update_test.go index 82a6b875d..0732ba7b5 100644 --- a/cmd/helm/dependency_update_test.go +++ b/cmd/helm/dependency_update_test.go @@ -24,7 +24,7 @@ import ( "helm.sh/helm/v4/internal/test/ensure" "helm.sh/helm/v4/pkg/chart" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/helmpath" "helm.sh/helm/v4/pkg/provenance" "helm.sh/helm/v4/pkg/repo" @@ -32,10 +32,10 @@ import ( ) func TestDependencyUpdateCmd(t *testing.T) { - srv, err := repotest.NewTempServerWithCleanup(t, "testdata/testcharts/*.tgz") - if err != nil { - t.Fatal(err) - } + srv := repotest.NewTempServer( + t, + repotest.WithChartSourceGlob("testdata/testcharts/*.tgz"), + ) defer srv.Stop() t.Logf("Listening on directory %s", srv.Root()) @@ -151,10 +151,10 @@ func TestDependencyUpdateCmd_DoNotDeleteOldChartsOnError(t *testing.T) { defer resetEnv()() ensure.HelmHome(t) - srv, err := repotest.NewTempServerWithCleanup(t, "testdata/testcharts/*.tgz") - if err != nil { - t.Fatal(err) - } + srv := repotest.NewTempServer( + t, + repotest.WithChartSourceGlob("testdata/testcharts/*.tgz"), + ) defer srv.Stop() t.Logf("Listening on directory %s", srv.Root()) @@ -248,10 +248,10 @@ func TestDependencyUpdateCmd_WithRepoThatWasNotAdded(t *testing.T) { } func setupMockRepoServer(t *testing.T) *repotest.Server { - srv, err := repotest.NewTempServerWithCleanup(t, "testdata/testcharts/*.tgz") - if err != nil { - t.Fatal(err) - } + srv := repotest.NewTempServer( + t, + repotest.WithChartSourceGlob("testdata/testcharts/*.tgz"), + ) t.Logf("Listening on directory %s", srv.Root()) diff --git a/cmd/helm/flags.go b/cmd/helm/flags.go index c73bab63f..77af6544d 100644 --- a/cmd/helm/flags.go +++ b/cmd/helm/flags.go @@ -48,7 +48,7 @@ func addValueOptionsFlags(f *pflag.FlagSet, v *values.Options) { f.StringArrayVar(&v.Values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") f.StringArrayVar(&v.StringValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") f.StringArrayVar(&v.FileValues, "set-file", []string{}, "set values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2)") - f.StringArrayVar(&v.JSONValues, "set-json", []string{}, "set JSON values on the command line (can specify multiple or separate values with commas: key1=jsonval1,key2=jsonval2)") + f.StringArrayVar(&v.JSONValues, "set-json", []string{}, "set JSON values on the command line (can specify multiple or separate values with commas: key1=jsonval1,key2=jsonval2 or using json format: {\"key1\": jsonval1, \"key2\": \"jsonval2\"})") f.StringArrayVar(&v.LiteralValues, "set-literal", []string{}, "set a literal STRING value on the command line") } diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index aa981740f..c8de18796 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -57,6 +57,11 @@ func warning(format string, v ...interface{}) { fmt.Fprintf(os.Stderr, format, v...) } +// hookOutputWriter provides the writer for writing hook logs. +func hookOutputWriter(_, _, _ string) io.Writer { + return log.Writer() +} + func main() { // Setting the name of the app for managedFields in the Kubernetes client. // It is set here to the full name of "helm" so that renaming of helm to @@ -80,6 +85,7 @@ func main() { if helmDriver == "memory" { loadReleasesInMemory(actionConfig) } + actionConfig.SetHookOutputFunc(hookOutputWriter) }) if err := cmd.Execute(); err != nil { diff --git a/cmd/helm/helm_test.go b/cmd/helm/helm_test.go index a20928b37..e7a05aecf 100644 --- a/cmd/helm/helm_test.go +++ b/cmd/helm/helm_test.go @@ -30,7 +30,7 @@ import ( "helm.sh/helm/v4/internal/test" "helm.sh/helm/v4/pkg/action" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/cli" kubefake "helm.sh/helm/v4/pkg/kube/fake" "helm.sh/helm/v4/pkg/release" diff --git a/cmd/helm/history.go b/cmd/helm/history.go index 91d005e7a..2c929c161 100644 --- a/cmd/helm/history.go +++ b/cmd/helm/history.go @@ -30,7 +30,7 @@ import ( "helm.sh/helm/v4/pkg/chart" "helm.sh/helm/v4/pkg/cli/output" "helm.sh/helm/v4/pkg/release" - "helm.sh/helm/v4/pkg/releaseutil" + releaseutil "helm.sh/helm/v4/pkg/release/util" helmtime "helm.sh/helm/v4/pkg/time" ) diff --git a/cmd/helm/install.go b/cmd/helm/install.go index 4d72be966..ac6157dac 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -52,7 +52,7 @@ or use the '--set' flag and pass configuration from the command line, to force a string value use '--set-string'. You can use '--set-file' to set individual values from a file when the value itself is too long for the command line or is dynamically generated. You can also use '--set-json' to set json values -(scalars/objects/arrays) from the command line. +(scalars/objects/arrays) from the command line. Additionally, you can use '--set-json' and passing json object as a string. $ helm install -f myvalues.yaml myredis ./redis @@ -72,6 +72,9 @@ or $ helm install --set-json 'master.sidecars=[{"name":"sidecar","image":"myImage","imagePullPolicy":"Always","ports":[{"name":"portname","containerPort":1234}]}]' myredis ./redis +or + + $ helm install --set-json '{"master":{"sidecars":[{"name":"sidecar","image":"myImage","imagePullPolicy":"Always","ports":[{"name":"portname","containerPort":1234}]}]}}' myredis ./redis You can specify the '--values'/'-f' flag multiple times. The priority will be given to the last (right-most) file specified. For example, if both myvalues.yaml and override.yaml diff --git a/cmd/helm/install_test.go b/cmd/helm/install_test.go index e7b6e1dff..be8480423 100644 --- a/cmd/helm/install_test.go +++ b/cmd/helm/install_test.go @@ -27,19 +27,13 @@ import ( ) func TestInstall(t *testing.T) { - srv, err := repotest.NewTempServerWithCleanup(t, "testdata/testcharts/*.tgz*") - if err != nil { - t.Fatal(err) - } + srv := repotest.NewTempServer( + t, + repotest.WithChartSourceGlob("testdata/testcharts/*.tgz*"), + repotest.WithMiddleware(repotest.BasicAuthMiddleware(t)), + ) defer srv.Stop() - srv.WithMiddleware(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) { - username, password, ok := r.BasicAuth() - if !ok || username != "username" || password != "password" { - t.Errorf("Expected request to use basic auth and for username == 'username' and password == 'password', got '%v', '%s', '%s'", ok, username, password) - } - })) - srv2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.FileServer(http.Dir(srv.Root())).ServeHTTP(w, r) })) diff --git a/cmd/helm/lint.go b/cmd/helm/lint.go index 9a3f7a2fc..3e37922b2 100644 --- a/cmd/helm/lint.go +++ b/cmd/helm/lint.go @@ -27,7 +27,7 @@ import ( "github.com/spf13/cobra" "helm.sh/helm/v4/pkg/action" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/cli/values" "helm.sh/helm/v4/pkg/getter" "helm.sh/helm/v4/pkg/lint/support" diff --git a/cmd/helm/profiling.go b/cmd/helm/profiling.go new file mode 100644 index 000000000..950ad15da --- /dev/null +++ b/cmd/helm/profiling.go @@ -0,0 +1,91 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "errors" + "fmt" + "os" + "runtime" + "runtime/pprof" +) + +var ( + cpuProfileFile *os.File + cpuProfilePath string + memProfilePath string +) + +func init() { + cpuProfilePath = os.Getenv("HELM_PPROF_CPU_PROFILE") + memProfilePath = os.Getenv("HELM_PPROF_MEM_PROFILE") +} + +// startProfiling starts profiling CPU usage if HELM_PPROF_CPU_PROFILE is set +// to a file path. It returns an error if the file could not be created or +// CPU profiling could not be started. +func startProfiling() error { + if cpuProfilePath != "" { + var err error + cpuProfileFile, err = os.Create(cpuProfilePath) + if err != nil { + return fmt.Errorf("could not create CPU profile: %w", err) + } + if err := pprof.StartCPUProfile(cpuProfileFile); err != nil { + cpuProfileFile.Close() + cpuProfileFile = nil + return fmt.Errorf("could not start CPU profile: %w", err) + } + } + return nil +} + +// stopProfiling stops profiling CPU and memory usage. +// It writes memory profile to the file path specified in HELM_PPROF_MEM_PROFILE +// environment variable. +func stopProfiling() error { + errs := []error{} + + // Stop CPU profiling if it was started + if cpuProfileFile != nil { + pprof.StopCPUProfile() + err := cpuProfileFile.Close() + if err != nil { + errs = append(errs, err) + } + cpuProfileFile = nil + } + + if memProfilePath != "" { + f, err := os.Create(memProfilePath) + if err != nil { + errs = append(errs, err) + } + defer f.Close() + + runtime.GC() // get up-to-date statistics + if err := pprof.WriteHeapProfile(f); err != nil { + errs = append(errs, err) + } + } + + if err := errors.Join(errs...); err != nil { + return fmt.Errorf("error(s) while stopping profiling: %w", err) + } + + return nil +} diff --git a/cmd/helm/pull_test.go b/cmd/helm/pull_test.go index 160e95c76..1110a6bdf 100644 --- a/cmd/helm/pull_test.go +++ b/cmd/helm/pull_test.go @@ -28,10 +28,10 @@ import ( ) func TestPullCmd(t *testing.T) { - srv, err := repotest.NewTempServerWithCleanup(t, "testdata/testcharts/*.tgz*") - if err != nil { - t.Fatal(err) - } + srv := repotest.NewTempServer( + t, + repotest.WithChartSourceGlob("testdata/testcharts/*.tgz*"), + ) defer srv.Stop() ociSrv, err := repotest.NewOCIServer(t, srv.Root()) @@ -257,19 +257,13 @@ func TestPullCmd(t *testing.T) { } func TestPullWithCredentialsCmd(t *testing.T) { - srv, err := repotest.NewTempServerWithCleanup(t, "testdata/testcharts/*.tgz*") - if err != nil { - t.Fatal(err) - } + srv := repotest.NewTempServer( + t, + repotest.WithChartSourceGlob("testdata/testcharts/*.tgz*"), + repotest.WithMiddleware(repotest.BasicAuthMiddleware(t)), + ) defer srv.Stop() - srv.WithMiddleware(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) { - username, password, ok := r.BasicAuth() - if !ok || username != "username" || password != "password" { - t.Errorf("Expected request to use basic auth and for username == 'username' and password == 'password', got '%v', '%s', '%s'", ok, username, password) - } - })) - srv2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.FileServer(http.Dir(srv.Root())).ServeHTTP(w, r) })) diff --git a/cmd/helm/repo_add_test.go b/cmd/helm/repo_add_test.go index 15ad835e6..35911d5ae 100644 --- a/cmd/helm/repo_add_test.go +++ b/cmd/helm/repo_add_test.go @@ -34,22 +34,21 @@ import ( ) func TestRepoAddCmd(t *testing.T) { - srv, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*") - if err != nil { - t.Fatal(err) - } + srv := repotest.NewTempServer( + t, + repotest.WithChartSourceGlob("testdata/testserver/*.*"), + ) defer srv.Stop() // A second test server is setup to verify URL changing - srv2, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*") - if err != nil { - t.Fatal(err) - } + srv2 := repotest.NewTempServer( + t, + repotest.WithChartSourceGlob("testdata/testserver/*.*"), + ) defer srv2.Stop() tmpdir := filepath.Join(t.TempDir(), "path-component.yaml/data") - err = os.MkdirAll(tmpdir, 0777) - if err != nil { + if err := os.MkdirAll(tmpdir, 0777); err != nil { t.Fatal(err) } repoFile := filepath.Join(tmpdir, "repositories.yaml") @@ -81,10 +80,10 @@ func TestRepoAddCmd(t *testing.T) { } func TestRepoAdd(t *testing.T) { - ts, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*") - if err != nil { - t.Fatal(err) - } + ts := repotest.NewTempServer( + t, + repotest.WithChartSourceGlob("testdata/testserver/*.*"), + ) defer ts.Stop() rootDir := t.TempDir() @@ -134,10 +133,10 @@ func TestRepoAdd(t *testing.T) { } func TestRepoAddCheckLegalName(t *testing.T) { - ts, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*") - if err != nil { - t.Fatal(err) - } + ts := repotest.NewTempServer( + t, + repotest.WithChartSourceGlob("testdata/testserver/*.*"), + ) defer ts.Stop() defer resetEnv()() @@ -190,10 +189,10 @@ func TestRepoAddConcurrentHiddenFile(t *testing.T) { } func repoAddConcurrent(t *testing.T, testName, repoFile string) { - ts, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*") - if err != nil { - t.Fatal(err) - } + ts := repotest.NewTempServer( + t, + repotest.WithChartSourceGlob("testdata/testserver/*.*"), + ) defer ts.Stop() var wg sync.WaitGroup @@ -240,7 +239,11 @@ func TestRepoAddFileCompletion(t *testing.T) { } func TestRepoAddWithPasswordFromStdin(t *testing.T) { - srv := repotest.NewTempServerWithCleanupAndBasicAuth(t, "testdata/testserver/*.*") + srv := repotest.NewTempServer( + t, + repotest.WithChartSourceGlob("testdata/testserver/*.*"), + repotest.WithMiddleware(repotest.BasicAuthMiddleware(t)), + ) defer srv.Stop() defer resetEnv()() diff --git a/cmd/helm/repo_remove_test.go b/cmd/helm/repo_remove_test.go index dcfbd99f8..7e6609671 100644 --- a/cmd/helm/repo_remove_test.go +++ b/cmd/helm/repo_remove_test.go @@ -30,10 +30,10 @@ import ( ) func TestRepoRemove(t *testing.T) { - ts, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*") - if err != nil { - t.Fatal(err) - } + ts := repotest.NewTempServer( + t, + repotest.WithChartSourceGlob("testdata/testserver/*.*"), + ) defer ts.Stop() rootDir := t.TempDir() @@ -162,10 +162,11 @@ func testCacheFiles(t *testing.T, cacheIndexFile string, cacheChartsFile string, } func TestRepoRemoveCompletion(t *testing.T) { - ts, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*") - if err != nil { - t.Fatal(err) - } + ts := repotest.NewTempServer( + t, + repotest.WithChartSourceGlob("testdata/testserver/*.*"), + ) + defer ts.Stop() rootDir := t.TempDir() diff --git a/cmd/helm/repo_update_test.go b/cmd/helm/repo_update_test.go index 13369c7cc..7e379da91 100644 --- a/cmd/helm/repo_update_test.go +++ b/cmd/helm/repo_update_test.go @@ -106,10 +106,11 @@ func TestUpdateCustomCacheCmd(t *testing.T) { cachePath := filepath.Join(rootDir, "updcustomcache") os.Mkdir(cachePath, os.ModePerm) - ts, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*") - if err != nil { - t.Fatal(err) - } + ts := repotest.NewTempServer( + t, + repotest.WithChartSourceGlob("testdata/testserver/*.*"), + ) + defer ts.Stop() o := &repoUpdateOptions{ @@ -130,10 +131,9 @@ func TestUpdateCharts(t *testing.T) { defer resetEnv()() ensure.HelmHome(t) - ts, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*") - if err != nil { - t.Fatal(err) - } + ts := repotest.NewTempServer(t, + repotest.WithChartSourceGlob("testdata/testserver/*.*"), + ) defer ts.Stop() r, err := repo.NewChartRepository(&repo.Entry{ @@ -165,10 +165,10 @@ func TestUpdateChartsFail(t *testing.T) { defer resetEnv()() ensure.HelmHome(t) - ts, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*") - if err != nil { - t.Fatal(err) - } + ts := repotest.NewTempServer( + t, + repotest.WithChartSourceGlob("testdata/testserver/*.*"), + ) defer ts.Stop() var invalidURL = ts.URL() + "55" @@ -198,10 +198,10 @@ func TestUpdateChartsFailWithError(t *testing.T) { defer resetEnv()() ensure.HelmHome(t) - ts, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*") - if err != nil { - t.Fatal(err) - } + ts := repotest.NewTempServer( + t, + repotest.WithChartSourceGlob("testdata/testserver/*.*"), + ) defer ts.Stop() var invalidURL = ts.URL() + "55" diff --git a/cmd/helm/require/args_test.go b/cmd/helm/require/args_test.go index 5a84a42d0..cd5850650 100644 --- a/cmd/helm/require/args_test.go +++ b/cmd/helm/require/args_test.go @@ -71,7 +71,8 @@ func runTestCases(t *testing.T, testCases []testCase) { Args: tc.validateFunc, } cmd.SetArgs(tc.args) - cmd.SetOutput(io.Discard) + cmd.SetOut(io.Discard) + cmd.SetErr(io.Discard) err := cmd.Execute() if tc.wantError == "" { diff --git a/cmd/helm/root.go b/cmd/helm/root.go index d21d5e04b..dd3ddeab7 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -95,6 +95,16 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string Short: "The Helm package manager for Kubernetes.", Long: globalUsage, SilenceUsage: true, + PersistentPreRun: func(_ *cobra.Command, _ []string) { + if err := startProfiling(); err != nil { + log.Printf("Warning: Failed to start profiling: %v", err) + } + }, + PersistentPostRun: func(_ *cobra.Command, _ []string) { + if err := stopProfiling(); err != nil { + log.Printf("Warning: Failed to stop profiling: %v", err) + } + }, } flags := cmd.PersistentFlags() diff --git a/cmd/helm/show_test.go b/cmd/helm/show_test.go index 098335f09..0598095b5 100644 --- a/cmd/helm/show_test.go +++ b/cmd/helm/show_test.go @@ -26,10 +26,10 @@ import ( ) func TestShowPreReleaseChart(t *testing.T) { - srv, err := repotest.NewTempServerWithCleanup(t, "testdata/testcharts/*.tgz*") - if err != nil { - t.Fatal(err) - } + srv := repotest.NewTempServer( + t, + repotest.WithChartSourceGlob("testdata/testcharts/*.tgz*"), + ) defer srv.Stop() if err := srv.LinkIndices(); err != nil { diff --git a/cmd/helm/status.go b/cmd/helm/status.go index c56ff1f29..fd3e4ce14 100644 --- a/cmd/helm/status.go +++ b/cmd/helm/status.go @@ -30,7 +30,7 @@ import ( "helm.sh/helm/v4/cmd/helm/require" "helm.sh/helm/v4/pkg/action" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/cli/output" "helm.sh/helm/v4/pkg/release" ) diff --git a/cmd/helm/template.go b/cmd/helm/template.go index 8cd91df32..212664dc8 100644 --- a/cmd/helm/template.go +++ b/cmd/helm/template.go @@ -34,9 +34,9 @@ import ( "helm.sh/helm/v4/cmd/helm/require" "helm.sh/helm/v4/pkg/action" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/cli/values" - "helm.sh/helm/v4/pkg/releaseutil" + releaseutil "helm.sh/helm/v4/pkg/release/util" ) const templateDesc = ` diff --git a/cmd/helm/upgrade.go b/cmd/helm/upgrade.go index 092f6bdcc..4801a784a 100644 --- a/cmd/helm/upgrade.go +++ b/cmd/helm/upgrade.go @@ -53,7 +53,7 @@ or use the '--set' flag and pass configuration from the command line, to force s values, use '--set-string'. You can use '--set-file' to set individual values from a file when the value itself is too long for the command line or is dynamically generated. You can also use '--set-json' to set json values -(scalars/objects/arrays) from the command line. +(scalars/objects/arrays) from the command line. Additionally, you can use '--set-json' and passing json object as a string. You can specify the '--values'/'-f' flag multiple times. The priority will be given to the last (right-most) file specified. For example, if both myvalues.yaml and override.yaml diff --git a/cmd/helm/upgrade_test.go b/cmd/helm/upgrade_test.go index e3d41d7fa..f97a4a26b 100644 --- a/cmd/helm/upgrade_test.go +++ b/cmd/helm/upgrade_test.go @@ -26,7 +26,7 @@ import ( "helm.sh/helm/v4/pkg/chart" "helm.sh/helm/v4/pkg/chart/loader" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/release" ) diff --git a/go.mod b/go.mod index 0b33440d3..d04102ee0 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 github.com/containerd/containerd v1.7.25 github.com/cyphar/filepath-securejoin v0.4.1 - github.com/distribution/distribution/v3 v3.0.0-rc.2 + github.com/distribution/distribution/v3 v3.0.0-rc.3 github.com/evanphx/json-patch v5.9.11+incompatible github.com/foxcpp/go-mockdns v1.1.0 github.com/gobwas/glob v0.2.3 @@ -29,21 +29,22 @@ require ( github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 github.com/pkg/errors v0.9.1 github.com/rubenv/sql-migrate v1.7.1 - github.com/spf13/cobra v1.8.1 + github.com/spf13/cobra v1.9.1 github.com/spf13/pflag v1.0.6 github.com/stretchr/testify v1.10.0 github.com/xeipuuv/gojsonschema v1.2.0 - golang.org/x/crypto v0.32.0 + golang.org/x/crypto v0.35.0 golang.org/x/term v0.29.0 golang.org/x/text v0.22.0 - k8s.io/api v0.32.1 - k8s.io/apiextensions-apiserver v0.32.1 - k8s.io/apimachinery v0.32.1 - k8s.io/apiserver v0.32.1 - k8s.io/cli-runtime v0.32.1 - k8s.io/client-go v0.32.1 + gopkg.in/yaml.v3 v3.0.1 + k8s.io/api v0.32.2 + k8s.io/apiextensions-apiserver v0.32.2 + k8s.io/apimachinery v0.32.2 + k8s.io/apiserver v0.32.2 + k8s.io/cli-runtime v0.32.2 + k8s.io/client-go v0.32.2 k8s.io/klog/v2 v2.130.1 - k8s.io/kubectl v0.32.1 + k8s.io/kubectl v0.32.2 oras.land/oras-go/v2 v2.5.0 sigs.k8s.io/cli-utils v0.37.2 sigs.k8s.io/controller-runtime v0.20.1 @@ -65,7 +66,7 @@ require ( github.com/containerd/log v0.1.0 // indirect github.com/containerd/platforms v0.2.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/distribution/reference v0.6.0 // indirect @@ -177,8 +178,7 @@ require ( gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/component-base v0.32.1 // indirect + k8s.io/component-base v0.32.2 // indirect k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect diff --git a/go.sum b/go.sum index 2591a2051..17999c57b 100644 --- a/go.sum +++ b/go.sum @@ -58,8 +58,8 @@ github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpS github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= @@ -71,8 +71,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/distribution/distribution/v3 v3.0.0-rc.2 h1:tTrzntanYMbd20SyvdeR83Ya1l/aBwDcA3NCIpmwemc= -github.com/distribution/distribution/v3 v3.0.0-rc.2/go.mod h1:H2zIRRXS20ylnv2HTuKILAWuANjuA60GB7MLOsQag7Y= +github.com/distribution/distribution/v3 v3.0.0-rc.3 h1:JRJso9IVLoooKX76oWR+DWCCdZlK5m4nRtDWvzB1ITg= +github.com/distribution/distribution/v3 v3.0.0-rc.3/go.mod h1:offoOgrnYs+CFwis8nE0hyzYZqRCZj5EFc5kgfszwiE= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo= @@ -317,9 +317,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -409,8 +408,8 @@ 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.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= +golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -527,26 +526,26 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.32.1 h1:f562zw9cy+GvXzXf0CKlVQ7yHJVYzLfL6JAS4kOAaOc= -k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k= -k8s.io/apiextensions-apiserver v0.32.1 h1:hjkALhRUeCariC8DiVmb5jj0VjIc1N0DREP32+6UXZw= -k8s.io/apiextensions-apiserver v0.32.1/go.mod h1:sxWIGuGiYov7Io1fAS2X06NjMIk5CbRHc2StSmbaQto= -k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs= -k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= -k8s.io/apiserver v0.32.1 h1:oo0OozRos66WFq87Zc5tclUX2r0mymoVHRq8JmR7Aak= -k8s.io/apiserver v0.32.1/go.mod h1:UcB9tWjBY7aryeI5zAgzVJB/6k7E97bkr1RgqDz0jPw= -k8s.io/cli-runtime v0.32.1 h1:19nwZPlYGJPUDbhAxDIS2/oydCikvKMHsxroKNGA2mM= -k8s.io/cli-runtime v0.32.1/go.mod h1:NJPbeadVFnV2E7B7vF+FvU09mpwYlZCu8PqjzfuOnkY= -k8s.io/client-go v0.32.1 h1:otM0AxdhdBIaQh7l1Q0jQpmo7WOFIk5FFa4bg6YMdUU= -k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg= -k8s.io/component-base v0.32.1 h1:/5IfJ0dHIKBWysGV0yKTFfacZ5yNV1sulPh3ilJjRZk= -k8s.io/component-base v0.32.1/go.mod h1:j1iMMHi/sqAHeG5z+O9BFNCF698a1u0186zkjMZQ28w= +k8s.io/api v0.32.2 h1:bZrMLEkgizC24G9eViHGOPbW+aRo9duEISRIJKfdJuw= +k8s.io/api v0.32.2/go.mod h1:hKlhk4x1sJyYnHENsrdCWw31FEmCijNGPJO5WzHiJ6Y= +k8s.io/apiextensions-apiserver v0.32.2 h1:2YMk285jWMk2188V2AERy5yDwBYrjgWYggscghPCvV4= +k8s.io/apiextensions-apiserver v0.32.2/go.mod h1:GPwf8sph7YlJT3H6aKUWtd0E+oyShk/YHWQHf/OOgCA= +k8s.io/apimachinery v0.32.2 h1:yoQBR9ZGkA6Rgmhbp/yuT9/g+4lxtsGYwW6dR6BDPLQ= +k8s.io/apimachinery v0.32.2/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/apiserver v0.32.2 h1:WzyxAu4mvLkQxwD9hGa4ZfExo3yZZaYzoYvvVDlM6vw= +k8s.io/apiserver v0.32.2/go.mod h1:PEwREHiHNU2oFdte7BjzA1ZyjWjuckORLIK/wLV5goM= +k8s.io/cli-runtime v0.32.2 h1:aKQR4foh9qeyckKRkNXUccP9moxzffyndZAvr+IXMks= +k8s.io/cli-runtime v0.32.2/go.mod h1:a/JpeMztz3xDa7GCyyShcwe55p8pbcCVQxvqZnIwXN8= +k8s.io/client-go v0.32.2 h1:4dYCD4Nz+9RApM2b/3BtVvBHw54QjMFUl1OLcJG5yOA= +k8s.io/client-go v0.32.2/go.mod h1:fpZ4oJXclZ3r2nDOv+Ux3XcJutfrwjKTCHz2H3sww94= +k8s.io/component-base v0.32.2 h1:1aUL5Vdmu7qNo4ZsE+569PV5zFatM9hl+lb3dEea2zU= +k8s.io/component-base v0.32.2/go.mod h1:PXJ61Vx9Lg+P5mS8TLd7bCIr+eMJRQTyXe8KvkrvJq0= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4= -k8s.io/kubectl v0.32.1 h1:/btLtXLQUU1rWx8AEvX9jrb9LaI6yeezt3sFALhB8M8= -k8s.io/kubectl v0.32.1/go.mod h1:sezNuyWi1STk4ZNPVRIFfgjqMI6XMf+oCVLjZen/pFQ= +k8s.io/kubectl v0.32.2 h1:TAkag6+XfSBgkqK9I7ZvwtF0WVtUAvK8ZqTt+5zi1Us= +k8s.io/kubectl v0.32.2/go.mod h1:+h/NQFSPxiDZYX/WZaWw9fwYezGLISP0ud8nQKg+3g8= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= oras.land/oras-go/v2 v2.5.0 h1:o8Me9kLY74Vp5uw07QXPiitjsw7qNXi8Twd+19Zf02c= diff --git a/pkg/action/action.go b/pkg/action/action.go index d067c67ea..24a82ed82 100644 --- a/pkg/action/action.go +++ b/pkg/action/action.go @@ -19,6 +19,7 @@ package action import ( "bytes" "fmt" + "io" "os" "path" "path/filepath" @@ -33,13 +34,13 @@ import ( "k8s.io/client-go/rest" "helm.sh/helm/v4/pkg/chart" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/engine" "helm.sh/helm/v4/pkg/kube" "helm.sh/helm/v4/pkg/postrender" "helm.sh/helm/v4/pkg/registry" "helm.sh/helm/v4/pkg/release" - "helm.sh/helm/v4/pkg/releaseutil" + releaseutil "helm.sh/helm/v4/pkg/release/util" "helm.sh/helm/v4/pkg/storage" "helm.sh/helm/v4/pkg/storage/driver" "helm.sh/helm/v4/pkg/time" @@ -95,6 +96,9 @@ type Configuration struct { Capabilities *chartutil.Capabilities Log func(string, ...interface{}) + + // HookOutputFunc called with container name and returns and expects writer that will receive the log output. + HookOutputFunc func(namespace, pod, container string) io.Writer } // renderResources renders the templates in a chart @@ -122,7 +126,7 @@ func (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Valu var err2 error // A `helm template` should not talk to the remote cluster. However, commands with the flag - //`--dry-run` with the value of `false`, `none`, or `server` should try to interact with the cluster. + // `--dry-run` with the value of `false`, `none`, or `server` should try to interact with the cluster. // It may break in interesting and exotic ways because other data (e.g. discovery) is mocked. if interactWithRemote && cfg.RESTClientGetter != nil { restConfig, err := cfg.RESTClientGetter.ToRESTConfig() @@ -425,6 +429,12 @@ func (cfg *Configuration) Init(getter genericclioptions.RESTClientGetter, namesp cfg.KubeClient = kc cfg.Releases = store cfg.Log = log + cfg.HookOutputFunc = func(_, _, _ string) io.Writer { return io.Discard } return nil } + +// SetHookOutputFunc sets the HookOutputFunc on the Configuration. +func (cfg *Configuration) SetHookOutputFunc(hookOutputFunc func(_, _, _ string) io.Writer) { + cfg.HookOutputFunc = hookOutputFunc +} diff --git a/pkg/action/action_test.go b/pkg/action/action_test.go index 71ea83789..aa5d589f8 100644 --- a/pkg/action/action_test.go +++ b/pkg/action/action_test.go @@ -25,7 +25,7 @@ import ( fakeclientset "k8s.io/client-go/kubernetes/fake" "helm.sh/helm/v4/pkg/chart" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" kubefake "helm.sh/helm/v4/pkg/kube/fake" "helm.sh/helm/v4/pkg/registry" "helm.sh/helm/v4/pkg/release" @@ -111,6 +111,14 @@ type chartOptions struct { type chartOption func(*chartOptions) func buildChart(opts ...chartOption) *chart.Chart { + defaultTemplates := []*chart.File{ + {Name: "templates/hello", Data: []byte("hello: world")}, + {Name: "templates/hooks", Data: []byte(manifestWithHook)}, + } + return buildChartWithTemplates(defaultTemplates, opts...) +} + +func buildChartWithTemplates(templates []*chart.File, opts ...chartOption) *chart.Chart { c := &chartOptions{ Chart: &chart.Chart{ // TODO: This should be more complete. @@ -119,18 +127,13 @@ func buildChart(opts ...chartOption) *chart.Chart { Name: "hello", Version: "0.1.0", }, - // This adds a basic template and hooks. - Templates: []*chart.File{ - {Name: "templates/hello", Data: []byte("hello: world")}, - {Name: "templates/hooks", Data: []byte(manifestWithHook)}, - }, + Templates: templates, }, } for _, opt := range opts { opt(c) } - return c.Chart } diff --git a/pkg/action/dependency_test.go b/pkg/action/dependency_test.go index aee8c240f..38f2668ae 100644 --- a/pkg/action/dependency_test.go +++ b/pkg/action/dependency_test.go @@ -26,7 +26,7 @@ import ( "helm.sh/helm/v4/internal/test" "helm.sh/helm/v4/pkg/chart" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" ) func TestList(t *testing.T) { diff --git a/pkg/action/get_values.go b/pkg/action/get_values.go index aa399f76c..21253b7aa 100644 --- a/pkg/action/get_values.go +++ b/pkg/action/get_values.go @@ -17,7 +17,7 @@ limitations under the License. package action import ( - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" ) // GetValues is the action for checking a given release's values. diff --git a/pkg/action/history.go b/pkg/action/history.go index 5f514a14a..1c5cfa86f 100644 --- a/pkg/action/history.go +++ b/pkg/action/history.go @@ -19,7 +19,7 @@ package action import ( "github.com/pkg/errors" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/release" ) diff --git a/pkg/action/hooks.go b/pkg/action/hooks.go index c32b9b3ce..a48380e7d 100644 --- a/pkg/action/hooks.go +++ b/pkg/action/hooks.go @@ -17,10 +17,18 @@ package action import ( "bytes" + "fmt" + "log" + "slices" "sort" "time" + "helm.sh/helm/v4/pkg/kube" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/pkg/errors" + "gopkg.in/yaml.v3" "helm.sh/helm/v4/pkg/release" helmtime "helm.sh/helm/v4/pkg/time" @@ -86,10 +94,16 @@ func (cfg *Configuration) execHook(rl *release.Release, hook release.HookEvent, // Mark hook as succeeded or failed if err != nil { h.LastRun.Phase = release.HookPhaseFailed + // If a hook is failed, check the annotation of the hook to determine if we should copy the logs client side + if errOutputting := cfg.outputLogsByPolicy(h, rl.Namespace, release.HookOutputOnFailed); errOutputting != nil { + // We log the error here as we want to propagate the hook failure upwards to the release object. + log.Printf("error outputting logs for hook failure: %v", errOutputting) + } // If a hook is failed, check the annotation of the hook to determine whether the hook should be deleted // under failed condition. If so, then clear the corresponding resource object in the hook - if err := cfg.deleteHookByPolicy(h, release.HookFailed, timeout); err != nil { - return err + if errDeleting := cfg.deleteHookByPolicy(h, release.HookFailed, timeout); errDeleting != nil { + // We log the error here as we want to propagate the hook failure upwards to the release object. + log.Printf("error deleting the hook resource on hook failure: %v", errDeleting) } return err } @@ -97,9 +111,13 @@ func (cfg *Configuration) execHook(rl *release.Release, hook release.HookEvent, } // If all hooks are successful, check the annotation of each hook to determine whether the hook should be deleted - // under succeeded condition. If so, then clear the corresponding resource object in each hook + // or output should be logged under succeeded condition. If so, then clear the corresponding resource object in each hook for i := len(executingHooks) - 1; i >= 0; i-- { h := executingHooks[i] + if err := cfg.outputLogsByPolicy(h, rl.Namespace, release.HookOutputOnSucceeded); err != nil { + // We log here as we still want to attempt hook resource deletion even if output logging fails. + log.Printf("error outputting logs for hook failure: %v", err) + } if err := cfg.deleteHookByPolicy(h, release.HookSucceeded, timeout); err != nil { return err } @@ -154,3 +172,57 @@ func hookHasDeletePolicy(h *release.Hook, policy release.HookDeletePolicy) bool } return false } + +// outputLogsByPolicy outputs a pods logs if the hook policy instructs it to +func (cfg *Configuration) outputLogsByPolicy(h *release.Hook, releaseNamespace string, policy release.HookOutputLogPolicy) error { + if !hookHasOutputLogPolicy(h, policy) { + return nil + } + namespace, err := cfg.deriveNamespace(h, releaseNamespace) + if err != nil { + return err + } + switch h.Kind { + case "Job": + return cfg.outputContainerLogsForListOptions(namespace, metav1.ListOptions{LabelSelector: fmt.Sprintf("job-name=%s", h.Name)}) + case "Pod": + return cfg.outputContainerLogsForListOptions(namespace, metav1.ListOptions{FieldSelector: fmt.Sprintf("metadata.name=%s", h.Name)}) + default: + return nil + } +} + +func (cfg *Configuration) outputContainerLogsForListOptions(namespace string, listOptions metav1.ListOptions) error { + // TODO Helm 4: Remove this check when GetPodList and OutputContainerLogsForPodList are moved from InterfaceLogs to Interface + if kubeClient, ok := cfg.KubeClient.(kube.InterfaceLogs); ok { + podList, err := kubeClient.GetPodList(namespace, listOptions) + if err != nil { + return err + } + err = kubeClient.OutputContainerLogsForPodList(podList, namespace, cfg.HookOutputFunc) + return err + } + return nil +} + +func (cfg *Configuration) deriveNamespace(h *release.Hook, namespace string) (string, error) { + tmp := struct { + Metadata struct { + Namespace string + } + }{} + err := yaml.Unmarshal([]byte(h.Manifest), &tmp) + if err != nil { + return "", errors.Wrapf(err, "unable to parse metadata.namespace from kubernetes manifest for output logs hook %s", h.Path) + } + if tmp.Metadata.Namespace == "" { + return namespace, nil + } + return tmp.Metadata.Namespace, nil +} + +// hookHasOutputLogPolicy determines whether the defined hook output log policy matches the hook output log policies +// supported by helm. +func hookHasOutputLogPolicy(h *release.Hook, policy release.HookOutputLogPolicy) bool { + return slices.Contains(h.OutputLogPolicies, policy) +} diff --git a/pkg/action/hooks_test.go b/pkg/action/hooks_test.go new file mode 100644 index 000000000..0f4a9be34 --- /dev/null +++ b/pkg/action/hooks_test.go @@ -0,0 +1,208 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package action + +import ( + "bytes" + "fmt" + "io" + "testing" + + "github.com/stretchr/testify/assert" + + "helm.sh/helm/v4/pkg/chart" + kubefake "helm.sh/helm/v4/pkg/kube/fake" + "helm.sh/helm/v4/pkg/release" +) + +func podManifestWithOutputLogs(hookDefinitions []release.HookOutputLogPolicy) string { + hookDefinitionString := convertHooksToCommaSeparated(hookDefinitions) + return fmt.Sprintf(`kind: Pod +metadata: + name: finding-sharky, + annotations: + "helm.sh/hook": pre-install + "helm.sh/hook-output-log-policy": %s +spec: + containers: + - name: sharky-test + image: fake-image + cmd: fake-command`, hookDefinitionString) +} + +func podManifestWithOutputLogWithNamespace(hookDefinitions []release.HookOutputLogPolicy) string { + hookDefinitionString := convertHooksToCommaSeparated(hookDefinitions) + return fmt.Sprintf(`kind: Pod +metadata: + name: finding-george + namespace: sneaky-namespace + annotations: + "helm.sh/hook": pre-install + "helm.sh/hook-output-log-policy": %s +spec: + containers: + - name: george-test + image: fake-image + cmd: fake-command`, hookDefinitionString) +} + +func jobManifestWithOutputLog(hookDefinitions []release.HookOutputLogPolicy) string { + hookDefinitionString := convertHooksToCommaSeparated(hookDefinitions) + return fmt.Sprintf(`kind: Job +apiVersion: batch/v1 +metadata: + name: losing-religion + annotations: + "helm.sh/hook": pre-install + "helm.sh/hook-output-log-policy": %s +spec: + completions: 1 + parallelism: 1 + activeDeadlineSeconds: 30 + template: + spec: + containers: + - name: religion-container + image: religion-image + cmd: religion-command`, hookDefinitionString) +} + +func jobManifestWithOutputLogWithNamespace(hookDefinitions []release.HookOutputLogPolicy) string { + hookDefinitionString := convertHooksToCommaSeparated(hookDefinitions) + return fmt.Sprintf(`kind: Job +apiVersion: batch/v1 +metadata: + name: losing-religion + namespace: rem-namespace + annotations: + "helm.sh/hook": pre-install + "helm.sh/hook-output-log-policy": %s +spec: + completions: 1 + parallelism: 1 + activeDeadlineSeconds: 30 + template: + spec: + containers: + - name: religion-container + image: religion-image + cmd: religion-command`, hookDefinitionString) +} + +func convertHooksToCommaSeparated(hookDefinitions []release.HookOutputLogPolicy) string { + var commaSeparated string + for i, policy := range hookDefinitions { + if i+1 == len(hookDefinitions) { + commaSeparated += policy.String() + } else { + commaSeparated += policy.String() + "," + } + } + return commaSeparated +} + +func TestInstallRelease_HookOutputLogsOnFailure(t *testing.T) { + // Should output on failure with expected namespace if hook-failed is set + runInstallForHooksWithFailure(t, podManifestWithOutputLogs([]release.HookOutputLogPolicy{release.HookOutputOnFailed}), "spaced", true) + runInstallForHooksWithFailure(t, podManifestWithOutputLogWithNamespace([]release.HookOutputLogPolicy{release.HookOutputOnFailed}), "sneaky-namespace", true) + runInstallForHooksWithFailure(t, jobManifestWithOutputLog([]release.HookOutputLogPolicy{release.HookOutputOnFailed}), "spaced", true) + runInstallForHooksWithFailure(t, jobManifestWithOutputLogWithNamespace([]release.HookOutputLogPolicy{release.HookOutputOnFailed}), "rem-namespace", true) + + // Should not output on failure with expected namespace if hook-succeed is set + runInstallForHooksWithFailure(t, podManifestWithOutputLogs([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded}), "", false) + runInstallForHooksWithFailure(t, podManifestWithOutputLogWithNamespace([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded}), "", false) + runInstallForHooksWithFailure(t, jobManifestWithOutputLog([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded}), "", false) + runInstallForHooksWithFailure(t, jobManifestWithOutputLogWithNamespace([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded}), "", false) +} + +func TestInstallRelease_HookOutputLogsOnSuccess(t *testing.T) { + // Should output on success with expected namespace if hook-succeeded is set + runInstallForHooksWithSuccess(t, podManifestWithOutputLogs([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded}), "spaced", true) + runInstallForHooksWithSuccess(t, podManifestWithOutputLogWithNamespace([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded}), "sneaky-namespace", true) + runInstallForHooksWithSuccess(t, jobManifestWithOutputLog([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded}), "spaced", true) + runInstallForHooksWithSuccess(t, jobManifestWithOutputLogWithNamespace([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded}), "rem-namespace", true) + + // Should not output on success if hook-failed is set + runInstallForHooksWithSuccess(t, podManifestWithOutputLogs([]release.HookOutputLogPolicy{release.HookOutputOnFailed}), "", false) + runInstallForHooksWithSuccess(t, podManifestWithOutputLogWithNamespace([]release.HookOutputLogPolicy{release.HookOutputOnFailed}), "", false) + runInstallForHooksWithSuccess(t, jobManifestWithOutputLog([]release.HookOutputLogPolicy{release.HookOutputOnFailed}), "", false) + runInstallForHooksWithSuccess(t, jobManifestWithOutputLogWithNamespace([]release.HookOutputLogPolicy{release.HookOutputOnFailed}), "", false) +} + +func TestInstallRelease_HooksOutputLogsOnSuccessAndFailure(t *testing.T) { + // Should output on success with expected namespace if hook-succeeded and hook-failed is set + runInstallForHooksWithSuccess(t, podManifestWithOutputLogs([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded, release.HookOutputOnFailed}), "spaced", true) + runInstallForHooksWithSuccess(t, podManifestWithOutputLogWithNamespace([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded, release.HookOutputOnFailed}), "sneaky-namespace", true) + runInstallForHooksWithSuccess(t, jobManifestWithOutputLog([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded, release.HookOutputOnFailed}), "spaced", true) + runInstallForHooksWithSuccess(t, jobManifestWithOutputLogWithNamespace([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded, release.HookOutputOnFailed}), "rem-namespace", true) + + // Should output on failure if hook-succeeded and hook-failed is set + runInstallForHooksWithFailure(t, podManifestWithOutputLogs([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded, release.HookOutputOnFailed}), "spaced", true) + runInstallForHooksWithFailure(t, podManifestWithOutputLogWithNamespace([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded, release.HookOutputOnFailed}), "sneaky-namespace", true) + runInstallForHooksWithFailure(t, jobManifestWithOutputLog([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded, release.HookOutputOnFailed}), "spaced", true) + runInstallForHooksWithFailure(t, jobManifestWithOutputLogWithNamespace([]release.HookOutputLogPolicy{release.HookOutputOnSucceeded, release.HookOutputOnFailed}), "rem-namespace", true) +} + +func runInstallForHooksWithSuccess(t *testing.T, manifest, expectedNamespace string, shouldOutput bool) { + var expectedOutput string + if shouldOutput { + expectedOutput = fmt.Sprintf("attempted to output logs for namespace: %s", expectedNamespace) + } + is := assert.New(t) + instAction := installAction(t) + instAction.ReleaseName = "failed-hooks" + outBuffer := &bytes.Buffer{} + instAction.cfg.KubeClient = &kubefake.PrintingKubeClient{Out: io.Discard, LogOutput: outBuffer} + + templates := []*chart.File{ + {Name: "templates/hello", Data: []byte("hello: world")}, + {Name: "templates/hooks", Data: []byte(manifest)}, + } + vals := map[string]interface{}{} + + res, err := instAction.Run(buildChartWithTemplates(templates), vals) + is.NoError(err) + is.Equal(expectedOutput, outBuffer.String()) + is.Equal(release.StatusDeployed, res.Info.Status) +} + +func runInstallForHooksWithFailure(t *testing.T, manifest, expectedNamespace string, shouldOutput bool) { + var expectedOutput string + if shouldOutput { + expectedOutput = fmt.Sprintf("attempted to output logs for namespace: %s", expectedNamespace) + } + is := assert.New(t) + instAction := installAction(t) + instAction.ReleaseName = "failed-hooks" + failingClient := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient) + failingClient.WatchUntilReadyError = fmt.Errorf("failed watch") + instAction.cfg.KubeClient = failingClient + outBuffer := &bytes.Buffer{} + failingClient.PrintingKubeClient = kubefake.PrintingKubeClient{Out: io.Discard, LogOutput: outBuffer} + + templates := []*chart.File{ + {Name: "templates/hello", Data: []byte("hello: world")}, + {Name: "templates/hooks", Data: []byte(manifest)}, + } + vals := map[string]interface{}{} + + res, err := instAction.Run(buildChartWithTemplates(templates), vals) + is.Error(err) + is.Contains(res.Info.Description, "failed pre-install") + is.Equal(expectedOutput, outBuffer.String()) + is.Equal(release.StatusFailed, res.Info.Status) +} diff --git a/pkg/action/install.go b/pkg/action/install.go index a589aaf04..7a249b239 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -40,7 +40,7 @@ import ( "sigs.k8s.io/yaml" "helm.sh/helm/v4/pkg/chart" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/cli" "helm.sh/helm/v4/pkg/downloader" "helm.sh/helm/v4/pkg/getter" @@ -49,7 +49,7 @@ import ( "helm.sh/helm/v4/pkg/postrender" "helm.sh/helm/v4/pkg/registry" "helm.sh/helm/v4/pkg/release" - "helm.sh/helm/v4/pkg/releaseutil" + releaseutil "helm.sh/helm/v4/pkg/release/util" "helm.sh/helm/v4/pkg/repo" "helm.sh/helm/v4/pkg/storage" "helm.sh/helm/v4/pkg/storage/driver" diff --git a/pkg/action/install_test.go b/pkg/action/install_test.go index 6377cfda5..57fc40263 100644 --- a/pkg/action/install_test.go +++ b/pkg/action/install_test.go @@ -17,6 +17,7 @@ limitations under the License. package action import ( + "bytes" "context" "fmt" "io" @@ -33,7 +34,7 @@ import ( "helm.sh/helm/v4/internal/test" "helm.sh/helm/v4/pkg/chart" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/kube" kubefake "helm.sh/helm/v4/pkg/kube/fake" "helm.sh/helm/v4/pkg/release" @@ -355,11 +356,14 @@ func TestInstallRelease_FailedHooks(t *testing.T) { failer := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient) failer.WatchUntilReadyError = fmt.Errorf("Failed watch") instAction.cfg.KubeClient = failer + outBuffer := &bytes.Buffer{} + failer.PrintingKubeClient = kubefake.PrintingKubeClient{Out: io.Discard, LogOutput: outBuffer} vals := map[string]interface{}{} res, err := instAction.Run(buildChart(), vals) is.Error(err) is.Contains(res.Info.Description, "failed post-install") + is.Equal("", outBuffer.String()) is.Equal(release.StatusFailed, res.Info.Status) } diff --git a/pkg/action/lint.go b/pkg/action/lint.go index 06fc48612..a6fd7c71c 100644 --- a/pkg/action/lint.go +++ b/pkg/action/lint.go @@ -23,7 +23,7 @@ import ( "github.com/pkg/errors" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/lint" "helm.sh/helm/v4/pkg/lint/support" ) diff --git a/pkg/action/list.go b/pkg/action/list.go index f90c31acd..5c2b1e8a1 100644 --- a/pkg/action/list.go +++ b/pkg/action/list.go @@ -23,7 +23,7 @@ import ( "k8s.io/apimachinery/pkg/labels" "helm.sh/helm/v4/pkg/release" - "helm.sh/helm/v4/pkg/releaseutil" + releaseutil "helm.sh/helm/v4/pkg/release/util" ) // ListStates represents zero or more status codes that a list item may have set diff --git a/pkg/action/package.go b/pkg/action/package.go index 2e792a65c..8343ba109 100644 --- a/pkg/action/package.go +++ b/pkg/action/package.go @@ -27,7 +27,7 @@ import ( "golang.org/x/term" "helm.sh/helm/v4/pkg/chart/loader" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/provenance" ) diff --git a/pkg/action/pull.go b/pkg/action/pull.go index 7fc1a9fdb..fa85fe242 100644 --- a/pkg/action/pull.go +++ b/pkg/action/pull.go @@ -24,7 +24,7 @@ import ( "github.com/pkg/errors" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/cli" "helm.sh/helm/v4/pkg/downloader" "helm.sh/helm/v4/pkg/getter" diff --git a/pkg/action/release_testing.go b/pkg/action/release_testing.go index 2539a7f65..a2c68ad64 100644 --- a/pkg/action/release_testing.go +++ b/pkg/action/release_testing.go @@ -20,13 +20,14 @@ import ( "context" "fmt" "io" + "slices" "sort" "time" "github.com/pkg/errors" v1 "k8s.io/api/core/v1" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/release" ) @@ -75,7 +76,7 @@ func (r *ReleaseTesting) Run(name string) (*release.Release, error) { executingHooks := []*release.Hook{} if len(r.Filters[ExcludeNameFilter]) != 0 { for _, h := range rel.Hooks { - if contains(r.Filters[ExcludeNameFilter], h.Name) { + if slices.Contains(r.Filters[ExcludeNameFilter], h.Name) { skippedHooks = append(skippedHooks, h) } else { executingHooks = append(executingHooks, h) @@ -86,7 +87,7 @@ func (r *ReleaseTesting) Run(name string) (*release.Release, error) { if len(r.Filters[IncludeNameFilter]) != 0 { executingHooks = nil for _, h := range rel.Hooks { - if contains(r.Filters[IncludeNameFilter], h.Name) { + if slices.Contains(r.Filters[IncludeNameFilter], h.Name) { executingHooks = append(executingHooks, h) } else { skippedHooks = append(skippedHooks, h) @@ -119,10 +120,10 @@ func (r *ReleaseTesting) GetPodLogs(out io.Writer, rel *release.Release) error { for _, h := range hooksByWight { for _, e := range h.Events { if e == release.HookTest { - if contains(r.Filters[ExcludeNameFilter], h.Name) { + if slices.Contains(r.Filters[ExcludeNameFilter], h.Name) { continue } - if len(r.Filters[IncludeNameFilter]) > 0 && !contains(r.Filters[IncludeNameFilter], h.Name) { + if len(r.Filters[IncludeNameFilter]) > 0 && !slices.Contains(r.Filters[IncludeNameFilter], h.Name) { continue } req := client.CoreV1().Pods(r.Namespace).GetLogs(h.Name, &v1.PodLogOptions{}) @@ -142,12 +143,3 @@ func (r *ReleaseTesting) GetPodLogs(out io.Writer, rel *release.Release) error { } return nil } - -func contains(arr []string, value string) bool { - for _, item := range arr { - if item == value { - return true - } - } - return false -} diff --git a/pkg/action/resource_policy.go b/pkg/action/resource_policy.go index f18acb880..b72e94124 100644 --- a/pkg/action/resource_policy.go +++ b/pkg/action/resource_policy.go @@ -20,7 +20,7 @@ import ( "strings" "helm.sh/helm/v4/pkg/kube" - "helm.sh/helm/v4/pkg/releaseutil" + releaseutil "helm.sh/helm/v4/pkg/release/util" ) func filterManifestsToKeep(manifests []releaseutil.Manifest) (keep, remaining []releaseutil.Manifest) { diff --git a/pkg/action/rollback.go b/pkg/action/rollback.go index 804bdbd58..b4117dde9 100644 --- a/pkg/action/rollback.go +++ b/pkg/action/rollback.go @@ -24,7 +24,7 @@ import ( "github.com/pkg/errors" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/kube" "helm.sh/helm/v4/pkg/release" helmtime "helm.sh/helm/v4/pkg/time" diff --git a/pkg/action/show.go b/pkg/action/show.go index 1aa9bf1d0..3b2722eef 100644 --- a/pkg/action/show.go +++ b/pkg/action/show.go @@ -27,7 +27,7 @@ import ( "helm.sh/helm/v4/pkg/chart" "helm.sh/helm/v4/pkg/chart/loader" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/registry" ) diff --git a/pkg/action/uninstall.go b/pkg/action/uninstall.go index f21551bbf..dcbfa6898 100644 --- a/pkg/action/uninstall.go +++ b/pkg/action/uninstall.go @@ -25,10 +25,10 @@ import ( v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/kube" "helm.sh/helm/v4/pkg/release" - "helm.sh/helm/v4/pkg/releaseutil" + releaseutil "helm.sh/helm/v4/pkg/release/util" helmtime "helm.sh/helm/v4/pkg/time" ) diff --git a/pkg/action/upgrade.go b/pkg/action/upgrade.go index 626c1e6ad..5cf284afa 100644 --- a/pkg/action/upgrade.go +++ b/pkg/action/upgrade.go @@ -29,12 +29,12 @@ import ( "k8s.io/cli-runtime/pkg/resource" "helm.sh/helm/v4/pkg/chart" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/kube" "helm.sh/helm/v4/pkg/postrender" "helm.sh/helm/v4/pkg/registry" "helm.sh/helm/v4/pkg/release" - "helm.sh/helm/v4/pkg/releaseutil" + releaseutil "helm.sh/helm/v4/pkg/release/util" "helm.sh/helm/v4/pkg/storage/driver" ) diff --git a/pkg/chart/loader/load.go b/pkg/chart/loader/load.go index 7645ba96c..e32094ef5 100644 --- a/pkg/chart/loader/load.go +++ b/pkg/chart/loader/load.go @@ -17,14 +17,17 @@ limitations under the License. package loader import ( + "bufio" "bytes" "encoding/json" + "io" "log" "os" "path/filepath" "strings" "github.com/pkg/errors" + utilyaml "k8s.io/apimachinery/pkg/util/yaml" "sigs.k8s.io/yaml" "helm.sh/helm/v4/pkg/chart" @@ -104,13 +107,11 @@ func LoadFiles(files []*BufferedFile) (*chart.Chart, error) { return c, errors.Wrap(err, "cannot load Chart.lock") } case f.Name == "values.yaml": - c.Values = make(map[string]interface{}) - if err := yaml.Unmarshal(f.Data, &c.Values, func(d *json.Decoder) *json.Decoder { - d.UseNumber() - return d - }); err != nil { + values, err := LoadValues(bytes.NewReader(f.Data)) + if err != nil { return c, errors.Wrap(err, "cannot load values.yaml") } + c.Values = values case f.Name == "values.schema.json": c.Schema = f.Data @@ -205,3 +206,51 @@ func LoadFiles(files []*BufferedFile) (*chart.Chart, error) { return c, nil } + +// LoadValues loads values from a reader. +// +// The reader is expected to contain one or more YAML documents, the values of which are merged. +// And the values can be either a chart's default values or a user-supplied values. +func LoadValues(data io.Reader) (map[string]interface{}, error) { + values := map[string]interface{}{} + reader := utilyaml.NewYAMLReader(bufio.NewReader(data)) + for { + currentMap := map[string]interface{}{} + raw, err := reader.Read() + if err != nil { + if err == io.EOF { + break + } + return nil, errors.Wrap(err, "error reading yaml document") + } + if err := yaml.Unmarshal(raw, ¤tMap, func(d *json.Decoder) *json.Decoder { + d.UseNumber() + return d + }); err != nil { + return nil, errors.Wrap(err, "cannot unmarshal yaml document") + } + values = MergeMaps(values, currentMap) + } + return values, nil +} + +// MergeMaps merges two maps. If a key exists in both maps, the value from b will be used. +// If the value is a map, the maps will be merged recursively. +func MergeMaps(a, b map[string]interface{}) map[string]interface{} { + out := make(map[string]interface{}, len(a)) + for k, v := range a { + out[k] = v + } + for k, v := range b { + if v, ok := v.(map[string]interface{}); ok { + if bv, ok := out[k]; ok { + if bv, ok := bv.(map[string]interface{}); ok { + out[k] = MergeMaps(bv, v) + continue + } + } + } + out[k] = v + } + return out +} diff --git a/pkg/chart/loader/load_test.go b/pkg/chart/loader/load_test.go index 25e000835..e34124829 100644 --- a/pkg/chart/loader/load_test.go +++ b/pkg/chart/loader/load_test.go @@ -24,6 +24,7 @@ import ( "log" "os" "path/filepath" + "reflect" "runtime" "strings" "testing" @@ -488,6 +489,115 @@ func TestLoadInvalidArchive(t *testing.T) { } } +func TestLoadValues(t *testing.T) { + testCases := map[string]struct { + data []byte + expctedValues map[string]interface{} + }{ + "It should load values correctly": { + data: []byte(` +foo: + image: foo:v1 +bar: + version: v2 +`), + expctedValues: map[string]interface{}{ + "foo": map[string]interface{}{ + "image": "foo:v1", + }, + "bar": map[string]interface{}{ + "version": "v2", + }, + }, + }, + "It should load values correctly with multiple documents in one file": { + data: []byte(` +foo: + image: foo:v1 +bar: + version: v2 +--- +foo: + image: foo:v2 +`), + expctedValues: map[string]interface{}{ + "foo": map[string]interface{}{ + "image": "foo:v2", + }, + "bar": map[string]interface{}{ + "version": "v2", + }, + }, + }, + } + for testName, testCase := range testCases { + t.Run(testName, func(tt *testing.T) { + values, err := LoadValues(bytes.NewReader(testCase.data)) + if err != nil { + tt.Fatal(err) + } + if !reflect.DeepEqual(values, testCase.expctedValues) { + tt.Errorf("Expected values: %v, got %v", testCase.expctedValues, values) + } + }) + } +} + +func TestMergeValues(t *testing.T) { + nestedMap := map[string]interface{}{ + "foo": "bar", + "baz": map[string]string{ + "cool": "stuff", + }, + } + anotherNestedMap := map[string]interface{}{ + "foo": "bar", + "baz": map[string]string{ + "cool": "things", + "awesome": "stuff", + }, + } + flatMap := map[string]interface{}{ + "foo": "bar", + "baz": "stuff", + } + anotherFlatMap := map[string]interface{}{ + "testing": "fun", + } + + testMap := MergeMaps(flatMap, nestedMap) + equal := reflect.DeepEqual(testMap, nestedMap) + if !equal { + t.Errorf("Expected a nested map to overwrite a flat value. Expected: %v, got %v", nestedMap, testMap) + } + + testMap = MergeMaps(nestedMap, flatMap) + equal = reflect.DeepEqual(testMap, flatMap) + if !equal { + t.Errorf("Expected a flat value to overwrite a map. Expected: %v, got %v", flatMap, testMap) + } + + testMap = MergeMaps(nestedMap, anotherNestedMap) + equal = reflect.DeepEqual(testMap, anotherNestedMap) + if !equal { + t.Errorf("Expected a nested map to overwrite another nested map. Expected: %v, got %v", anotherNestedMap, testMap) + } + + testMap = MergeMaps(anotherFlatMap, anotherNestedMap) + expectedMap := map[string]interface{}{ + "testing": "fun", + "foo": "bar", + "baz": map[string]string{ + "cool": "things", + "awesome": "stuff", + }, + } + equal = reflect.DeepEqual(testMap, expectedMap) + if !equal { + t.Errorf("Expected a map with different keys to merge properly with another map. Expected: %v, got %v", expectedMap, testMap) + } +} + func verifyChart(t *testing.T, c *chart.Chart) { t.Helper() if c.Name() == "" { diff --git a/pkg/chartutil/capabilities.go b/pkg/chart/util/capabilities.go similarity index 99% rename from pkg/chartutil/capabilities.go rename to pkg/chart/util/capabilities.go index 6c1ebf24c..d4b420b2f 100644 --- a/pkg/chartutil/capabilities.go +++ b/pkg/chart/util/capabilities.go @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package chartutil +package util import ( "fmt" diff --git a/pkg/chartutil/capabilities_test.go b/pkg/chart/util/capabilities_test.go similarity index 99% rename from pkg/chartutil/capabilities_test.go rename to pkg/chart/util/capabilities_test.go index 502b0c7d5..aa9be9db8 100644 --- a/pkg/chartutil/capabilities_test.go +++ b/pkg/chart/util/capabilities_test.go @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package chartutil +package util import ( "testing" diff --git a/pkg/chartutil/chartfile.go b/pkg/chart/util/chartfile.go similarity index 99% rename from pkg/chartutil/chartfile.go rename to pkg/chart/util/chartfile.go index 596011ba3..acab80d0a 100644 --- a/pkg/chartutil/chartfile.go +++ b/pkg/chart/util/chartfile.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package chartutil +package util import ( "os" diff --git a/pkg/chartutil/chartfile_test.go b/pkg/chart/util/chartfile_test.go similarity index 99% rename from pkg/chartutil/chartfile_test.go rename to pkg/chart/util/chartfile_test.go index 595aee517..6eb599680 100644 --- a/pkg/chartutil/chartfile_test.go +++ b/pkg/chart/util/chartfile_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package chartutil +package util import ( "testing" diff --git a/pkg/chartutil/coalesce.go b/pkg/chart/util/coalesce.go similarity index 99% rename from pkg/chartutil/coalesce.go rename to pkg/chart/util/coalesce.go index a46936cc6..9ab5c1015 100644 --- a/pkg/chartutil/coalesce.go +++ b/pkg/chart/util/coalesce.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package chartutil +package util import ( "fmt" diff --git a/pkg/chartutil/coalesce_test.go b/pkg/chart/util/coalesce_test.go similarity index 99% rename from pkg/chartutil/coalesce_test.go rename to pkg/chart/util/coalesce_test.go index e207274d7..5a8dfe94a 100644 --- a/pkg/chartutil/coalesce_test.go +++ b/pkg/chart/util/coalesce_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package chartutil +package util import ( "encoding/json" diff --git a/pkg/chartutil/compatible.go b/pkg/chart/util/compatible.go similarity index 98% rename from pkg/chartutil/compatible.go rename to pkg/chart/util/compatible.go index f4656c913..d384d2d45 100644 --- a/pkg/chartutil/compatible.go +++ b/pkg/chart/util/compatible.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package chartutil +package util import "github.com/Masterminds/semver/v3" diff --git a/pkg/chartutil/compatible_test.go b/pkg/chart/util/compatible_test.go similarity index 98% rename from pkg/chartutil/compatible_test.go rename to pkg/chart/util/compatible_test.go index df7be6161..e17d33e35 100644 --- a/pkg/chartutil/compatible_test.go +++ b/pkg/chart/util/compatible_test.go @@ -15,7 +15,7 @@ limitations under the License. */ // Package version represents the current version of the project. -package chartutil +package util import "testing" diff --git a/pkg/chartutil/create.go b/pkg/chart/util/create.go similarity index 99% rename from pkg/chartutil/create.go rename to pkg/chart/util/create.go index 4f59b9f83..dfb5099f2 100644 --- a/pkg/chartutil/create.go +++ b/pkg/chart/util/create.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package chartutil +package util import ( "fmt" diff --git a/pkg/chartutil/create_test.go b/pkg/chart/util/create_test.go similarity index 99% rename from pkg/chartutil/create_test.go rename to pkg/chart/util/create_test.go index 8f8122f6f..e67ce3e1f 100644 --- a/pkg/chartutil/create_test.go +++ b/pkg/chart/util/create_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package chartutil +package util import ( "bytes" diff --git a/pkg/chartutil/dependencies.go b/pkg/chart/util/dependencies.go similarity index 99% rename from pkg/chartutil/dependencies.go rename to pkg/chart/util/dependencies.go index f0ba166d9..fb9052d71 100644 --- a/pkg/chartutil/dependencies.go +++ b/pkg/chart/util/dependencies.go @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package chartutil +package util import ( "log" diff --git a/pkg/chartutil/dependencies_test.go b/pkg/chart/util/dependencies_test.go similarity index 99% rename from pkg/chartutil/dependencies_test.go rename to pkg/chart/util/dependencies_test.go index ac8e4d76e..10fca265e 100644 --- a/pkg/chartutil/dependencies_test.go +++ b/pkg/chart/util/dependencies_test.go @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -package chartutil +package util import ( "encoding/json" diff --git a/pkg/chartutil/doc.go b/pkg/chart/util/doc.go similarity index 92% rename from pkg/chartutil/doc.go rename to pkg/chart/util/doc.go index 6c0b1582b..587fcaeb1 100644 --- a/pkg/chartutil/doc.go +++ b/pkg/chart/util/doc.go @@ -15,7 +15,7 @@ limitations under the License. */ /* -Package chartutil contains tools for working with charts. +package util contains tools for working with charts. Charts are described in the chart package (pkg/chart). This package provides utilities for serializing and deserializing charts. @@ -42,4 +42,4 @@ into a Chart. When creating charts in memory, use the 'helm.sh/helm/pkg/chart' package directly. */ -package chartutil // import "helm.sh/helm/v4/pkg/chartutil" +package util // import chartutil "helm.sh/helm/v4/pkg/chart/util" diff --git a/pkg/chartutil/errors.go b/pkg/chart/util/errors.go similarity index 98% rename from pkg/chartutil/errors.go rename to pkg/chart/util/errors.go index 0a4046d2e..a175b9758 100644 --- a/pkg/chartutil/errors.go +++ b/pkg/chart/util/errors.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package chartutil +package util import ( "fmt" diff --git a/pkg/chartutil/errors_test.go b/pkg/chart/util/errors_test.go similarity index 97% rename from pkg/chartutil/errors_test.go rename to pkg/chart/util/errors_test.go index 3f63e3733..b8ae86384 100644 --- a/pkg/chartutil/errors_test.go +++ b/pkg/chart/util/errors_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package chartutil +package util import ( "testing" diff --git a/pkg/chartutil/expand.go b/pkg/chart/util/expand.go similarity index 99% rename from pkg/chartutil/expand.go rename to pkg/chart/util/expand.go index a9943252d..4b83bf584 100644 --- a/pkg/chartutil/expand.go +++ b/pkg/chart/util/expand.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package chartutil +package util import ( "io" diff --git a/pkg/chartutil/expand_test.go b/pkg/chart/util/expand_test.go similarity index 99% rename from pkg/chartutil/expand_test.go rename to pkg/chart/util/expand_test.go index b46ace01f..280995f7e 100644 --- a/pkg/chartutil/expand_test.go +++ b/pkg/chart/util/expand_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package chartutil +package util import ( "os" diff --git a/pkg/chartutil/jsonschema.go b/pkg/chart/util/jsonschema.go similarity index 99% rename from pkg/chartutil/jsonschema.go rename to pkg/chart/util/jsonschema.go index f1c8dcdf4..616c6d444 100644 --- a/pkg/chartutil/jsonschema.go +++ b/pkg/chart/util/jsonschema.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package chartutil +package util import ( "bytes" diff --git a/pkg/chartutil/jsonschema_test.go b/pkg/chart/util/jsonschema_test.go similarity index 99% rename from pkg/chartutil/jsonschema_test.go rename to pkg/chart/util/jsonschema_test.go index 098cf70db..c5600044a 100644 --- a/pkg/chartutil/jsonschema_test.go +++ b/pkg/chart/util/jsonschema_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package chartutil +package util import ( "os" diff --git a/pkg/chartutil/save.go b/pkg/chart/util/save.go similarity index 99% rename from pkg/chartutil/save.go rename to pkg/chart/util/save.go index 0dddad912..635ff7944 100644 --- a/pkg/chartutil/save.go +++ b/pkg/chart/util/save.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package chartutil +package util import ( "archive/tar" diff --git a/pkg/chartutil/save_test.go b/pkg/chart/util/save_test.go similarity index 99% rename from pkg/chartutil/save_test.go rename to pkg/chart/util/save_test.go index 3789b6617..a7338c8d7 100644 --- a/pkg/chartutil/save_test.go +++ b/pkg/chart/util/save_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package chartutil +package util import ( "archive/tar" diff --git a/pkg/chartutil/testdata/chartfiletest.yaml b/pkg/chart/util/testdata/chartfiletest.yaml similarity index 100% rename from pkg/chartutil/testdata/chartfiletest.yaml rename to pkg/chart/util/testdata/chartfiletest.yaml diff --git a/pkg/chartutil/testdata/coleridge.yaml b/pkg/chart/util/testdata/coleridge.yaml similarity index 100% rename from pkg/chartutil/testdata/coleridge.yaml rename to pkg/chart/util/testdata/coleridge.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-alias/.helmignore b/pkg/chart/util/testdata/dependent-chart-alias/.helmignore similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-alias/.helmignore rename to pkg/chart/util/testdata/dependent-chart-alias/.helmignore diff --git a/pkg/chartutil/testdata/dependent-chart-alias/Chart.lock b/pkg/chart/util/testdata/dependent-chart-alias/Chart.lock similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-alias/Chart.lock rename to pkg/chart/util/testdata/dependent-chart-alias/Chart.lock diff --git a/pkg/chartutil/testdata/dependent-chart-alias/Chart.yaml b/pkg/chart/util/testdata/dependent-chart-alias/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-alias/Chart.yaml rename to pkg/chart/util/testdata/dependent-chart-alias/Chart.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-alias/INSTALL.txt b/pkg/chart/util/testdata/dependent-chart-alias/INSTALL.txt similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-alias/INSTALL.txt rename to pkg/chart/util/testdata/dependent-chart-alias/INSTALL.txt diff --git a/pkg/chartutil/testdata/dependent-chart-alias/LICENSE b/pkg/chart/util/testdata/dependent-chart-alias/LICENSE similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-alias/LICENSE rename to pkg/chart/util/testdata/dependent-chart-alias/LICENSE diff --git a/pkg/chartutil/testdata/dependent-chart-alias/README.md b/pkg/chart/util/testdata/dependent-chart-alias/README.md similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-alias/README.md rename to pkg/chart/util/testdata/dependent-chart-alias/README.md diff --git a/pkg/chartutil/testdata/dependent-chart-alias/charts/_ignore_me b/pkg/chart/util/testdata/dependent-chart-alias/charts/_ignore_me similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-alias/charts/_ignore_me rename to pkg/chart/util/testdata/dependent-chart-alias/charts/_ignore_me diff --git a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/Chart.yaml b/pkg/chart/util/testdata/dependent-chart-alias/charts/alpine/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/Chart.yaml rename to pkg/chart/util/testdata/dependent-chart-alias/charts/alpine/Chart.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/README.md b/pkg/chart/util/testdata/dependent-chart-alias/charts/alpine/README.md similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/README.md rename to pkg/chart/util/testdata/dependent-chart-alias/charts/alpine/README.md diff --git a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast1/Chart.yaml b/pkg/chart/util/testdata/dependent-chart-alias/charts/alpine/charts/mast1/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast1/Chart.yaml rename to pkg/chart/util/testdata/dependent-chart-alias/charts/alpine/charts/mast1/Chart.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast1/values.yaml b/pkg/chart/util/testdata/dependent-chart-alias/charts/alpine/charts/mast1/values.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast1/values.yaml rename to pkg/chart/util/testdata/dependent-chart-alias/charts/alpine/charts/mast1/values.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chart/util/testdata/dependent-chart-alias/charts/alpine/charts/mast2-0.1.0.tgz similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast2-0.1.0.tgz rename to pkg/chart/util/testdata/dependent-chart-alias/charts/alpine/charts/mast2-0.1.0.tgz diff --git a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/templates/alpine-pod.yaml b/pkg/chart/util/testdata/dependent-chart-alias/charts/alpine/templates/alpine-pod.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/templates/alpine-pod.yaml rename to pkg/chart/util/testdata/dependent-chart-alias/charts/alpine/templates/alpine-pod.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/values.yaml b/pkg/chart/util/testdata/dependent-chart-alias/charts/alpine/values.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/values.yaml rename to pkg/chart/util/testdata/dependent-chart-alias/charts/alpine/values.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-alias/charts/mariner-4.3.2.tgz b/pkg/chart/util/testdata/dependent-chart-alias/charts/mariner-4.3.2.tgz similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-alias/charts/mariner-4.3.2.tgz rename to pkg/chart/util/testdata/dependent-chart-alias/charts/mariner-4.3.2.tgz diff --git a/pkg/chartutil/testdata/dependent-chart-alias/docs/README.md b/pkg/chart/util/testdata/dependent-chart-alias/docs/README.md similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-alias/docs/README.md rename to pkg/chart/util/testdata/dependent-chart-alias/docs/README.md diff --git a/pkg/chartutil/testdata/dependent-chart-alias/icon.svg b/pkg/chart/util/testdata/dependent-chart-alias/icon.svg similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-alias/icon.svg rename to pkg/chart/util/testdata/dependent-chart-alias/icon.svg diff --git a/pkg/chartutil/testdata/dependent-chart-alias/ignore/me.txt b/pkg/chart/util/testdata/dependent-chart-alias/ignore/me.txt similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-alias/ignore/me.txt rename to pkg/chart/util/testdata/dependent-chart-alias/ignore/me.txt diff --git a/pkg/chartutil/testdata/dependent-chart-alias/templates/template.tpl b/pkg/chart/util/testdata/dependent-chart-alias/templates/template.tpl similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-alias/templates/template.tpl rename to pkg/chart/util/testdata/dependent-chart-alias/templates/template.tpl diff --git a/pkg/chartutil/testdata/dependent-chart-alias/values.yaml b/pkg/chart/util/testdata/dependent-chart-alias/values.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-alias/values.yaml rename to pkg/chart/util/testdata/dependent-chart-alias/values.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-helmignore/.helmignore b/pkg/chart/util/testdata/dependent-chart-helmignore/.helmignore similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-helmignore/.helmignore rename to pkg/chart/util/testdata/dependent-chart-helmignore/.helmignore diff --git a/pkg/chartutil/testdata/dependent-chart-helmignore/Chart.yaml b/pkg/chart/util/testdata/dependent-chart-helmignore/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-helmignore/Chart.yaml rename to pkg/chart/util/testdata/dependent-chart-helmignore/Chart.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/.ignore_me b/pkg/chart/util/testdata/dependent-chart-helmignore/charts/.ignore_me similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-helmignore/charts/.ignore_me rename to pkg/chart/util/testdata/dependent-chart-helmignore/charts/.ignore_me diff --git a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/_ignore_me b/pkg/chart/util/testdata/dependent-chart-helmignore/charts/_ignore_me similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-helmignore/charts/_ignore_me rename to pkg/chart/util/testdata/dependent-chart-helmignore/charts/_ignore_me diff --git a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/Chart.yaml b/pkg/chart/util/testdata/dependent-chart-helmignore/charts/alpine/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/Chart.yaml rename to pkg/chart/util/testdata/dependent-chart-helmignore/charts/alpine/Chart.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/README.md b/pkg/chart/util/testdata/dependent-chart-helmignore/charts/alpine/README.md similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/README.md rename to pkg/chart/util/testdata/dependent-chart-helmignore/charts/alpine/README.md diff --git a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/charts/mast1/Chart.yaml b/pkg/chart/util/testdata/dependent-chart-helmignore/charts/alpine/charts/mast1/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/charts/mast1/Chart.yaml rename to pkg/chart/util/testdata/dependent-chart-helmignore/charts/alpine/charts/mast1/Chart.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/charts/mast1/values.yaml b/pkg/chart/util/testdata/dependent-chart-helmignore/charts/alpine/charts/mast1/values.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/charts/mast1/values.yaml rename to pkg/chart/util/testdata/dependent-chart-helmignore/charts/alpine/charts/mast1/values.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chart/util/testdata/dependent-chart-helmignore/charts/alpine/charts/mast2-0.1.0.tgz similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/charts/mast2-0.1.0.tgz rename to pkg/chart/util/testdata/dependent-chart-helmignore/charts/alpine/charts/mast2-0.1.0.tgz diff --git a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/templates/alpine-pod.yaml b/pkg/chart/util/testdata/dependent-chart-helmignore/charts/alpine/templates/alpine-pod.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/templates/alpine-pod.yaml rename to pkg/chart/util/testdata/dependent-chart-helmignore/charts/alpine/templates/alpine-pod.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/values.yaml b/pkg/chart/util/testdata/dependent-chart-helmignore/charts/alpine/values.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/values.yaml rename to pkg/chart/util/testdata/dependent-chart-helmignore/charts/alpine/values.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-helmignore/templates/template.tpl b/pkg/chart/util/testdata/dependent-chart-helmignore/templates/template.tpl similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-helmignore/templates/template.tpl rename to pkg/chart/util/testdata/dependent-chart-helmignore/templates/template.tpl diff --git a/pkg/chartutil/testdata/dependent-chart-helmignore/values.yaml b/pkg/chart/util/testdata/dependent-chart-helmignore/values.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-helmignore/values.yaml rename to pkg/chart/util/testdata/dependent-chart-helmignore/values.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/.helmignore b/pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/.helmignore similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/.helmignore rename to pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/.helmignore diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/Chart.yaml b/pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/Chart.yaml rename to pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/Chart.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/INSTALL.txt b/pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/INSTALL.txt similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/INSTALL.txt rename to pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/INSTALL.txt diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/LICENSE b/pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/LICENSE similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/LICENSE rename to pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/LICENSE diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/README.md b/pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/README.md similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/README.md rename to pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/README.md diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/_ignore_me b/pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/charts/_ignore_me similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/_ignore_me rename to pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/charts/_ignore_me diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/Chart.yaml b/pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/charts/alpine/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/Chart.yaml rename to pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/charts/alpine/Chart.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/README.md b/pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/charts/alpine/README.md similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/README.md rename to pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/charts/alpine/README.md diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml b/pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml rename to pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/charts/mast1/values.yaml b/pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/charts/alpine/charts/mast1/values.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/charts/mast1/values.yaml rename to pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/charts/alpine/charts/mast1/values.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz rename to pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/templates/alpine-pod.yaml b/pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/charts/alpine/templates/alpine-pod.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/templates/alpine-pod.yaml rename to pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/charts/alpine/templates/alpine-pod.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/values.yaml b/pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/charts/alpine/values.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/values.yaml rename to pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/charts/alpine/values.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/mariner-4.3.2.tgz b/pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/charts/mariner-4.3.2.tgz similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/mariner-4.3.2.tgz rename to pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/charts/mariner-4.3.2.tgz diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/docs/README.md b/pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/docs/README.md similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/docs/README.md rename to pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/docs/README.md diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/icon.svg b/pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/icon.svg similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/icon.svg rename to pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/icon.svg diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/ignore/me.txt b/pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/ignore/me.txt similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/ignore/me.txt rename to pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/ignore/me.txt diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/templates/template.tpl b/pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/templates/template.tpl similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/templates/template.tpl rename to pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/templates/template.tpl diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/values.yaml b/pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/values.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/values.yaml rename to pkg/chart/util/testdata/dependent-chart-no-requirements-yaml/values.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/.helmignore b/pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/.helmignore similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/.helmignore rename to pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/.helmignore diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/Chart.yaml b/pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/Chart.yaml rename to pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/Chart.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/INSTALL.txt b/pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/INSTALL.txt similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/INSTALL.txt rename to pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/INSTALL.txt diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/LICENSE b/pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/LICENSE similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/LICENSE rename to pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/LICENSE diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/README.md b/pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/README.md similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/README.md rename to pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/README.md diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/_ignore_me b/pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/charts/_ignore_me similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/_ignore_me rename to pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/charts/_ignore_me diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/Chart.yaml b/pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/Chart.yaml rename to pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/Chart.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/README.md b/pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/README.md similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/README.md rename to pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/README.md diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml b/pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml rename to pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/charts/mast1/values.yaml b/pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/charts/mast1/values.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/charts/mast1/values.yaml rename to pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/charts/mast1/values.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz rename to pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/templates/alpine-pod.yaml b/pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/templates/alpine-pod.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/templates/alpine-pod.yaml rename to pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/templates/alpine-pod.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/values.yaml b/pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/values.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/values.yaml rename to pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/values.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/mariner-4.3.2.tgz b/pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/charts/mariner-4.3.2.tgz similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/mariner-4.3.2.tgz rename to pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/charts/mariner-4.3.2.tgz diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/docs/README.md b/pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/docs/README.md similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/docs/README.md rename to pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/docs/README.md diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/icon.svg b/pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/icon.svg similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/icon.svg rename to pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/icon.svg diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/ignore/me.txt b/pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/ignore/me.txt similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/ignore/me.txt rename to pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/ignore/me.txt diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/templates/template.tpl b/pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/templates/template.tpl similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/templates/template.tpl rename to pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/templates/template.tpl diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/values.yaml b/pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/values.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/values.yaml rename to pkg/chart/util/testdata/dependent-chart-with-all-in-requirements-yaml/values.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/.helmignore b/pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/.helmignore similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/.helmignore rename to pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/.helmignore diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/Chart.yaml b/pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/Chart.yaml rename to pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/Chart.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/INSTALL.txt b/pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/INSTALL.txt similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/INSTALL.txt rename to pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/INSTALL.txt diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/LICENSE b/pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/LICENSE similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/LICENSE rename to pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/LICENSE diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/README.md b/pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/README.md similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/README.md rename to pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/README.md diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/_ignore_me b/pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/charts/_ignore_me similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/_ignore_me rename to pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/charts/_ignore_me diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/Chart.yaml b/pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/Chart.yaml rename to pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/Chart.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/README.md b/pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/README.md similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/README.md rename to pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/README.md diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml b/pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml rename to pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/charts/mast1/values.yaml b/pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/charts/mast1/values.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/charts/mast1/values.yaml rename to pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/charts/mast1/values.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz rename to pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/templates/alpine-pod.yaml b/pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/templates/alpine-pod.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/templates/alpine-pod.yaml rename to pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/templates/alpine-pod.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/values.yaml b/pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/values.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/values.yaml rename to pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/values.yaml diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/mariner-4.3.2.tgz b/pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/charts/mariner-4.3.2.tgz similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/mariner-4.3.2.tgz rename to pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/charts/mariner-4.3.2.tgz diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/docs/README.md b/pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/docs/README.md similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/docs/README.md rename to pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/docs/README.md diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/icon.svg b/pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/icon.svg similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/icon.svg rename to pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/icon.svg diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/ignore/me.txt b/pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/ignore/me.txt similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/ignore/me.txt rename to pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/ignore/me.txt diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/templates/template.tpl b/pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/templates/template.tpl similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/templates/template.tpl rename to pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/templates/template.tpl diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/values.yaml b/pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/values.yaml similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/values.yaml rename to pkg/chart/util/testdata/dependent-chart-with-mixed-requirements-yaml/values.yaml diff --git a/pkg/chartutil/testdata/frobnitz-1.2.3.tgz b/pkg/chart/util/testdata/frobnitz-1.2.3.tgz similarity index 100% rename from pkg/chartutil/testdata/frobnitz-1.2.3.tgz rename to pkg/chart/util/testdata/frobnitz-1.2.3.tgz diff --git a/pkg/chartutil/testdata/frobnitz/.helmignore b/pkg/chart/util/testdata/frobnitz/.helmignore similarity index 100% rename from pkg/chartutil/testdata/frobnitz/.helmignore rename to pkg/chart/util/testdata/frobnitz/.helmignore diff --git a/pkg/chartutil/testdata/frobnitz/Chart.lock b/pkg/chart/util/testdata/frobnitz/Chart.lock similarity index 100% rename from pkg/chartutil/testdata/frobnitz/Chart.lock rename to pkg/chart/util/testdata/frobnitz/Chart.lock diff --git a/pkg/chartutil/testdata/frobnitz/Chart.yaml b/pkg/chart/util/testdata/frobnitz/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/frobnitz/Chart.yaml rename to pkg/chart/util/testdata/frobnitz/Chart.yaml diff --git a/pkg/chartutil/testdata/frobnitz/INSTALL.txt b/pkg/chart/util/testdata/frobnitz/INSTALL.txt similarity index 100% rename from pkg/chartutil/testdata/frobnitz/INSTALL.txt rename to pkg/chart/util/testdata/frobnitz/INSTALL.txt diff --git a/pkg/chartutil/testdata/frobnitz/LICENSE b/pkg/chart/util/testdata/frobnitz/LICENSE similarity index 100% rename from pkg/chartutil/testdata/frobnitz/LICENSE rename to pkg/chart/util/testdata/frobnitz/LICENSE diff --git a/pkg/chartutil/testdata/frobnitz/README.md b/pkg/chart/util/testdata/frobnitz/README.md similarity index 100% rename from pkg/chartutil/testdata/frobnitz/README.md rename to pkg/chart/util/testdata/frobnitz/README.md diff --git a/pkg/chartutil/testdata/frobnitz/charts/_ignore_me b/pkg/chart/util/testdata/frobnitz/charts/_ignore_me similarity index 100% rename from pkg/chartutil/testdata/frobnitz/charts/_ignore_me rename to pkg/chart/util/testdata/frobnitz/charts/_ignore_me diff --git a/pkg/chartutil/testdata/frobnitz/charts/alpine/Chart.yaml b/pkg/chart/util/testdata/frobnitz/charts/alpine/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/frobnitz/charts/alpine/Chart.yaml rename to pkg/chart/util/testdata/frobnitz/charts/alpine/Chart.yaml diff --git a/pkg/chartutil/testdata/frobnitz/charts/alpine/README.md b/pkg/chart/util/testdata/frobnitz/charts/alpine/README.md similarity index 100% rename from pkg/chartutil/testdata/frobnitz/charts/alpine/README.md rename to pkg/chart/util/testdata/frobnitz/charts/alpine/README.md diff --git a/pkg/chartutil/testdata/frobnitz/charts/alpine/charts/mast1/Chart.yaml b/pkg/chart/util/testdata/frobnitz/charts/alpine/charts/mast1/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/frobnitz/charts/alpine/charts/mast1/Chart.yaml rename to pkg/chart/util/testdata/frobnitz/charts/alpine/charts/mast1/Chart.yaml diff --git a/pkg/chartutil/testdata/frobnitz/charts/alpine/charts/mast1/values.yaml b/pkg/chart/util/testdata/frobnitz/charts/alpine/charts/mast1/values.yaml similarity index 100% rename from pkg/chartutil/testdata/frobnitz/charts/alpine/charts/mast1/values.yaml rename to pkg/chart/util/testdata/frobnitz/charts/alpine/charts/mast1/values.yaml diff --git a/pkg/chartutil/testdata/frobnitz/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chart/util/testdata/frobnitz/charts/alpine/charts/mast2-0.1.0.tgz similarity index 100% rename from pkg/chartutil/testdata/frobnitz/charts/alpine/charts/mast2-0.1.0.tgz rename to pkg/chart/util/testdata/frobnitz/charts/alpine/charts/mast2-0.1.0.tgz diff --git a/pkg/chartutil/testdata/frobnitz/charts/alpine/templates/alpine-pod.yaml b/pkg/chart/util/testdata/frobnitz/charts/alpine/templates/alpine-pod.yaml similarity index 100% rename from pkg/chartutil/testdata/frobnitz/charts/alpine/templates/alpine-pod.yaml rename to pkg/chart/util/testdata/frobnitz/charts/alpine/templates/alpine-pod.yaml diff --git a/pkg/chartutil/testdata/frobnitz/charts/alpine/values.yaml b/pkg/chart/util/testdata/frobnitz/charts/alpine/values.yaml similarity index 100% rename from pkg/chartutil/testdata/frobnitz/charts/alpine/values.yaml rename to pkg/chart/util/testdata/frobnitz/charts/alpine/values.yaml diff --git a/pkg/chartutil/testdata/frobnitz/charts/mariner/Chart.yaml b/pkg/chart/util/testdata/frobnitz/charts/mariner/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/frobnitz/charts/mariner/Chart.yaml rename to pkg/chart/util/testdata/frobnitz/charts/mariner/Chart.yaml diff --git a/pkg/chartutil/testdata/frobnitz/charts/mariner/charts/albatross/Chart.yaml b/pkg/chart/util/testdata/frobnitz/charts/mariner/charts/albatross/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/frobnitz/charts/mariner/charts/albatross/Chart.yaml rename to pkg/chart/util/testdata/frobnitz/charts/mariner/charts/albatross/Chart.yaml diff --git a/pkg/chartutil/testdata/frobnitz/charts/mariner/charts/albatross/values.yaml b/pkg/chart/util/testdata/frobnitz/charts/mariner/charts/albatross/values.yaml similarity index 100% rename from pkg/chartutil/testdata/frobnitz/charts/mariner/charts/albatross/values.yaml rename to pkg/chart/util/testdata/frobnitz/charts/mariner/charts/albatross/values.yaml diff --git a/pkg/chartutil/testdata/frobnitz/charts/mariner/templates/placeholder.tpl b/pkg/chart/util/testdata/frobnitz/charts/mariner/templates/placeholder.tpl similarity index 100% rename from pkg/chartutil/testdata/frobnitz/charts/mariner/templates/placeholder.tpl rename to pkg/chart/util/testdata/frobnitz/charts/mariner/templates/placeholder.tpl diff --git a/pkg/chartutil/testdata/frobnitz/charts/mariner/values.yaml b/pkg/chart/util/testdata/frobnitz/charts/mariner/values.yaml similarity index 100% rename from pkg/chartutil/testdata/frobnitz/charts/mariner/values.yaml rename to pkg/chart/util/testdata/frobnitz/charts/mariner/values.yaml diff --git a/pkg/chartutil/testdata/frobnitz/docs/README.md b/pkg/chart/util/testdata/frobnitz/docs/README.md similarity index 100% rename from pkg/chartutil/testdata/frobnitz/docs/README.md rename to pkg/chart/util/testdata/frobnitz/docs/README.md diff --git a/pkg/chartutil/testdata/frobnitz/icon.svg b/pkg/chart/util/testdata/frobnitz/icon.svg similarity index 100% rename from pkg/chartutil/testdata/frobnitz/icon.svg rename to pkg/chart/util/testdata/frobnitz/icon.svg diff --git a/pkg/chartutil/testdata/frobnitz/ignore/me.txt b/pkg/chart/util/testdata/frobnitz/ignore/me.txt similarity index 100% rename from pkg/chartutil/testdata/frobnitz/ignore/me.txt rename to pkg/chart/util/testdata/frobnitz/ignore/me.txt diff --git a/pkg/chartutil/testdata/frobnitz/templates/template.tpl b/pkg/chart/util/testdata/frobnitz/templates/template.tpl similarity index 100% rename from pkg/chartutil/testdata/frobnitz/templates/template.tpl rename to pkg/chart/util/testdata/frobnitz/templates/template.tpl diff --git a/pkg/chartutil/testdata/frobnitz/values.yaml b/pkg/chart/util/testdata/frobnitz/values.yaml similarity index 100% rename from pkg/chartutil/testdata/frobnitz/values.yaml rename to pkg/chart/util/testdata/frobnitz/values.yaml diff --git a/pkg/chartutil/testdata/frobnitz_backslash-1.2.3.tgz b/pkg/chart/util/testdata/frobnitz_backslash-1.2.3.tgz similarity index 100% rename from pkg/chartutil/testdata/frobnitz_backslash-1.2.3.tgz rename to pkg/chart/util/testdata/frobnitz_backslash-1.2.3.tgz diff --git a/pkg/chartutil/testdata/genfrob.sh b/pkg/chart/util/testdata/genfrob.sh similarity index 100% rename from pkg/chartutil/testdata/genfrob.sh rename to pkg/chart/util/testdata/genfrob.sh diff --git a/pkg/chartutil/testdata/import-values-from-enabled-subchart/parent-chart/Chart.lock b/pkg/chart/util/testdata/import-values-from-enabled-subchart/parent-chart/Chart.lock similarity index 100% rename from pkg/chartutil/testdata/import-values-from-enabled-subchart/parent-chart/Chart.lock rename to pkg/chart/util/testdata/import-values-from-enabled-subchart/parent-chart/Chart.lock diff --git a/pkg/chartutil/testdata/import-values-from-enabled-subchart/parent-chart/Chart.yaml b/pkg/chart/util/testdata/import-values-from-enabled-subchart/parent-chart/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/import-values-from-enabled-subchart/parent-chart/Chart.yaml rename to pkg/chart/util/testdata/import-values-from-enabled-subchart/parent-chart/Chart.yaml diff --git a/pkg/chartutil/testdata/import-values-from-enabled-subchart/parent-chart/charts/dev-v0.1.0.tgz b/pkg/chart/util/testdata/import-values-from-enabled-subchart/parent-chart/charts/dev-v0.1.0.tgz similarity index 100% rename from pkg/chartutil/testdata/import-values-from-enabled-subchart/parent-chart/charts/dev-v0.1.0.tgz rename to pkg/chart/util/testdata/import-values-from-enabled-subchart/parent-chart/charts/dev-v0.1.0.tgz diff --git a/pkg/chartutil/testdata/import-values-from-enabled-subchart/parent-chart/charts/prod-v0.1.0.tgz b/pkg/chart/util/testdata/import-values-from-enabled-subchart/parent-chart/charts/prod-v0.1.0.tgz similarity index 100% rename from pkg/chartutil/testdata/import-values-from-enabled-subchart/parent-chart/charts/prod-v0.1.0.tgz rename to pkg/chart/util/testdata/import-values-from-enabled-subchart/parent-chart/charts/prod-v0.1.0.tgz diff --git a/pkg/chartutil/testdata/import-values-from-enabled-subchart/parent-chart/envs/dev/Chart.yaml b/pkg/chart/util/testdata/import-values-from-enabled-subchart/parent-chart/envs/dev/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/import-values-from-enabled-subchart/parent-chart/envs/dev/Chart.yaml rename to pkg/chart/util/testdata/import-values-from-enabled-subchart/parent-chart/envs/dev/Chart.yaml diff --git a/pkg/chartutil/testdata/import-values-from-enabled-subchart/parent-chart/envs/dev/values.yaml b/pkg/chart/util/testdata/import-values-from-enabled-subchart/parent-chart/envs/dev/values.yaml similarity index 100% rename from pkg/chartutil/testdata/import-values-from-enabled-subchart/parent-chart/envs/dev/values.yaml rename to pkg/chart/util/testdata/import-values-from-enabled-subchart/parent-chart/envs/dev/values.yaml diff --git a/pkg/chartutil/testdata/import-values-from-enabled-subchart/parent-chart/envs/prod/Chart.yaml b/pkg/chart/util/testdata/import-values-from-enabled-subchart/parent-chart/envs/prod/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/import-values-from-enabled-subchart/parent-chart/envs/prod/Chart.yaml rename to pkg/chart/util/testdata/import-values-from-enabled-subchart/parent-chart/envs/prod/Chart.yaml diff --git a/pkg/chartutil/testdata/import-values-from-enabled-subchart/parent-chart/envs/prod/values.yaml b/pkg/chart/util/testdata/import-values-from-enabled-subchart/parent-chart/envs/prod/values.yaml similarity index 100% rename from pkg/chartutil/testdata/import-values-from-enabled-subchart/parent-chart/envs/prod/values.yaml rename to pkg/chart/util/testdata/import-values-from-enabled-subchart/parent-chart/envs/prod/values.yaml diff --git a/pkg/chartutil/testdata/import-values-from-enabled-subchart/parent-chart/templates/autoscaler.yaml b/pkg/chart/util/testdata/import-values-from-enabled-subchart/parent-chart/templates/autoscaler.yaml similarity index 100% rename from pkg/chartutil/testdata/import-values-from-enabled-subchart/parent-chart/templates/autoscaler.yaml rename to pkg/chart/util/testdata/import-values-from-enabled-subchart/parent-chart/templates/autoscaler.yaml diff --git a/pkg/chartutil/testdata/import-values-from-enabled-subchart/parent-chart/values.yaml b/pkg/chart/util/testdata/import-values-from-enabled-subchart/parent-chart/values.yaml similarity index 100% rename from pkg/chartutil/testdata/import-values-from-enabled-subchart/parent-chart/values.yaml rename to pkg/chart/util/testdata/import-values-from-enabled-subchart/parent-chart/values.yaml diff --git a/pkg/chartutil/testdata/joonix/Chart.yaml b/pkg/chart/util/testdata/joonix/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/joonix/Chart.yaml rename to pkg/chart/util/testdata/joonix/Chart.yaml diff --git a/pkg/chartutil/testdata/joonix/charts/.gitkeep b/pkg/chart/util/testdata/joonix/charts/.gitkeep similarity index 100% rename from pkg/chartutil/testdata/joonix/charts/.gitkeep rename to pkg/chart/util/testdata/joonix/charts/.gitkeep diff --git a/pkg/chartutil/testdata/subpop/Chart.yaml b/pkg/chart/util/testdata/subpop/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/subpop/Chart.yaml rename to pkg/chart/util/testdata/subpop/Chart.yaml diff --git a/pkg/chartutil/testdata/subpop/README.md b/pkg/chart/util/testdata/subpop/README.md similarity index 100% rename from pkg/chartutil/testdata/subpop/README.md rename to pkg/chart/util/testdata/subpop/README.md diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/Chart.yaml b/pkg/chart/util/testdata/subpop/charts/subchart1/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/subpop/charts/subchart1/Chart.yaml rename to pkg/chart/util/testdata/subpop/charts/subchart1/Chart.yaml diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/Chart.yaml b/pkg/chart/util/testdata/subpop/charts/subchart1/charts/subchartA/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/Chart.yaml rename to pkg/chart/util/testdata/subpop/charts/subchart1/charts/subchartA/Chart.yaml diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/templates/service.yaml b/pkg/chart/util/testdata/subpop/charts/subchart1/charts/subchartA/templates/service.yaml similarity index 100% rename from pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/templates/service.yaml rename to pkg/chart/util/testdata/subpop/charts/subchart1/charts/subchartA/templates/service.yaml diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/values.yaml b/pkg/chart/util/testdata/subpop/charts/subchart1/charts/subchartA/values.yaml similarity index 100% rename from pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/values.yaml rename to pkg/chart/util/testdata/subpop/charts/subchart1/charts/subchartA/values.yaml diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/Chart.yaml b/pkg/chart/util/testdata/subpop/charts/subchart1/charts/subchartB/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/Chart.yaml rename to pkg/chart/util/testdata/subpop/charts/subchart1/charts/subchartB/Chart.yaml diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/templates/service.yaml b/pkg/chart/util/testdata/subpop/charts/subchart1/charts/subchartB/templates/service.yaml similarity index 100% rename from pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/templates/service.yaml rename to pkg/chart/util/testdata/subpop/charts/subchart1/charts/subchartB/templates/service.yaml diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml b/pkg/chart/util/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml similarity index 100% rename from pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml rename to pkg/chart/util/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/crds/crdA.yaml b/pkg/chart/util/testdata/subpop/charts/subchart1/crds/crdA.yaml similarity index 100% rename from pkg/chartutil/testdata/subpop/charts/subchart1/crds/crdA.yaml rename to pkg/chart/util/testdata/subpop/charts/subchart1/crds/crdA.yaml diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/templates/NOTES.txt b/pkg/chart/util/testdata/subpop/charts/subchart1/templates/NOTES.txt similarity index 100% rename from pkg/chartutil/testdata/subpop/charts/subchart1/templates/NOTES.txt rename to pkg/chart/util/testdata/subpop/charts/subchart1/templates/NOTES.txt diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/templates/service.yaml b/pkg/chart/util/testdata/subpop/charts/subchart1/templates/service.yaml similarity index 100% rename from pkg/chartutil/testdata/subpop/charts/subchart1/templates/service.yaml rename to pkg/chart/util/testdata/subpop/charts/subchart1/templates/service.yaml diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/templates/subdir/role.yaml b/pkg/chart/util/testdata/subpop/charts/subchart1/templates/subdir/role.yaml similarity index 100% rename from pkg/chartutil/testdata/subpop/charts/subchart1/templates/subdir/role.yaml rename to pkg/chart/util/testdata/subpop/charts/subchart1/templates/subdir/role.yaml diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/templates/subdir/rolebinding.yaml b/pkg/chart/util/testdata/subpop/charts/subchart1/templates/subdir/rolebinding.yaml similarity index 100% rename from pkg/chartutil/testdata/subpop/charts/subchart1/templates/subdir/rolebinding.yaml rename to pkg/chart/util/testdata/subpop/charts/subchart1/templates/subdir/rolebinding.yaml diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/templates/subdir/serviceaccount.yaml b/pkg/chart/util/testdata/subpop/charts/subchart1/templates/subdir/serviceaccount.yaml similarity index 100% rename from pkg/chartutil/testdata/subpop/charts/subchart1/templates/subdir/serviceaccount.yaml rename to pkg/chart/util/testdata/subpop/charts/subchart1/templates/subdir/serviceaccount.yaml diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml b/pkg/chart/util/testdata/subpop/charts/subchart1/values.yaml similarity index 100% rename from pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml rename to pkg/chart/util/testdata/subpop/charts/subchart1/values.yaml diff --git a/pkg/chartutil/testdata/subpop/charts/subchart2/Chart.yaml b/pkg/chart/util/testdata/subpop/charts/subchart2/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/subpop/charts/subchart2/Chart.yaml rename to pkg/chart/util/testdata/subpop/charts/subchart2/Chart.yaml diff --git a/pkg/chartutil/testdata/subpop/charts/subchart2/charts/subchartB/Chart.yaml b/pkg/chart/util/testdata/subpop/charts/subchart2/charts/subchartB/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/subpop/charts/subchart2/charts/subchartB/Chart.yaml rename to pkg/chart/util/testdata/subpop/charts/subchart2/charts/subchartB/Chart.yaml diff --git a/pkg/chartutil/testdata/subpop/charts/subchart2/charts/subchartB/templates/service.yaml b/pkg/chart/util/testdata/subpop/charts/subchart2/charts/subchartB/templates/service.yaml similarity index 100% rename from pkg/chartutil/testdata/subpop/charts/subchart2/charts/subchartB/templates/service.yaml rename to pkg/chart/util/testdata/subpop/charts/subchart2/charts/subchartB/templates/service.yaml diff --git a/pkg/chartutil/testdata/subpop/charts/subchart2/charts/subchartB/values.yaml b/pkg/chart/util/testdata/subpop/charts/subchart2/charts/subchartB/values.yaml similarity index 100% rename from pkg/chartutil/testdata/subpop/charts/subchart2/charts/subchartB/values.yaml rename to pkg/chart/util/testdata/subpop/charts/subchart2/charts/subchartB/values.yaml diff --git a/pkg/chartutil/testdata/subpop/charts/subchart2/charts/subchartC/Chart.yaml b/pkg/chart/util/testdata/subpop/charts/subchart2/charts/subchartC/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/subpop/charts/subchart2/charts/subchartC/Chart.yaml rename to pkg/chart/util/testdata/subpop/charts/subchart2/charts/subchartC/Chart.yaml diff --git a/pkg/chartutil/testdata/subpop/charts/subchart2/charts/subchartC/templates/service.yaml b/pkg/chart/util/testdata/subpop/charts/subchart2/charts/subchartC/templates/service.yaml similarity index 100% rename from pkg/chartutil/testdata/subpop/charts/subchart2/charts/subchartC/templates/service.yaml rename to pkg/chart/util/testdata/subpop/charts/subchart2/charts/subchartC/templates/service.yaml diff --git a/pkg/chartutil/testdata/subpop/charts/subchart2/charts/subchartC/values.yaml b/pkg/chart/util/testdata/subpop/charts/subchart2/charts/subchartC/values.yaml similarity index 100% rename from pkg/chartutil/testdata/subpop/charts/subchart2/charts/subchartC/values.yaml rename to pkg/chart/util/testdata/subpop/charts/subchart2/charts/subchartC/values.yaml diff --git a/pkg/chartutil/testdata/subpop/charts/subchart2/templates/service.yaml b/pkg/chart/util/testdata/subpop/charts/subchart2/templates/service.yaml similarity index 100% rename from pkg/chartutil/testdata/subpop/charts/subchart2/templates/service.yaml rename to pkg/chart/util/testdata/subpop/charts/subchart2/templates/service.yaml diff --git a/pkg/chartutil/testdata/subpop/charts/subchart2/values.yaml b/pkg/chart/util/testdata/subpop/charts/subchart2/values.yaml similarity index 100% rename from pkg/chartutil/testdata/subpop/charts/subchart2/values.yaml rename to pkg/chart/util/testdata/subpop/charts/subchart2/values.yaml diff --git a/pkg/chartutil/testdata/subpop/noreqs/Chart.yaml b/pkg/chart/util/testdata/subpop/noreqs/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/subpop/noreqs/Chart.yaml rename to pkg/chart/util/testdata/subpop/noreqs/Chart.yaml diff --git a/pkg/chartutil/testdata/subpop/noreqs/templates/service.yaml b/pkg/chart/util/testdata/subpop/noreqs/templates/service.yaml similarity index 100% rename from pkg/chartutil/testdata/subpop/noreqs/templates/service.yaml rename to pkg/chart/util/testdata/subpop/noreqs/templates/service.yaml diff --git a/pkg/chartutil/testdata/subpop/noreqs/values.yaml b/pkg/chart/util/testdata/subpop/noreqs/values.yaml similarity index 100% rename from pkg/chartutil/testdata/subpop/noreqs/values.yaml rename to pkg/chart/util/testdata/subpop/noreqs/values.yaml diff --git a/pkg/chartutil/testdata/subpop/values.yaml b/pkg/chart/util/testdata/subpop/values.yaml similarity index 100% rename from pkg/chartutil/testdata/subpop/values.yaml rename to pkg/chart/util/testdata/subpop/values.yaml diff --git a/pkg/chartutil/testdata/test-values-invalid.schema.json b/pkg/chart/util/testdata/test-values-invalid.schema.json similarity index 100% rename from pkg/chartutil/testdata/test-values-invalid.schema.json rename to pkg/chart/util/testdata/test-values-invalid.schema.json diff --git a/pkg/chartutil/testdata/test-values-negative.yaml b/pkg/chart/util/testdata/test-values-negative.yaml similarity index 100% rename from pkg/chartutil/testdata/test-values-negative.yaml rename to pkg/chart/util/testdata/test-values-negative.yaml diff --git a/pkg/chartutil/testdata/test-values.schema.json b/pkg/chart/util/testdata/test-values.schema.json similarity index 100% rename from pkg/chartutil/testdata/test-values.schema.json rename to pkg/chart/util/testdata/test-values.schema.json diff --git a/pkg/chartutil/testdata/test-values.yaml b/pkg/chart/util/testdata/test-values.yaml similarity index 100% rename from pkg/chartutil/testdata/test-values.yaml rename to pkg/chart/util/testdata/test-values.yaml diff --git a/pkg/chartutil/testdata/three-level-dependent-chart/README.md b/pkg/chart/util/testdata/three-level-dependent-chart/README.md similarity index 100% rename from pkg/chartutil/testdata/three-level-dependent-chart/README.md rename to pkg/chart/util/testdata/three-level-dependent-chart/README.md diff --git a/pkg/chartutil/testdata/three-level-dependent-chart/umbrella/Chart.yaml b/pkg/chart/util/testdata/three-level-dependent-chart/umbrella/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/three-level-dependent-chart/umbrella/Chart.yaml rename to pkg/chart/util/testdata/three-level-dependent-chart/umbrella/Chart.yaml diff --git a/pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app1/Chart.yaml b/pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app1/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app1/Chart.yaml rename to pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app1/Chart.yaml diff --git a/pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app1/charts/library/Chart.yaml b/pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app1/charts/library/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app1/charts/library/Chart.yaml rename to pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app1/charts/library/Chart.yaml diff --git a/pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app1/charts/library/templates/service.yaml b/pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app1/charts/library/templates/service.yaml similarity index 100% rename from pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app1/charts/library/templates/service.yaml rename to pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app1/charts/library/templates/service.yaml diff --git a/pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app1/charts/library/values.yaml b/pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app1/charts/library/values.yaml similarity index 100% rename from pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app1/charts/library/values.yaml rename to pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app1/charts/library/values.yaml diff --git a/pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app1/templates/service.yaml b/pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app1/templates/service.yaml similarity index 100% rename from pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app1/templates/service.yaml rename to pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app1/templates/service.yaml diff --git a/pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app1/values.yaml b/pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app1/values.yaml similarity index 100% rename from pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app1/values.yaml rename to pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app1/values.yaml diff --git a/pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app2/Chart.yaml b/pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app2/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app2/Chart.yaml rename to pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app2/Chart.yaml diff --git a/pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app2/charts/library/Chart.yaml b/pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app2/charts/library/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app2/charts/library/Chart.yaml rename to pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app2/charts/library/Chart.yaml diff --git a/pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app2/charts/library/templates/service.yaml b/pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app2/charts/library/templates/service.yaml similarity index 100% rename from pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app2/charts/library/templates/service.yaml rename to pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app2/charts/library/templates/service.yaml diff --git a/pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app2/charts/library/values.yaml b/pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app2/charts/library/values.yaml similarity index 100% rename from pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app2/charts/library/values.yaml rename to pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app2/charts/library/values.yaml diff --git a/pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app2/templates/service.yaml b/pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app2/templates/service.yaml similarity index 100% rename from pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app2/templates/service.yaml rename to pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app2/templates/service.yaml diff --git a/pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app2/values.yaml b/pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app2/values.yaml similarity index 100% rename from pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app2/values.yaml rename to pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app2/values.yaml diff --git a/pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app3/Chart.yaml b/pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app3/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app3/Chart.yaml rename to pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app3/Chart.yaml diff --git a/pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app3/charts/library/Chart.yaml b/pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app3/charts/library/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app3/charts/library/Chart.yaml rename to pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app3/charts/library/Chart.yaml diff --git a/pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app3/charts/library/templates/service.yaml b/pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app3/charts/library/templates/service.yaml similarity index 100% rename from pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app3/charts/library/templates/service.yaml rename to pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app3/charts/library/templates/service.yaml diff --git a/pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app3/charts/library/values.yaml b/pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app3/charts/library/values.yaml similarity index 100% rename from pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app3/charts/library/values.yaml rename to pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app3/charts/library/values.yaml diff --git a/pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app3/templates/service.yaml b/pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app3/templates/service.yaml similarity index 100% rename from pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app3/templates/service.yaml rename to pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app3/templates/service.yaml diff --git a/pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app3/values.yaml b/pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app3/values.yaml similarity index 100% rename from pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app3/values.yaml rename to pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app3/values.yaml diff --git a/pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app4/Chart.yaml b/pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app4/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app4/Chart.yaml rename to pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app4/Chart.yaml diff --git a/pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app4/charts/library/Chart.yaml b/pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app4/charts/library/Chart.yaml similarity index 100% rename from pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app4/charts/library/Chart.yaml rename to pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app4/charts/library/Chart.yaml diff --git a/pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app4/charts/library/templates/service.yaml b/pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app4/charts/library/templates/service.yaml similarity index 100% rename from pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app4/charts/library/templates/service.yaml rename to pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app4/charts/library/templates/service.yaml diff --git a/pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app4/charts/library/values.yaml b/pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app4/charts/library/values.yaml similarity index 100% rename from pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app4/charts/library/values.yaml rename to pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app4/charts/library/values.yaml diff --git a/pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app4/templates/service.yaml b/pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app4/templates/service.yaml similarity index 100% rename from pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app4/templates/service.yaml rename to pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app4/templates/service.yaml diff --git a/pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app4/values.yaml b/pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app4/values.yaml similarity index 100% rename from pkg/chartutil/testdata/three-level-dependent-chart/umbrella/charts/app4/values.yaml rename to pkg/chart/util/testdata/three-level-dependent-chart/umbrella/charts/app4/values.yaml diff --git a/pkg/chartutil/testdata/three-level-dependent-chart/umbrella/values.yaml b/pkg/chart/util/testdata/three-level-dependent-chart/umbrella/values.yaml similarity index 100% rename from pkg/chartutil/testdata/three-level-dependent-chart/umbrella/values.yaml rename to pkg/chart/util/testdata/three-level-dependent-chart/umbrella/values.yaml diff --git a/pkg/chartutil/validate_name.go b/pkg/chart/util/validate_name.go similarity index 99% rename from pkg/chartutil/validate_name.go rename to pkg/chart/util/validate_name.go index 05c090cb6..73be43303 100644 --- a/pkg/chartutil/validate_name.go +++ b/pkg/chart/util/validate_name.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package chartutil +package util import ( "fmt" diff --git a/pkg/chartutil/validate_name_test.go b/pkg/chart/util/validate_name_test.go similarity index 99% rename from pkg/chartutil/validate_name_test.go rename to pkg/chart/util/validate_name_test.go index 09dec9fbb..cfc62a0f7 100644 --- a/pkg/chartutil/validate_name_test.go +++ b/pkg/chart/util/validate_name_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package chartutil +package util import "testing" diff --git a/pkg/chartutil/values.go b/pkg/chart/util/values.go similarity index 99% rename from pkg/chartutil/values.go rename to pkg/chart/util/values.go index 706c44d48..46952c079 100644 --- a/pkg/chartutil/values.go +++ b/pkg/chart/util/values.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package chartutil +package util import ( "encoding/json" diff --git a/pkg/chartutil/values_test.go b/pkg/chart/util/values_test.go similarity index 99% rename from pkg/chartutil/values_test.go rename to pkg/chart/util/values_test.go index 56167c8a1..660b7e4ed 100644 --- a/pkg/chartutil/values_test.go +++ b/pkg/chart/util/values_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package chartutil +package util import ( "bytes" diff --git a/pkg/cli/values/options.go b/pkg/cli/values/options.go index 24c47ecba..2c3706b84 100644 --- a/pkg/cli/values/options.go +++ b/pkg/cli/values/options.go @@ -17,14 +17,16 @@ limitations under the License. package values import ( + "bytes" + "encoding/json" "io" "net/url" "os" "strings" "github.com/pkg/errors" - "sigs.k8s.io/yaml" + "helm.sh/helm/v4/pkg/chart/loader" "helm.sh/helm/v4/pkg/getter" "helm.sh/helm/v4/pkg/strvals" ) @@ -46,24 +48,33 @@ func (opts *Options) MergeValues(p getter.Providers) (map[string]interface{}, er // User specified a values files via -f/--values for _, filePath := range opts.ValueFiles { - currentMap := map[string]interface{}{} - - bytes, err := readFile(filePath, p) + raw, err := readFile(filePath, p) if err != nil { return nil, err } - - if err := yaml.Unmarshal(bytes, ¤tMap); err != nil { + currentMap, err := loader.LoadValues(bytes.NewReader(raw)) + if err != nil { return nil, errors.Wrapf(err, "failed to parse %s", filePath) } // Merge with the previous map - base = mergeMaps(base, currentMap) + base = loader.MergeMaps(base, currentMap) } // User specified a value via --set-json for _, value := range opts.JSONValues { - if err := strvals.ParseJSON(value, base); err != nil { - return nil, errors.Errorf("failed parsing --set-json data %s", value) + trimmedValue := strings.TrimSpace(value) + if len(trimmedValue) > 0 && trimmedValue[0] == '{' { + // If value is JSON object format, parse it as map + var jsonMap map[string]interface{} + if err := json.Unmarshal([]byte(trimmedValue), &jsonMap); err != nil { + return nil, errors.Errorf("failed parsing --set-json data JSON: %s", value) + } + base = loader.MergeMaps(base, jsonMap) + } else { + // Otherwise, parse it as key=value format + if err := strvals.ParseJSON(value, base); err != nil { + return nil, errors.Errorf("failed parsing --set-json data %s", value) + } } } @@ -105,25 +116,6 @@ func (opts *Options) MergeValues(p getter.Providers) (map[string]interface{}, er return base, nil } -func mergeMaps(a, b map[string]interface{}) map[string]interface{} { - out := make(map[string]interface{}, len(a)) - for k, v := range a { - out[k] = v - } - for k, v := range b { - if v, ok := v.(map[string]interface{}); ok { - if bv, ok := out[k]; ok { - if bv, ok := bv.(map[string]interface{}); ok { - out[k] = mergeMaps(bv, v) - continue - } - } - } - out[k] = v - } - return out -} - // readFile load a file from stdin, the local directory, or a remote file with a url. func readFile(filePath string, p getter.Providers) ([]byte, error) { if strings.TrimSpace(filePath) == "-" { @@ -143,5 +135,5 @@ func readFile(filePath string, p getter.Providers) ([]byte, error) { if err != nil { return nil, err } - return data.Bytes(), err + return data.Bytes(), nil } diff --git a/pkg/cli/values/options_test.go b/pkg/cli/values/options_test.go index 9182e3cc8..c3bb0af33 100644 --- a/pkg/cli/values/options_test.go +++ b/pkg/cli/values/options_test.go @@ -23,61 +23,6 @@ import ( "helm.sh/helm/v4/pkg/getter" ) -func TestMergeValues(t *testing.T) { - nestedMap := map[string]interface{}{ - "foo": "bar", - "baz": map[string]string{ - "cool": "stuff", - }, - } - anotherNestedMap := map[string]interface{}{ - "foo": "bar", - "baz": map[string]string{ - "cool": "things", - "awesome": "stuff", - }, - } - flatMap := map[string]interface{}{ - "foo": "bar", - "baz": "stuff", - } - anotherFlatMap := map[string]interface{}{ - "testing": "fun", - } - - testMap := mergeMaps(flatMap, nestedMap) - equal := reflect.DeepEqual(testMap, nestedMap) - if !equal { - t.Errorf("Expected a nested map to overwrite a flat value. Expected: %v, got %v", nestedMap, testMap) - } - - testMap = mergeMaps(nestedMap, flatMap) - equal = reflect.DeepEqual(testMap, flatMap) - if !equal { - t.Errorf("Expected a flat value to overwrite a map. Expected: %v, got %v", flatMap, testMap) - } - - testMap = mergeMaps(nestedMap, anotherNestedMap) - equal = reflect.DeepEqual(testMap, anotherNestedMap) - if !equal { - t.Errorf("Expected a nested map to overwrite another nested map. Expected: %v, got %v", anotherNestedMap, testMap) - } - - testMap = mergeMaps(anotherFlatMap, anotherNestedMap) - expectedMap := map[string]interface{}{ - "testing": "fun", - "foo": "bar", - "baz": map[string]string{ - "cool": "things", - "awesome": "stuff", - }, - } - equal = reflect.DeepEqual(testMap, expectedMap) - if !equal { - t.Errorf("Expected a map with different keys to merge properly with another map. Expected: %v, got %v", expectedMap, testMap) - } -} - func TestReadFile(t *testing.T) { var p getter.Providers filePath := "%a.txt" @@ -86,3 +31,97 @@ func TestReadFile(t *testing.T) { t.Errorf("Expected error when has special strings") } } + +func TestMergeValues(t *testing.T) { + tests := []struct { + name string + opts Options + expected map[string]interface{} + wantErr bool + }{ + { + name: "set-json object", + opts: Options{ + JSONValues: []string{`{"foo": {"bar": "baz"}}`}, + }, + expected: map[string]interface{}{ + "foo": map[string]interface{}{ + "bar": "baz", + }, + }, + }, + { + name: "set-json key=value", + opts: Options{ + JSONValues: []string{"foo.bar=[1,2,3]"}, + }, + expected: map[string]interface{}{ + "foo": map[string]interface{}{ + "bar": []interface{}{1.0, 2.0, 3.0}, + }, + }, + }, + { + name: "set regular value", + opts: Options{ + Values: []string{"foo=bar"}, + }, + expected: map[string]interface{}{ + "foo": "bar", + }, + }, + { + name: "set string value", + opts: Options{ + StringValues: []string{"foo=123"}, + }, + expected: map[string]interface{}{ + "foo": "123", + }, + }, + { + name: "set literal value", + opts: Options{ + LiteralValues: []string{"foo=true"}, + }, + expected: map[string]interface{}{ + "foo": "true", + }, + }, + { + name: "multiple options", + opts: Options{ + Values: []string{"a=foo"}, + StringValues: []string{"b=bar"}, + JSONValues: []string{`{"c": "foo1"}`}, + LiteralValues: []string{"d=bar1"}, + }, + expected: map[string]interface{}{ + "a": "foo", + "b": "bar", + "c": "foo1", + "d": "bar1", + }, + }, + { + name: "invalid json", + opts: Options{ + JSONValues: []string{`{invalid`}, + }, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.opts.MergeValues(getter.Providers{}) + if (err != nil) != tt.wantErr { + t.Errorf("MergeValues() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !tt.wantErr && !reflect.DeepEqual(got, tt.expected) { + t.Errorf("MergeValues() = %v, want %v", got, tt.expected) + } + }) + } +} diff --git a/pkg/downloader/chart_downloader_test.go b/pkg/downloader/chart_downloader_test.go index 1d28e3c22..26dcc58ff 100644 --- a/pkg/downloader/chart_downloader_test.go +++ b/pkg/downloader/chart_downloader_test.go @@ -175,7 +175,11 @@ func TestIsTar(t *testing.T) { } func TestDownloadTo(t *testing.T) { - srv := repotest.NewTempServerWithCleanupAndBasicAuth(t, "testdata/*.tgz*") + srv := repotest.NewTempServer( + t, + repotest.WithChartSourceGlob("testdata/*.tgz*"), + repotest.WithMiddleware(repotest.BasicAuthMiddleware(t)), + ) defer srv.Stop() if err := srv.CreateIndex(); err != nil { t.Fatal(err) @@ -222,12 +226,11 @@ func TestDownloadTo(t *testing.T) { func TestDownloadTo_TLS(t *testing.T) { // Set up mock server w/ tls enabled - srv, err := repotest.NewTempServerWithCleanup(t, "testdata/*.tgz*") - srv.Stop() - if err != nil { - t.Fatal(err) - } - srv.StartTLS() + srv := repotest.NewTempServer( + t, + repotest.WithChartSourceGlob("testdata/*.tgz*"), + repotest.WithTLSConfig(repotest.MakeTestTLSConfig(t, "../../testdata")), + ) defer srv.Stop() if err := srv.CreateIndex(); err != nil { t.Fatal(err) @@ -249,7 +252,13 @@ func TestDownloadTo_TLS(t *testing.T) { RepositoryConfig: repoConfig, RepositoryCache: repoCache, }), - Options: []getter.Option{}, + Options: []getter.Option{ + getter.WithTLSClientConfig( + "", + "", + filepath.Join("../../testdata/rootca.crt"), + ), + }, } cname := "test/signtest" dest := srv.Root() @@ -278,10 +287,10 @@ func TestDownloadTo_VerifyLater(t *testing.T) { dest := t.TempDir() // Set up a fake repo - srv, err := repotest.NewTempServerWithCleanup(t, "testdata/*.tgz*") - if err != nil { - t.Fatal(err) - } + srv := repotest.NewTempServer( + t, + repotest.WithChartSourceGlob("testdata/*.tgz*"), + ) defer srv.Stop() if err := srv.LinkIndices(); err != nil { t.Fatal(err) diff --git a/pkg/downloader/manager.go b/pkg/downloader/manager.go index f10cbfb8d..e5dac8b10 100644 --- a/pkg/downloader/manager.go +++ b/pkg/downloader/manager.go @@ -38,7 +38,7 @@ import ( "helm.sh/helm/v4/internal/urlutil" "helm.sh/helm/v4/pkg/chart" "helm.sh/helm/v4/pkg/chart/loader" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/getter" "helm.sh/helm/v4/pkg/helmpath" "helm.sh/helm/v4/pkg/registry" @@ -659,10 +659,28 @@ func (m *Manager) UpdateRepositories() error { return nil } +// Filter out duplicate repos by URL, including those with trailing slashes. +func dedupeRepos(repos []*repo.Entry) []*repo.Entry { + seen := make(map[string]*repo.Entry) + for _, r := range repos { + // Normalize URL by removing trailing slashes. + seenURL := strings.TrimSuffix(r.URL, "/") + seen[seenURL] = r + } + var unique []*repo.Entry + for _, r := range seen { + unique = append(unique, r) + } + return unique +} + func (m *Manager) parallelRepoUpdate(repos []*repo.Entry) error { var wg sync.WaitGroup - for _, c := range repos { + + localRepos := dedupeRepos(repos) + + for _, c := range localRepos { r, err := repo.NewChartRepository(c, m.Getters) if err != nil { return err diff --git a/pkg/downloader/manager_test.go b/pkg/downloader/manager_test.go index ae4e693ba..8731fb003 100644 --- a/pkg/downloader/manager_test.go +++ b/pkg/downloader/manager_test.go @@ -22,10 +22,13 @@ import ( "reflect" "testing" + "github.com/stretchr/testify/assert" + "helm.sh/helm/v4/pkg/chart" "helm.sh/helm/v4/pkg/chart/loader" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/getter" + "helm.sh/helm/v4/pkg/repo" "helm.sh/helm/v4/pkg/repo/repotest" ) @@ -292,10 +295,10 @@ version: 0.1.0` func TestUpdateBeforeBuild(t *testing.T) { // Set up a fake repo - srv, err := repotest.NewTempServerWithCleanup(t, "testdata/*.tgz*") - if err != nil { - t.Fatal(err) - } + srv := repotest.NewTempServer( + t, + repotest.WithChartSourceGlob("testdata/*.tgz*"), + ) defer srv.Stop() if err := srv.LinkIndices(); err != nil { t.Fatal(err) @@ -347,13 +350,11 @@ func TestUpdateBeforeBuild(t *testing.T) { } // Update before Build. see issue: https://github.com/helm/helm/issues/7101 - err = m.Update() - if err != nil { + if err := m.Update(); err != nil { t.Fatal(err) } - err = m.Build() - if err != nil { + if err := m.Build(); err != nil { t.Fatal(err) } } @@ -363,10 +364,10 @@ func TestUpdateBeforeBuild(t *testing.T) { // to be fetched. func TestUpdateWithNoRepo(t *testing.T) { // Set up a fake repo - srv, err := repotest.NewTempServerWithCleanup(t, "testdata/*.tgz*") - if err != nil { - t.Fatal(err) - } + srv := repotest.NewTempServer( + t, + repotest.WithChartSourceGlob("testdata/*.tgz*"), + ) defer srv.Stop() if err := srv.LinkIndices(); err != nil { t.Fatal(err) @@ -422,8 +423,7 @@ func TestUpdateWithNoRepo(t *testing.T) { } // Test the update - err = m.Update() - if err != nil { + if err := m.Update(); err != nil { t.Fatal(err) } } @@ -436,10 +436,10 @@ func TestUpdateWithNoRepo(t *testing.T) { // If each of these main fields (name, version, repository) is not supplied by dep param, default value will be used. func checkBuildWithOptionalFields(t *testing.T, chartName string, dep chart.Dependency) { // Set up a fake repo - srv, err := repotest.NewTempServerWithCleanup(t, "testdata/*.tgz*") - if err != nil { - t.Fatal(err) - } + srv := repotest.NewTempServer( + t, + repotest.WithChartSourceGlob("testdata/*.tgz*"), + ) defer srv.Stop() if err := srv.LinkIndices(); err != nil { t.Fatal(err) @@ -487,14 +487,12 @@ func checkBuildWithOptionalFields(t *testing.T, chartName string, dep chart.Depe } // First build will update dependencies and create Chart.lock file. - err = m.Build() - if err != nil { + if err := m.Build(); err != nil { t.Fatal(err) } // Second build should be passed. See PR #6655. - err = m.Build() - if err != nil { + if err := m.Build(); err != nil { t.Fatal(err) } } @@ -598,3 +596,71 @@ func TestKey(t *testing.T) { } } } + +// Test dedupeRepos tests that the dedupeRepos function correctly deduplicates +func TestDedupeRepos(t *testing.T) { + tests := []struct { + name string + repos []*repo.Entry + want []*repo.Entry + }{ + { + name: "no duplicates", + repos: []*repo.Entry{ + { + URL: "https://example.com/charts", + }, + { + URL: "https://example.com/charts2", + }, + }, + want: []*repo.Entry{ + { + URL: "https://example.com/charts", + }, + { + URL: "https://example.com/charts2", + }, + }, + }, + { + name: "duplicates", + repos: []*repo.Entry{ + { + URL: "https://example.com/charts", + }, + { + URL: "https://example.com/charts", + }, + }, + want: []*repo.Entry{ + { + URL: "https://example.com/charts", + }, + }, + }, + { + name: "duplicates with trailing slash", + repos: []*repo.Entry{ + { + URL: "https://example.com/charts", + }, + { + URL: "https://example.com/charts/", + }, + }, + want: []*repo.Entry{ + { + // the last one wins + URL: "https://example.com/charts/", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := dedupeRepos(tt.repos) + assert.ElementsMatch(t, tt.want, got) + }) + } +} diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index 8f69505f6..0229b1833 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -30,7 +30,7 @@ import ( "k8s.io/client-go/rest" "helm.sh/helm/v4/pkg/chart" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" ) // Engine is an implementation of the Helm rendering implementation for templates. diff --git a/pkg/engine/engine_test.go b/pkg/engine/engine_test.go index f6a96bd6a..4c28c2965 100644 --- a/pkg/engine/engine_test.go +++ b/pkg/engine/engine_test.go @@ -31,7 +31,7 @@ import ( "k8s.io/client-go/dynamic/fake" "helm.sh/helm/v4/pkg/chart" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" ) func TestSortTemplates(t *testing.T) { diff --git a/pkg/kube/client.go b/pkg/kube/client.go index ff062a172..d174614db 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -76,8 +76,8 @@ type Client struct { // Namespace allows to bypass the kubeconfig file for the choice of the namespace Namespace string - kubeClient *kubernetes.Clientset Waiter + kubeClient kubernetes.Interface } type WaitStrategy string @@ -174,7 +174,7 @@ func New(getter genericclioptions.RESTClientGetter) (*Client, error) { var nopLogger = func(_ string, _ ...interface{}) {} // getKubeClient get or create a new KubernetesClientSet -func (c *Client) getKubeClient() (*kubernetes.Clientset, error) { +func (c *Client) getKubeClient() (kubernetes.Interface, error) { var err error if c.kubeClient == nil { c.kubeClient, err = c.Factory.KubernetesClientSet() @@ -194,7 +194,7 @@ func (c *Client) IsReachable() error { if err != nil { return errors.Wrap(err, "Kubernetes cluster unreachable") } - if _, err := client.ServerVersion(); err != nil { + if _, err := client.Discovery().ServerVersion(); err != nil { return errors.Wrap(err, "Kubernetes cluster unreachable") } return nil @@ -687,6 +687,151 @@ func updateResource(c *Client, target *resource.Info, currentObj runtime.Object, return nil } +// func (c *Client) watchUntilReady(timeout time.Duration, info *resource.Info) error { +// kind := info.Mapping.GroupVersionKind.Kind +// switch kind { +// case "Job", "Pod": +// default: +// return nil +// } + +// c.Log("Watching for changes to %s %s with timeout of %v", kind, info.Name, timeout) + +// // Use a selector on the name of the resource. This should be unique for the +// // given version and kind +// selector, err := fields.ParseSelector(fmt.Sprintf("metadata.name=%s", info.Name)) +// if err != nil { +// return err +// } +// lw := cachetools.NewListWatchFromClient(info.Client, info.Mapping.Resource.Resource, info.Namespace, selector) + +// // What we watch for depends on the Kind. +// // - For a Job, we watch for completion. +// // - For all else, we watch until Ready. +// // In the future, we might want to add some special logic for types +// // like Ingress, Volume, etc. + +// ctx, cancel := watchtools.ContextWithOptionalTimeout(context.Background(), timeout) +// defer cancel() +// _, err = watchtools.UntilWithSync(ctx, lw, &unstructured.Unstructured{}, nil, func(e watch.Event) (bool, error) { +// // Make sure the incoming object is versioned as we use unstructured +// // objects when we build manifests +// obj := convertWithMapper(e.Object, info.Mapping) +// switch e.Type { +// case watch.Added, watch.Modified: +// // For things like a secret or a config map, this is the best indicator +// // we get. We care mostly about jobs, where what we want to see is +// // the status go into a good state. For other types, like ReplicaSet +// // we don't really do anything to support these as hooks. +// c.Log("Add/Modify event for %s: %v", info.Name, e.Type) +// switch kind { +// case "Job": +// return c.waitForJob(obj, info.Name) +// case "Pod": +// return c.waitForPodSuccess(obj, info.Name) +// } +// return true, nil +// case watch.Deleted: +// c.Log("Deleted event for %s", info.Name) +// return true, nil +// case watch.Error: +// // Handle error and return with an error. +// c.Log("Error event for %s", info.Name) +// return true, errors.Errorf("failed to deploy %s", info.Name) +// default: +// return false, nil +// } +// }) +// return err +// } + +// // waitForJob is a helper that waits for a job to complete. +// // +// // This operates on an event returned from a watcher. +// func (c *Client) waitForJob(obj runtime.Object, name string) (bool, error) { +// o, ok := obj.(*batch.Job) +// if !ok { +// return true, errors.Errorf("expected %s to be a *batch.Job, got %T", name, obj) +// } + +// for _, c := range o.Status.Conditions { +// if c.Type == batch.JobComplete && c.Status == "True" { +// return true, nil +// } else if c.Type == batch.JobFailed && c.Status == "True" { +// return true, errors.Errorf("job %s failed: %s", name, c.Reason) +// } +// } + +// c.Log("%s: Jobs active: %d, jobs failed: %d, jobs succeeded: %d", name, o.Status.Active, o.Status.Failed, o.Status.Succeeded) +// return false, nil +// } + +// // waitForPodSuccess is a helper that waits for a pod to complete. +// // +// // This operates on an event returned from a watcher. +// func (c *Client) waitForPodSuccess(obj runtime.Object, name string) (bool, error) { +// o, ok := obj.(*v1.Pod) +// if !ok { +// return true, errors.Errorf("expected %s to be a *v1.Pod, got %T", name, obj) +// } + +// switch o.Status.Phase { +// case v1.PodSucceeded: +// c.Log("Pod %s succeeded", o.Name) +// return true, nil +// case v1.PodFailed: +// return true, errors.Errorf("pod %s failed", o.Name) +// case v1.PodPending: +// c.Log("Pod %s pending", o.Name) +// case v1.PodRunning: +// c.Log("Pod %s running", o.Name) +// } + +// return false, nil +// } + +// // GetPodList uses the kubernetes interface to get the list of pods filtered by listOptions +// func (c *Client) GetPodList(namespace string, listOptions metav1.ListOptions) (*v1.PodList, error) { +// podList, err := c.kubeClient.CoreV1().Pods(namespace).List(context.Background(), listOptions) +// if err != nil { +// return nil, fmt.Errorf("failed to get pod list with options: %+v with error: %v", listOptions, err) +// } +// return podList, nil +// } + +// // OutputContainerLogsForPodList is a helper that outputs logs for a list of pods +// func (c *Client) OutputContainerLogsForPodList(podList *v1.PodList, namespace string, writerFunc func(namespace, pod, container string) io.Writer) error { +// for _, pod := range podList.Items { +// for _, container := range pod.Spec.Containers { +// options := &v1.PodLogOptions{ +// Container: container.Name, +// } +// request := c.kubeClient.CoreV1().Pods(namespace).GetLogs(pod.Name, options) +// err2 := copyRequestStreamToWriter(request, pod.Name, container.Name, writerFunc(namespace, pod.Name, container.Name)) +// if err2 != nil { +// return err2 +// } +// } +// } +// return nil +// } + +// func copyRequestStreamToWriter(request *rest.Request, podName, containerName string, writer io.Writer) error { +// readCloser, err := request.Stream(context.Background()) +// if err != nil { +// return errors.Errorf("Failed to stream pod logs for pod: %s, container: %s", podName, containerName) +// } +// defer readCloser.Close() +// _, err = io.Copy(writer, readCloser) +// if err != nil { +// return errors.Errorf("Failed to copy IO from logs for pod: %s, container: %s", podName, containerName) +// } +// if err != nil { +// return errors.Errorf("Failed to close reader for pod: %s, container: %s", podName, containerName) +// } +// return nil +// } + // scrubValidationError removes kubectl info from the message. func scrubValidationError(err error) error { if err == nil { diff --git a/pkg/kube/client_test.go b/pkg/kube/client_test.go index a5ad2b1eb..527f28a72 100644 --- a/pkg/kube/client_test.go +++ b/pkg/kube/client_test.go @@ -24,10 +24,13 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/cli-runtime/pkg/resource" + k8sfake "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest/fake" cmdtesting "k8s.io/kubectl/pkg/cmd/testing" @@ -703,6 +706,39 @@ func TestReal(t *testing.T) { } } +func TestGetPodList(t *testing.T) { + + namespace := "some-namespace" + names := []string{"dave", "jimmy"} + var responsePodList v1.PodList + for _, name := range names { + responsePodList.Items = append(responsePodList.Items, newPodWithStatus(name, v1.PodStatus{}, namespace)) + } + + kubeClient := k8sfake.NewSimpleClientset(&responsePodList) + c := Client{Namespace: namespace, kubeClient: kubeClient} + + podList, err := c.GetPodList(namespace, metav1.ListOptions{}) + clientAssertions := assert.New(t) + clientAssertions.NoError(err) + clientAssertions.Equal(&responsePodList, podList) + +} + +func TestOutputContainerLogsForPodList(t *testing.T) { + namespace := "some-namespace" + somePodList := newPodList("jimmy", "three", "structs") + + kubeClient := k8sfake.NewSimpleClientset(&somePodList) + c := Client{Namespace: namespace, kubeClient: kubeClient} + outBuffer := &bytes.Buffer{} + outBufferFunc := func(_, _, _ string) io.Writer { return outBuffer } + err := c.OutputContainerLogsForPodList(&somePodList, namespace, outBufferFunc) + clientAssertions := assert.New(t) + clientAssertions.NoError(err) + clientAssertions.Equal("fake logsfake logsfake logs", outBuffer.String()) +} + const testServiceManifest = ` kind: Service apiVersion: v1 diff --git a/pkg/kube/config.go b/pkg/kube/config.go deleted file mode 100644 index 0ef94453f..000000000 --- a/pkg/kube/config.go +++ /dev/null @@ -1,30 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package kube // import "helm.sh/helm/v4/pkg/kube" - -import "k8s.io/cli-runtime/pkg/genericclioptions" - -// GetConfig returns a Kubernetes client config. -// -// Deprecated -func GetConfig(kubeconfig, context, namespace string) *genericclioptions.ConfigFlags { - cf := genericclioptions.NewConfigFlags(true) - cf.Namespace = &namespace - cf.Context = &context - cf.KubeConfig = &kubeconfig - return cf -} diff --git a/pkg/kube/fake/printer.go b/pkg/kube/fake/printer.go index 82649b202..fa25a04b3 100644 --- a/pkg/kube/fake/printer.go +++ b/pkg/kube/fake/printer.go @@ -17,10 +17,12 @@ limitations under the License. package fake import ( + "fmt" "io" "strings" "time" + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/cli-runtime/pkg/resource" @@ -31,7 +33,8 @@ import ( // PrintingKubeClient implements KubeClient, but simply prints the reader to // the given output. type PrintingKubeClient struct { - Out io.Writer + Out io.Writer + LogOutput io.Writer } // IsReachable checks if the cluster is reachable @@ -110,6 +113,22 @@ func (p *PrintingKubeClient) BuildTable(_ io.Reader, _ bool) (kube.ResourceList, return []*resource.Info{}, nil } +// WaitAndGetCompletedPodPhase implements KubeClient WaitAndGetCompletedPodPhase. +func (p *PrintingKubeClient) WaitAndGetCompletedPodPhase(_ string, _ time.Duration) (v1.PodPhase, error) { + return v1.PodSucceeded, nil +} + +// GetPodList implements KubeClient GetPodList. +func (p *PrintingKubeClient) GetPodList(_ string, _ metav1.ListOptions) (*v1.PodList, error) { + return &v1.PodList{}, nil +} + +// OutputContainerLogsForPodList implements KubeClient OutputContainerLogsForPodList. +func (p *PrintingKubeClient) OutputContainerLogsForPodList(_ *v1.PodList, someNamespace string, _ func(namespace, pod, container string) io.Writer) error { + _, err := io.Copy(p.LogOutput, strings.NewReader(fmt.Sprintf("attempted to output logs for namespace: %s", someNamespace))) + return err +} + // DeleteWithPropagationPolicy implements KubeClient delete. // // It only prints out the content to be deleted. diff --git a/pkg/kube/interface.go b/pkg/kube/interface.go index fc74a9833..64d954853 100644 --- a/pkg/kube/interface.go +++ b/pkg/kube/interface.go @@ -20,6 +20,7 @@ import ( "io" "time" + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -75,11 +76,22 @@ type Waiter interface { WatchUntilReady(resources ResourceList, timeout time.Duration) error } +// InterfaceLogs was introduced to avoid breaking backwards compatibility for Interface implementers. +// +// TODO Helm 4: Remove InterfaceLogs and integrate its method(s) into the Interface. +type InterfaceLogs interface { + // GetPodList list all pods that match the specified listOptions + GetPodList(namespace string, listOptions metav1.ListOptions) (*v1.PodList, error) + + // OutputContainerLogsForPodList output the logs for a pod list + OutputContainerLogsForPodList(podList *v1.PodList, namespace string, writerFunc func(namespace, pod, container string) io.Writer) error +} + // InterfaceDeletionPropagation is introduced to avoid breaking backwards compatibility for Interface implementers. // // TODO Helm 4: Remove InterfaceDeletionPropagation and integrate its method(s) into the Interface. type InterfaceDeletionPropagation interface { - // Delete destroys one or more resources. The deletion propagation is handled as per the given deletion propagation value. + // DeleteWithPropagationPolicy destroys one or more resources. The deletion propagation is handled as per the given deletion propagation value. DeleteWithPropagationPolicy(resources ResourceList, policy metav1.DeletionPropagation) (*Result, []error) } diff --git a/pkg/kube/roundtripper.go b/pkg/kube/roundtripper.go index fdb103529..52cb5bad2 100644 --- a/pkg/kube/roundtripper.go +++ b/pkg/kube/roundtripper.go @@ -49,7 +49,7 @@ func (rt *RetryingRoundTripper) roundTrip(req *http.Request, retry int, prevResp b, err := io.ReadAll(resp.Body) resp.Body.Close() if err != nil { - return resp, rtErr + return resp, err } var ke kubernetesError @@ -58,10 +58,10 @@ func (rt *RetryingRoundTripper) roundTrip(req *http.Request, retry int, prevResp r.Seek(0, io.SeekStart) resp.Body = io.NopCloser(r) if err != nil { - return resp, rtErr + return resp, err } if ke.Code < 500 { - return resp, rtErr + return resp, nil } // Matches messages like "etcdserver: leader changed" if strings.HasSuffix(ke.Message, "etcdserver: leader changed") { @@ -71,7 +71,7 @@ func (rt *RetryingRoundTripper) roundTrip(req *http.Request, retry int, prevResp if strings.HasSuffix(ke.Message, "raft proposal dropped") { return rt.roundTrip(req, retry-1, resp) } - return resp, rtErr + return resp, nil } type kubernetesError struct { diff --git a/pkg/lint/lint.go b/pkg/lint/lint.go index 70a49ee90..b53400c87 100644 --- a/pkg/lint/lint.go +++ b/pkg/lint/lint.go @@ -19,7 +19,7 @@ package lint // import "helm.sh/helm/v4/pkg/lint" import ( "path/filepath" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/lint/rules" "helm.sh/helm/v4/pkg/lint/support" ) diff --git a/pkg/lint/lint_test.go b/pkg/lint/lint_test.go index 9246b64f2..bba024d59 100644 --- a/pkg/lint/lint_test.go +++ b/pkg/lint/lint_test.go @@ -21,7 +21,7 @@ import ( "testing" "time" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/lint/support" ) diff --git a/pkg/lint/rules/chartfile.go b/pkg/lint/rules/chartfile.go index 2e718067b..a1e08b58d 100644 --- a/pkg/lint/rules/chartfile.go +++ b/pkg/lint/rules/chartfile.go @@ -27,7 +27,7 @@ import ( "sigs.k8s.io/yaml" "helm.sh/helm/v4/pkg/chart" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/lint/support" ) diff --git a/pkg/lint/rules/chartfile_test.go b/pkg/lint/rules/chartfile_test.go index eb9b19cb7..6d709790a 100644 --- a/pkg/lint/rules/chartfile_test.go +++ b/pkg/lint/rules/chartfile_test.go @@ -25,7 +25,7 @@ import ( "github.com/pkg/errors" "helm.sh/helm/v4/pkg/chart" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/lint/support" ) diff --git a/pkg/lint/rules/dependencies_test.go b/pkg/lint/rules/dependencies_test.go index e167b4479..e946b1c01 100644 --- a/pkg/lint/rules/dependencies_test.go +++ b/pkg/lint/rules/dependencies_test.go @@ -20,7 +20,7 @@ import ( "testing" "helm.sh/helm/v4/pkg/chart" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/lint/support" ) diff --git a/pkg/lint/rules/deprecations.go b/pkg/lint/rules/deprecations.go index 6eae06802..0a8e642c9 100644 --- a/pkg/lint/rules/deprecations.go +++ b/pkg/lint/rules/deprecations.go @@ -25,7 +25,7 @@ import ( "k8s.io/apiserver/pkg/endpoints/deprecation" kscheme "k8s.io/client-go/kubernetes/scheme" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" ) var ( diff --git a/pkg/lint/rules/template.go b/pkg/lint/rules/template.go index a50485a50..ec434aba5 100644 --- a/pkg/lint/rules/template.go +++ b/pkg/lint/rules/template.go @@ -34,7 +34,7 @@ import ( "k8s.io/apimachinery/pkg/util/yaml" "helm.sh/helm/v4/pkg/chart/loader" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/engine" "helm.sh/helm/v4/pkg/lint/support" ) diff --git a/pkg/lint/rules/template_test.go b/pkg/lint/rules/template_test.go index 9e6dc4004..d3185d8c8 100644 --- a/pkg/lint/rules/template_test.go +++ b/pkg/lint/rules/template_test.go @@ -24,7 +24,7 @@ import ( "testing" "helm.sh/helm/v4/pkg/chart" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/lint/support" ) diff --git a/pkg/lint/rules/values.go b/pkg/lint/rules/values.go index 82278522d..f430178c3 100644 --- a/pkg/lint/rules/values.go +++ b/pkg/lint/rules/values.go @@ -22,7 +22,7 @@ import ( "github.com/pkg/errors" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/lint/support" ) diff --git a/pkg/plugin/installer/vcs_installer.go b/pkg/plugin/installer/vcs_installer.go index 8153550b2..3967e46cd 100644 --- a/pkg/plugin/installer/vcs_installer.go +++ b/pkg/plugin/installer/vcs_installer.go @@ -63,7 +63,7 @@ func NewVCSInstaller(source, version string) (*VCSInstaller, error) { Version: version, base: newBase(source), } - return i, err + return i, nil } // Install clones a remote repository and installs into the plugin directory. diff --git a/pkg/postrender/exec.go b/pkg/postrender/exec.go index 167e737d6..84357c656 100644 --- a/pkg/postrender/exec.go +++ b/pkg/postrender/exec.go @@ -64,6 +64,12 @@ func (p *execRender) Run(renderedManifests *bytes.Buffer) (*bytes.Buffer, error) return nil, errors.Wrapf(err, "error while running command %s. error output:\n%s", p.binaryPath, stderr.String()) } + // If the binary returned almost nothing, it's likely that it didn't + // successfully render anything + if len(bytes.TrimSpace(postRendered.Bytes())) == 0 { + return nil, errors.Errorf("post-renderer %q produced empty output", p.binaryPath) + } + return postRendered, nil } diff --git a/pkg/postrender/exec_test.go b/pkg/postrender/exec_test.go index 19a6ec6c4..2b091cc12 100644 --- a/pkg/postrender/exec_test.go +++ b/pkg/postrender/exec_test.go @@ -121,6 +121,21 @@ func TestExecRun(t *testing.T) { is.Contains(output.String(), "BARTEST") } +func TestExecRunWithNoOutput(t *testing.T) { + if runtime.GOOS == "windows" { + // the actual Run test uses a basic sed example, so skip this test on windows + t.Skip("skipping on windows") + } + is := assert.New(t) + testpath := setupTestingScript(t) + + renderer, err := NewExec(testpath) + require.NoError(t, err) + + _, err = renderer.Run(bytes.NewBufferString("")) + is.Error(err) +} + func TestNewExecWithOneArgsRun(t *testing.T) { if runtime.GOOS == "windows" { // the actual Run test uses a basic sed example, so skip this test on windows diff --git a/pkg/provenance/sign_test.go b/pkg/provenance/sign_test.go index bf6848368..69a6dad5b 100644 --- a/pkg/provenance/sign_test.go +++ b/pkg/provenance/sign_test.go @@ -34,7 +34,7 @@ const ( // phrase. Use `gpg --export-secret-keys helm-test` to export the secret. testKeyfile = "testdata/helm-test-key.secret" - // testPasswordKeyFile is a keyfile with a password. + // testPasswordKeyfile is a keyfile with a password. testPasswordKeyfile = "testdata/helm-password-key.secret" // testPubfile is the public key file. diff --git a/pkg/release/hook.go b/pkg/release/hook.go index e2cdc0eb8..5ff61fdaa 100644 --- a/pkg/release/hook.go +++ b/pkg/release/hook.go @@ -50,6 +50,17 @@ const ( func (x HookDeletePolicy) String() string { return string(x) } +// HookOutputLogPolicy specifies the hook output log policy +type HookOutputLogPolicy string + +// Hook output log policy types +const ( + HookOutputOnSucceeded HookOutputLogPolicy = "hook-succeeded" + HookOutputOnFailed HookOutputLogPolicy = "hook-failed" +) + +func (x HookOutputLogPolicy) String() string { return string(x) } + // HookAnnotation is the label name for a hook const HookAnnotation = "helm.sh/hook" @@ -59,6 +70,9 @@ const HookWeightAnnotation = "helm.sh/hook-weight" // HookDeleteAnnotation is the label name for the delete policy for a hook const HookDeleteAnnotation = "helm.sh/hook-delete-policy" +// HookOutputLogAnnotation is the label name for the output log policy for a hook +const HookOutputLogAnnotation = "helm.sh/hook-output-log-policy" + // Hook defines a hook object. type Hook struct { Name string `json:"name,omitempty"` @@ -76,6 +90,8 @@ type Hook struct { Weight int `json:"weight,omitempty"` // DeletePolicies are the policies that indicate when to delete the hook DeletePolicies []HookDeletePolicy `json:"delete_policies,omitempty"` + // OutputLogPolicies defines whether we should copy hook logs back to main process + OutputLogPolicies []HookOutputLogPolicy `json:"output_log_policies,omitempty"` } // A HookExecution records the result for the last execution of a hook for a given release. diff --git a/pkg/releaseutil/filter.go b/pkg/release/util/filter.go similarity index 96% rename from pkg/releaseutil/filter.go rename to pkg/release/util/filter.go index d600d43e8..e56752f86 100644 --- a/pkg/releaseutil/filter.go +++ b/pkg/release/util/filter.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package releaseutil // import "helm.sh/helm/v4/pkg/releaseutil" +package util // import "helm.sh/helm/v4/pkg/release/util" import rspb "helm.sh/helm/v4/pkg/release" diff --git a/pkg/releaseutil/filter_test.go b/pkg/release/util/filter_test.go similarity index 96% rename from pkg/releaseutil/filter_test.go rename to pkg/release/util/filter_test.go index 9fc5ce9b1..2037ef157 100644 --- a/pkg/releaseutil/filter_test.go +++ b/pkg/release/util/filter_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package releaseutil // import "helm.sh/helm/v4/pkg/releaseutil" +package util // import "helm.sh/helm/v4/pkg/release/util" import ( "testing" diff --git a/pkg/releaseutil/kind_sorter.go b/pkg/release/util/kind_sorter.go similarity index 99% rename from pkg/releaseutil/kind_sorter.go rename to pkg/release/util/kind_sorter.go index ec51d50d8..130b2c831 100644 --- a/pkg/releaseutil/kind_sorter.go +++ b/pkg/release/util/kind_sorter.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package releaseutil +package util import ( "sort" diff --git a/pkg/releaseutil/kind_sorter_test.go b/pkg/release/util/kind_sorter_test.go similarity index 99% rename from pkg/releaseutil/kind_sorter_test.go rename to pkg/release/util/kind_sorter_test.go index f7745d64d..cd40fe459 100644 --- a/pkg/releaseutil/kind_sorter_test.go +++ b/pkg/release/util/kind_sorter_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package releaseutil +package util import ( "bytes" diff --git a/pkg/releaseutil/manifest.go b/pkg/release/util/manifest.go similarity index 99% rename from pkg/releaseutil/manifest.go rename to pkg/release/util/manifest.go index 0b04a4599..9a87949f8 100644 --- a/pkg/releaseutil/manifest.go +++ b/pkg/release/util/manifest.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package releaseutil +package util import ( "fmt" diff --git a/pkg/releaseutil/manifest_sorter.go b/pkg/release/util/manifest_sorter.go similarity index 88% rename from pkg/releaseutil/manifest_sorter.go rename to pkg/release/util/manifest_sorter.go index b2db2ff9f..a598743a6 100644 --- a/pkg/releaseutil/manifest_sorter.go +++ b/pkg/release/util/manifest_sorter.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package releaseutil +package util import ( "log" @@ -26,7 +26,7 @@ import ( "github.com/pkg/errors" "sigs.k8s.io/yaml" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" "helm.sh/helm/v4/pkg/release" ) @@ -128,6 +128,14 @@ func SortManifests(files map[string]string, _ chartutil.VersionSet, ordering Kin // metadata: // annotations: // helm.sh/hook-delete-policy: hook-succeeded +// +// To determine the policy to output logs of the hook (for Pod and Job only), it looks for a YAML structure like this: +// +// kind: Pod +// apiVersion: v1 +// metadata: +// annotations: +// helm.sh/hook-output-log-policy: hook-succeeded,hook-failed func (file *manifestFile) sort(result *result) error { // Go through manifests in order found in file (function `SplitManifests` creates integer-sortable keys) var sortedEntryKeys []string @@ -166,13 +174,14 @@ func (file *manifestFile) sort(result *result) error { hw := calculateHookWeight(entry) h := &release.Hook{ - Name: entry.Metadata.Name, - Kind: entry.Kind, - Path: file.path, - Manifest: m, - Events: []release.HookEvent{}, - Weight: hw, - DeletePolicies: []release.HookDeletePolicy{}, + Name: entry.Metadata.Name, + Kind: entry.Kind, + Path: file.path, + Manifest: m, + Events: []release.HookEvent{}, + Weight: hw, + DeletePolicies: []release.HookDeletePolicy{}, + OutputLogPolicies: []release.HookOutputLogPolicy{}, } isUnknownHook := false @@ -196,6 +205,10 @@ func (file *manifestFile) sort(result *result) error { operateAnnotationValues(entry, release.HookDeleteAnnotation, func(value string) { h.DeletePolicies = append(h.DeletePolicies, release.HookDeletePolicy(value)) }) + + operateAnnotationValues(entry, release.HookOutputLogAnnotation, func(value string) { + h.OutputLogPolicies = append(h.OutputLogPolicies, release.HookOutputLogPolicy(value)) + }) } return nil diff --git a/pkg/releaseutil/manifest_sorter_test.go b/pkg/release/util/manifest_sorter_test.go similarity index 99% rename from pkg/releaseutil/manifest_sorter_test.go rename to pkg/release/util/manifest_sorter_test.go index 3bd196c12..281f24924 100644 --- a/pkg/releaseutil/manifest_sorter_test.go +++ b/pkg/release/util/manifest_sorter_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package releaseutil +package util import ( "reflect" diff --git a/pkg/releaseutil/manifest_test.go b/pkg/release/util/manifest_test.go similarity index 95% rename from pkg/releaseutil/manifest_test.go rename to pkg/release/util/manifest_test.go index 8e05b76dc..cfc19563d 100644 --- a/pkg/releaseutil/manifest_test.go +++ b/pkg/release/util/manifest_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package releaseutil // import "helm.sh/helm/v4/pkg/releaseutil" +package util // import "helm.sh/helm/v4/pkg/release/util" import ( "reflect" diff --git a/pkg/releaseutil/sorter.go b/pkg/release/util/sorter.go similarity index 97% rename from pkg/releaseutil/sorter.go rename to pkg/release/util/sorter.go index a2135d68f..8b1c89aa3 100644 --- a/pkg/releaseutil/sorter.go +++ b/pkg/release/util/sorter.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package releaseutil // import "helm.sh/helm/v4/pkg/releaseutil" +package util // import "helm.sh/helm/v4/pkg/release/util" import ( "sort" diff --git a/pkg/releaseutil/sorter_test.go b/pkg/release/util/sorter_test.go similarity index 97% rename from pkg/releaseutil/sorter_test.go rename to pkg/release/util/sorter_test.go index bef261ee8..6e92eef5c 100644 --- a/pkg/releaseutil/sorter_test.go +++ b/pkg/release/util/sorter_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package releaseutil // import "helm.sh/helm/v4/pkg/releaseutil" +package util // import "helm.sh/helm/v4/pkg/release/util" import ( "testing" diff --git a/pkg/repo/repotest/server.go b/pkg/repo/repotest/server.go index c7b674d04..7b004dbc3 100644 --- a/pkg/repo/repotest/server.go +++ b/pkg/repo/repotest/server.go @@ -17,6 +17,7 @@ package repotest import ( "context" + "crypto/tls" "fmt" "net/http" "net/http/httptest" @@ -33,42 +34,110 @@ import ( "golang.org/x/crypto/bcrypt" "sigs.k8s.io/yaml" - "helm.sh/helm/v4/internal/tlsutil" "helm.sh/helm/v4/pkg/chart" "helm.sh/helm/v4/pkg/chart/loader" - "helm.sh/helm/v4/pkg/chartutil" + chartutil "helm.sh/helm/v4/pkg/chart/util" ociRegistry "helm.sh/helm/v4/pkg/registry" "helm.sh/helm/v4/pkg/repo" ) -// NewTempServerWithCleanup creates a server inside of a temp dir. +func BasicAuthMiddleware(t *testing.T) http.HandlerFunc { + return http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) { + username, password, ok := r.BasicAuth() + if !ok || username != "username" || password != "password" { + t.Errorf("Expected request to use basic auth and for username == 'username' and password == 'password', got '%v', '%s', '%s'", ok, username, password) + } + }) +} + +type ServerOption func(*testing.T, *Server) + +func WithTLSConfig(tlsConfig *tls.Config) ServerOption { + return func(_ *testing.T, server *Server) { + server.tlsConfig = tlsConfig + } +} + +func WithMiddleware(middleware http.HandlerFunc) ServerOption { + return func(_ *testing.T, server *Server) { + server.middleware = middleware + } +} + +func WithChartSourceGlob(glob string) ServerOption { + return func(_ *testing.T, server *Server) { + server.chartSourceGlob = glob + } +} + +// Server is an implementation of a repository server for testing. +type Server struct { + docroot string + srv *httptest.Server + middleware http.HandlerFunc + tlsConfig *tls.Config + chartSourceGlob string +} + +// NewTempServer creates a server inside of a temp dir. // // If the passed in string is not "", it will be treated as a shell glob, and files // will be copied from that path to the server's docroot. // -// The caller is responsible for stopping the server. +// The server is started automatically. The caller is responsible for stopping +// the server. +// // The temp dir will be removed by testing package automatically when test finished. -func NewTempServerWithCleanup(t *testing.T, glob string) (*Server, error) { - srv, err := NewTempServer(glob) +func NewTempServer(t *testing.T, options ...ServerOption) *Server { + + docrootTempDir, err := os.MkdirTemp("", "helm-repotest-") + if err != nil { + t.Fatal(err) + } + + srv := newServer(t, docrootTempDir, options...) + t.Cleanup(func() { os.RemoveAll(srv.docroot) }) - return srv, err + + if srv.chartSourceGlob != "" { + if _, err := srv.CopyCharts(srv.chartSourceGlob); err != nil { + t.Fatal(err) + } + } + + return srv } -// Set up a fake repo with basic auth enabled -func NewTempServerWithCleanupAndBasicAuth(t *testing.T, glob string) *Server { - srv, err := NewTempServerWithCleanup(t, glob) - srv.Stop() +// Create the server, but don't yet start it +func newServer(t *testing.T, docroot string, options ...ServerOption) *Server { + absdocroot, err := filepath.Abs(docroot) if err != nil { t.Fatal(err) } - srv.WithMiddleware(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) { - username, password, ok := r.BasicAuth() - if !ok || username != "username" || password != "password" { - t.Errorf("Expected request to use basic auth and for username == 'username' and password == 'password', got '%v', '%s', '%s'", ok, username, password) + + s := &Server{ + docroot: absdocroot, + } + + for _, option := range options { + option(t, s) + } + + s.srv = httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if s.middleware != nil { + s.middleware.ServeHTTP(w, r) } + http.FileServer(http.Dir(s.Root())).ServeHTTP(w, r) })) - srv.Start() - return srv + + s.start() + + // Add the testing repository as the only repo. Server must be started for the server's URL to be valid + if err := setTestingRepository(s.URL(), filepath.Join(s.docroot, "repositories.yaml")); err != nil { + t.Fatal(err) + } + + return s } type OCIServer struct { @@ -239,69 +308,6 @@ func (srv *OCIServer) Run(t *testing.T, opts ...OCIServerOpt) { result.Chart.Digest, result.Chart.Size) } -// NewTempServer creates a server inside of a temp dir. -// -// If the passed in string is not "", it will be treated as a shell glob, and files -// will be copied from that path to the server's docroot. -// -// The caller is responsible for destroying the temp directory as well as stopping -// the server. -// -// Deprecated: use NewTempServerWithCleanup -func NewTempServer(glob string) (*Server, error) { - tdir, err := os.MkdirTemp("", "helm-repotest-") - if err != nil { - return nil, err - } - srv := NewServer(tdir) - - if glob != "" { - if _, err := srv.CopyCharts(glob); err != nil { - srv.Stop() - return srv, err - } - } - - return srv, nil -} - -// NewServer creates a repository server for testing. -// -// docroot should be a temp dir managed by the caller. -// -// This will start the server, serving files off of the docroot. -// -// Use CopyCharts to move charts into the repository and then index them -// for service. -func NewServer(docroot string) *Server { - root, err := filepath.Abs(docroot) - if err != nil { - panic(err) - } - srv := &Server{ - docroot: root, - } - srv.Start() - // Add the testing repository as the only repo. - if err := setTestingRepository(srv.URL(), filepath.Join(root, "repositories.yaml")); err != nil { - panic(err) - } - return srv -} - -// Server is an implementation of a repository server for testing. -type Server struct { - docroot string - srv *httptest.Server - middleware http.HandlerFunc -} - -// WithMiddleware injects middleware in front of the server. This can be used to inject -// additional functionality like layering in an authentication frontend. -func (s *Server) WithMiddleware(middleware http.HandlerFunc) { - s.middleware = middleware -} - // Root gets the docroot for the server. func (s *Server) Root() string { return s.docroot @@ -348,50 +354,12 @@ func (s *Server) CreateIndex() error { return os.WriteFile(ifile, d, 0644) } -func (s *Server) Start() { - s.srv = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if s.middleware != nil { - s.middleware.ServeHTTP(w, r) - } - http.FileServer(http.Dir(s.docroot)).ServeHTTP(w, r) - })) -} - -func (s *Server) StartTLS() { - cd := "../../testdata" - ca, pub, priv := filepath.Join(cd, "rootca.crt"), filepath.Join(cd, "crt.pem"), filepath.Join(cd, "key.pem") - insecure := false - - s.srv = httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if s.middleware != nil { - s.middleware.ServeHTTP(w, r) - } - http.FileServer(http.Dir(s.Root())).ServeHTTP(w, r) - })) - tlsConf, err := tlsutil.NewTLSConfig( - tlsutil.WithInsecureSkipVerify(insecure), - tlsutil.WithCertKeyPairFiles(pub, priv), - tlsutil.WithCAFile(ca), - ) - if err != nil { - panic(err) - } - tlsConf.ServerName = "helm.sh" - s.srv.TLS = tlsConf - s.srv.StartTLS() - - // Set up repositories config with ca file - repoConfig := filepath.Join(s.Root(), "repositories.yaml") - - r := repo.NewFile() - r.Add(&repo.Entry{ - Name: "test", - URL: s.URL(), - CAFile: filepath.Join("../../testdata", "rootca.crt"), - }) - - if err := r.WriteFile(repoConfig, 0600); err != nil { - panic(err) +func (s *Server) start() { + if s.tlsConfig != nil { + s.srv.TLS = s.tlsConfig + s.srv.StartTLS() + } else { + s.srv.Start() } } @@ -411,6 +379,10 @@ func (s *Server) URL() string { return s.srv.URL } +func (s *Server) Client() *http.Client { + return s.srv.Client() +} + // LinkIndices links the index created with CreateIndex and makes a symbolic link to the cache index. // // This makes it possible to simulate a local cache of a repository. @@ -422,6 +394,10 @@ func (s *Server) LinkIndices() error { // setTestingRepository sets up a testing repository.yaml with only the given URL. func setTestingRepository(url, fname string) error { + if url == "" { + panic("no url") + } + r := repo.NewFile() r.Add(&repo.Entry{ Name: "test", diff --git a/pkg/repo/repotest/server_test.go b/pkg/repo/repotest/server_test.go index 6d15925db..cf68e5110 100644 --- a/pkg/repo/repotest/server_test.go +++ b/pkg/repo/repotest/server_test.go @@ -19,6 +19,7 @@ import ( "io" "net/http" "path/filepath" + "strings" "testing" "sigs.k8s.io/yaml" @@ -34,7 +35,7 @@ func TestServer(t *testing.T) { rootDir := t.TempDir() - srv := NewServer(rootDir) + srv := newServer(t, rootDir) defer srv.Stop() c, err := srv.CopyCharts("testdata/*.tgz") @@ -99,18 +100,123 @@ func TestServer(t *testing.T) { func TestNewTempServer(t *testing.T) { ensure.HelmHome(t) - srv, err := NewTempServerWithCleanup(t, "testdata/examplechart-0.1.0.tgz") - if err != nil { - t.Fatal(err) + type testCase struct { + options []ServerOption } - defer srv.Stop() - res, err := http.Head(srv.URL() + "/examplechart-0.1.0.tgz") - res.Body.Close() - if err != nil { - t.Error(err) + testCases := map[string]testCase{ + "plainhttp": { + options: []ServerOption{ + WithChartSourceGlob("testdata/examplechart-0.1.0.tgz"), + }, + }, + "tls": { + options: []ServerOption{ + WithChartSourceGlob("testdata/examplechart-0.1.0.tgz"), + WithTLSConfig(MakeTestTLSConfig(t, "../../../testdata")), + }, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + srv := NewTempServer( + t, + tc.options..., + ) + defer srv.Stop() + + if srv.srv.URL == "" { + t.Fatal("unstarted server") + } + + client := srv.Client() + + { + res, err := client.Head(srv.URL() + "/repositories.yaml") + if err != nil { + t.Error(err) + } + + res.Body.Close() + + if res.StatusCode != 200 { + t.Errorf("Expected 200, got %d", res.StatusCode) + } + + } + + { + res, err := client.Head(srv.URL() + "/examplechart-0.1.0.tgz") + if err != nil { + t.Error(err) + } + res.Body.Close() + + if res.StatusCode != 200 { + t.Errorf("Expected 200, got %d", res.StatusCode) + } + } + + res, err := client.Get(srv.URL() + "/examplechart-0.1.0.tgz") + res.Body.Close() + if err != nil { + t.Fatal(err) + } + + if res.ContentLength < 500 { + t.Errorf("Expected at least 500 bytes of data, got %d", res.ContentLength) + } + + res, err = client.Get(srv.URL() + "/index.yaml") + if err != nil { + t.Fatal(err) + } + + data, err := io.ReadAll(res.Body) + res.Body.Close() + if err != nil { + t.Fatal(err) + } + + m := repo.NewIndexFile() + if err := yaml.Unmarshal(data, m); err != nil { + t.Fatal(err) + } + + if l := len(m.Entries); l != 1 { + t.Fatalf("Expected 1 entry, got %d", l) + } + + expect := "examplechart" + if !m.Has(expect, "0.1.0") { + t.Errorf("missing %q", expect) + } + + res, err = client.Get(srv.URL() + "/index.yaml-nosuchthing") + res.Body.Close() + if err != nil { + t.Fatal(err) + } + if res.StatusCode != 404 { + t.Fatalf("Expected 404, got %d", res.StatusCode) + } + }) } - if res.StatusCode != 200 { - t.Errorf("Expected 200, got %d", res.StatusCode) + +} + +func TestNewTempServer_TLS(t *testing.T) { + ensure.HelmHome(t) + + srv := NewTempServer( + t, + WithChartSourceGlob("testdata/examplechart-0.1.0.tgz"), + WithTLSConfig(MakeTestTLSConfig(t, "../../../testdata")), + ) + defer srv.Stop() + + if !strings.HasPrefix(srv.URL(), "https://") { + t.Fatal("non-TLS server") } } diff --git a/pkg/repo/repotest/tlsconfig.go b/pkg/repo/repotest/tlsconfig.go new file mode 100644 index 000000000..3914a4d3f --- /dev/null +++ b/pkg/repo/repotest/tlsconfig.go @@ -0,0 +1,43 @@ +/* +Copyright The Helm Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package repotest + +import ( + "crypto/tls" + "path/filepath" + "testing" + + "helm.sh/helm/v4/internal/tlsutil" + + "github.com/stretchr/testify/require" +) + +func MakeTestTLSConfig(t *testing.T, path string) *tls.Config { + ca, pub, priv := filepath.Join(path, "rootca.crt"), filepath.Join(path, "crt.pem"), filepath.Join(path, "key.pem") + + insecure := false + tlsConf, err := tlsutil.NewTLSConfig( + tlsutil.WithInsecureSkipVerify(insecure), + tlsutil.WithCertKeyPairFiles(pub, priv), + tlsutil.WithCAFile(ca), + ) + //require.Nil(t, err, err.Error()) + require.Nil(t, err) + + tlsConf.ServerName = "helm.sh" + + return tlsConf +} diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index af339b85e..6a77cbae6 100644 --- a/pkg/storage/storage.go +++ b/pkg/storage/storage.go @@ -23,7 +23,7 @@ import ( "github.com/pkg/errors" rspb "helm.sh/helm/v4/pkg/release" - relutil "helm.sh/helm/v4/pkg/releaseutil" + relutil "helm.sh/helm/v4/pkg/release/util" "helm.sh/helm/v4/pkg/storage/driver" ) diff --git a/scripts/get-helm-3 b/scripts/get-helm-3 index 5f19bd3fa..3aa44daee 100755 --- a/scripts/get-helm-3 +++ b/scripts/get-helm-3 @@ -279,7 +279,7 @@ testVersion() { help () { echo "Accepted cli arguments are:" echo -e "\t[--help|-h ] ->> prints this help" - echo -e "\t[--version|-v ] . When not defined it fetches the latest release from GitHub" + echo -e "\t[--version|-v ] . When not defined it fetches the latest release tag from the Helm CDN" echo -e "\te.g. --version v3.0.0 or -v canary" echo -e "\t[--no-sudo] ->> install without sudo" }