mirror of
https://github.com/helm/helm.git
synced 2026-02-03 20:39:45 -05:00
The repo package is internally versioned at v1. Repos were designed to be versioned. This change moves it to a versioned directory the same way other packages are now being handled. Signed-off-by: Matt Farina <matt.farina@suse.com>
312 lines
9 KiB
Go
312 lines
9 KiB
Go
/*
|
||
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 cmd
|
||
|
||
import (
|
||
"errors"
|
||
"fmt"
|
||
"io/fs"
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
"testing"
|
||
|
||
"helm.sh/helm/v4/internal/test/ensure"
|
||
chart "helm.sh/helm/v4/pkg/chart/v2"
|
||
chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
|
||
"helm.sh/helm/v4/pkg/helmpath"
|
||
"helm.sh/helm/v4/pkg/provenance"
|
||
"helm.sh/helm/v4/pkg/repo/v1"
|
||
"helm.sh/helm/v4/pkg/repo/v1/repotest"
|
||
)
|
||
|
||
func TestDependencyUpdateCmd(t *testing.T) {
|
||
srv := repotest.NewTempServer(
|
||
t,
|
||
repotest.WithChartSourceGlob("testdata/testcharts/*.tgz"),
|
||
)
|
||
defer srv.Stop()
|
||
t.Logf("Listening on directory %s", srv.Root())
|
||
|
||
ociSrv, err := repotest.NewOCIServer(t, srv.Root())
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
contentCache := t.TempDir()
|
||
|
||
ociChartName := "oci-depending-chart"
|
||
c := createTestingMetadataForOCI(ociChartName, ociSrv.RegistryURL)
|
||
if _, err := chartutil.Save(c, ociSrv.Dir); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
ociSrv.Run(t, repotest.WithDependingChart(c))
|
||
|
||
if err := srv.LinkIndices(); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
dir := func(p ...string) string {
|
||
return filepath.Join(append([]string{srv.Root()}, p...)...)
|
||
}
|
||
|
||
chartname := "depup"
|
||
ch := createTestingMetadata(chartname, srv.URL())
|
||
md := ch.Metadata
|
||
if err := chartutil.SaveDir(ch, dir()); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
_, out, err := executeActionCommand(
|
||
fmt.Sprintf("dependency update '%s' --repository-config %s --repository-cache %s --content-cache %s --plain-http", dir(chartname), dir("repositories.yaml"), dir(), contentCache),
|
||
)
|
||
if err != nil {
|
||
t.Logf("Output: %s", out)
|
||
t.Fatal(err)
|
||
}
|
||
|
||
// This is written directly to stdout, so we have to capture as is.
|
||
if !strings.Contains(out, `update from the "test" chart repository`) {
|
||
t.Errorf("Repo did not get updated\n%s", out)
|
||
}
|
||
|
||
// Make sure the actual file got downloaded.
|
||
expect := dir(chartname, "charts/reqtest-0.1.0.tgz")
|
||
if _, err := os.Stat(expect); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
hash, err := provenance.DigestFile(expect)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
i, err := repo.LoadIndexFile(dir(helmpath.CacheIndexFile("test")))
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
reqver := i.Entries["reqtest"][0]
|
||
if h := reqver.Digest; h != hash {
|
||
t.Errorf("Failed hash match: expected %s, got %s", hash, h)
|
||
}
|
||
|
||
// Now change the dependencies and update. This verifies that on update,
|
||
// old dependencies are cleansed and new dependencies are added.
|
||
md.Dependencies = []*chart.Dependency{
|
||
{Name: "reqtest", Version: "0.1.0", Repository: srv.URL()},
|
||
{Name: "compressedchart", Version: "0.3.0", Repository: srv.URL()},
|
||
}
|
||
if err := chartutil.SaveChartfile(dir(chartname, "Chart.yaml"), md); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
_, out, err = executeActionCommand(fmt.Sprintf("dependency update '%s' --repository-config %s --repository-cache %s --content-cache %s --plain-http", dir(chartname), dir("repositories.yaml"), dir(), contentCache))
|
||
if err != nil {
|
||
t.Logf("Output: %s", out)
|
||
t.Fatal(err)
|
||
}
|
||
|
||
// In this second run, we should see compressedchart-0.3.0.tgz, and not
|
||
// the 0.1.0 version.
|
||
expect = dir(chartname, "charts/compressedchart-0.3.0.tgz")
|
||
if _, err := os.Stat(expect); err != nil {
|
||
t.Fatalf("Expected %q: %s", expect, err)
|
||
}
|
||
unexpected := dir(chartname, "charts/compressedchart-0.1.0.tgz")
|
||
if _, err := os.Stat(unexpected); err == nil {
|
||
t.Fatalf("Unexpected %q", unexpected)
|
||
}
|
||
|
||
// test for OCI charts
|
||
if err := chartutil.SaveDir(c, dir()); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
cmd := fmt.Sprintf("dependency update '%s' --repository-config %s --repository-cache %s --registry-config %s/config.json --content-cache %s --plain-http",
|
||
dir(ociChartName),
|
||
dir("repositories.yaml"),
|
||
dir(),
|
||
dir(),
|
||
contentCache)
|
||
_, out, err = executeActionCommand(cmd)
|
||
if err != nil {
|
||
t.Logf("Output: %s", out)
|
||
t.Fatal(err)
|
||
}
|
||
expect = dir(ociChartName, "charts/oci-dependent-chart-0.1.0.tgz")
|
||
if _, err := os.Stat(expect); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
}
|
||
|
||
func TestDependencyUpdateCmd_DoNotDeleteOldChartsOnError(t *testing.T) {
|
||
defer resetEnv()()
|
||
ensure.HelmHome(t)
|
||
|
||
srv := repotest.NewTempServer(
|
||
t,
|
||
repotest.WithChartSourceGlob("testdata/testcharts/*.tgz"),
|
||
)
|
||
defer srv.Stop()
|
||
t.Logf("Listening on directory %s", srv.Root())
|
||
|
||
if err := srv.LinkIndices(); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
chartname := "depupdelete"
|
||
|
||
dir := func(p ...string) string {
|
||
return filepath.Join(append([]string{srv.Root()}, p...)...)
|
||
}
|
||
createTestingChart(t, dir(), chartname, srv.URL())
|
||
|
||
_, output, err := executeActionCommand(fmt.Sprintf("dependency update %s --repository-config %s --repository-cache %s --plain-http", dir(chartname), dir("repositories.yaml"), dir()))
|
||
if err != nil {
|
||
t.Logf("Output: %s", output)
|
||
t.Fatal(err)
|
||
}
|
||
|
||
// Chart repo is down
|
||
srv.Stop()
|
||
contentCache := t.TempDir()
|
||
|
||
_, output, err = executeActionCommand(fmt.Sprintf("dependency update %s --repository-config %s --repository-cache %s --content-cache %s --plain-http", dir(chartname), dir("repositories.yaml"), dir(), contentCache))
|
||
if err == nil {
|
||
t.Logf("Output: %s", output)
|
||
t.Fatal("Expected error, got nil")
|
||
}
|
||
|
||
// Make sure charts dir still has dependencies
|
||
files, err := os.ReadDir(filepath.Join(dir(chartname), "charts"))
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
dependencies := []string{"compressedchart-0.1.0.tgz", "reqtest-0.1.0.tgz"}
|
||
|
||
if len(dependencies) != len(files) {
|
||
t.Fatalf("Expected %d chart dependencies, got %d", len(dependencies), len(files))
|
||
}
|
||
for index, file := range files {
|
||
if dependencies[index] != file.Name() {
|
||
t.Fatalf("Chart dependency %s not matching %s", dependencies[index], file.Name())
|
||
}
|
||
}
|
||
|
||
// Make sure tmpcharts-x is deleted
|
||
tmpPath := filepath.Join(dir(chartname), fmt.Sprintf("tmpcharts-%d", os.Getpid()))
|
||
if _, err := os.Stat(tmpPath); !errors.Is(err, fs.ErrNotExist) {
|
||
t.Fatalf("tmpcharts dir still exists")
|
||
}
|
||
}
|
||
|
||
func TestDependencyUpdateCmd_WithRepoThatWasNotAdded(t *testing.T) {
|
||
srv := setupMockRepoServer(t)
|
||
srvForUnmanagedRepo := setupMockRepoServer(t)
|
||
defer srv.Stop()
|
||
defer srvForUnmanagedRepo.Stop()
|
||
|
||
dir := func(p ...string) string {
|
||
return filepath.Join(append([]string{srv.Root()}, p...)...)
|
||
}
|
||
|
||
chartname := "depup"
|
||
ch := createTestingMetadata(chartname, srv.URL())
|
||
chartDependency := &chart.Dependency{
|
||
Name: "signtest",
|
||
Version: "0.1.0",
|
||
Repository: srvForUnmanagedRepo.URL(),
|
||
}
|
||
ch.Metadata.Dependencies = append(ch.Metadata.Dependencies, chartDependency)
|
||
|
||
if err := chartutil.SaveDir(ch, dir()); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
contentCache := t.TempDir()
|
||
|
||
_, out, err := executeActionCommand(
|
||
fmt.Sprintf("dependency update '%s' --repository-config %s --repository-cache %s --content-cache %s", dir(chartname),
|
||
dir("repositories.yaml"), dir(), contentCache),
|
||
)
|
||
|
||
if err != nil {
|
||
t.Logf("Output: %s", out)
|
||
t.Fatal(err)
|
||
}
|
||
|
||
// This is written directly to stdout, so we have to capture as is
|
||
if !strings.Contains(out, `Getting updates for unmanaged Helm repositories...`) {
|
||
t.Errorf("No ‘unmanaged’ Helm repo used in test chartdependency or it doesn’t cause the creation "+
|
||
"of an ‘ad hoc’ repo index cache file\n%s", out)
|
||
}
|
||
}
|
||
|
||
func setupMockRepoServer(t *testing.T) *repotest.Server {
|
||
t.Helper()
|
||
srv := repotest.NewTempServer(
|
||
t,
|
||
repotest.WithChartSourceGlob("testdata/testcharts/*.tgz"),
|
||
)
|
||
|
||
t.Logf("Listening on directory %s", srv.Root())
|
||
|
||
if err := srv.LinkIndices(); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
return srv
|
||
}
|
||
|
||
// createTestingMetadata creates a basic chart that depends on reqtest-0.1.0
|
||
//
|
||
// The baseURL can be used to point to a particular repository server.
|
||
func createTestingMetadata(name, baseURL string) *chart.Chart {
|
||
return &chart.Chart{
|
||
Metadata: &chart.Metadata{
|
||
APIVersion: chart.APIVersionV2,
|
||
Name: name,
|
||
Version: "1.2.3",
|
||
Dependencies: []*chart.Dependency{
|
||
{Name: "reqtest", Version: "0.1.0", Repository: baseURL},
|
||
{Name: "compressedchart", Version: "0.1.0", Repository: baseURL},
|
||
},
|
||
},
|
||
}
|
||
}
|
||
|
||
func createTestingMetadataForOCI(name, registryURL string) *chart.Chart {
|
||
return &chart.Chart{
|
||
Metadata: &chart.Metadata{
|
||
APIVersion: chart.APIVersionV2,
|
||
Name: name,
|
||
Version: "1.2.3",
|
||
Dependencies: []*chart.Dependency{
|
||
{Name: "oci-dependent-chart", Version: "0.1.0", Repository: fmt.Sprintf("oci://%s/u/ocitestuser", registryURL)},
|
||
},
|
||
},
|
||
}
|
||
}
|
||
|
||
// createTestingChart creates a basic chart that depends on reqtest-0.1.0
|
||
//
|
||
// The baseURL can be used to point to a particular repository server.
|
||
func createTestingChart(t *testing.T, dest, name, baseURL string) {
|
||
t.Helper()
|
||
cfile := createTestingMetadata(name, baseURL)
|
||
if err := chartutil.SaveDir(cfile, dest); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
}
|