github.com/Racer159/helm-experiment@v0.0.0-20230822001441-1eb31183f614/src/dependency_update_test.go (about)

     1  /*
     2  Copyright The Helm Authors.
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6  
     7  http://www.apache.org/licenses/LICENSE-2.0
     8  
     9  Unless required by applicable law or agreed to in writing, software
    10  distributed under the License is distributed on an "AS IS" BASIS,
    11  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  See the License for the specific language governing permissions and
    13  limitations under the License.
    14  */
    15  
    16  package cmd
    17  
    18  import (
    19  	"fmt"
    20  	"os"
    21  	"path/filepath"
    22  	"strings"
    23  	"testing"
    24  
    25  	"helm.sh/helm/v3/pkg/chart"
    26  	"helm.sh/helm/v3/pkg/chartutil"
    27  	"helm.sh/helm/v3/pkg/helmpath"
    28  	"helm.sh/helm/v3/pkg/provenance"
    29  	"helm.sh/helm/v3/pkg/repo"
    30  	"helm.sh/helm/v3/pkg/repo/repotest"
    31  )
    32  
    33  func TestDependencyUpdateCmd(t *testing.T) {
    34  	srv, err := repotest.NewTempServerWithCleanup(t, "testdata/testcharts/*.tgz")
    35  	if err != nil {
    36  		t.Fatal(err)
    37  	}
    38  	defer srv.Stop()
    39  	t.Logf("Listening on directory %s", srv.Root())
    40  
    41  	ociSrv, err := repotest.NewOCIServer(t, srv.Root())
    42  	if err != nil {
    43  		t.Fatal(err)
    44  	}
    45  
    46  	ociChartName := "oci-depending-chart"
    47  	c := createTestingMetadataForOCI(ociChartName, ociSrv.RegistryURL)
    48  	if _, err := chartutil.Save(c, ociSrv.Dir); err != nil {
    49  		t.Fatal(err)
    50  	}
    51  	ociSrv.Run(t, repotest.WithDependingChart(c))
    52  
    53  	if err := srv.LinkIndices(); err != nil {
    54  		t.Fatal(err)
    55  	}
    56  
    57  	dir := func(p ...string) string {
    58  		return filepath.Join(append([]string{srv.Root()}, p...)...)
    59  	}
    60  
    61  	chartname := "depup"
    62  	ch := createTestingMetadata(chartname, srv.URL())
    63  	md := ch.Metadata
    64  	if err := chartutil.SaveDir(ch, dir()); err != nil {
    65  		t.Fatal(err)
    66  	}
    67  
    68  	_, out, err := executeActionCommand(
    69  		fmt.Sprintf("dependency update '%s' --repository-config %s --repository-cache %s", dir(chartname), dir("repositories.yaml"), dir()),
    70  	)
    71  	if err != nil {
    72  		t.Logf("Output: %s", out)
    73  		t.Fatal(err)
    74  	}
    75  
    76  	// This is written directly to stdout, so we have to capture as is.
    77  	if !strings.Contains(out, `update from the "test" chart repository`) {
    78  		t.Errorf("Repo did not get updated\n%s", out)
    79  	}
    80  
    81  	// Make sure the actual file got downloaded.
    82  	expect := dir(chartname, "charts/reqtest-0.1.0.tgz")
    83  	if _, err := os.Stat(expect); err != nil {
    84  		t.Fatal(err)
    85  	}
    86  
    87  	hash, err := provenance.DigestFile(expect)
    88  	if err != nil {
    89  		t.Fatal(err)
    90  	}
    91  
    92  	i, err := repo.LoadIndexFile(dir(helmpath.CacheIndexFile("test")))
    93  	if err != nil {
    94  		t.Fatal(err)
    95  	}
    96  
    97  	reqver := i.Entries["reqtest"][0]
    98  	if h := reqver.Digest; h != hash {
    99  		t.Errorf("Failed hash match: expected %s, got %s", hash, h)
   100  	}
   101  
   102  	// Now change the dependencies and update. This verifies that on update,
   103  	// old dependencies are cleansed and new dependencies are added.
   104  	md.Dependencies = []*chart.Dependency{
   105  		{Name: "reqtest", Version: "0.1.0", Repository: srv.URL()},
   106  		{Name: "compressedchart", Version: "0.3.0", Repository: srv.URL()},
   107  	}
   108  	if err := chartutil.SaveChartfile(dir(chartname, "Chart.yaml"), md); err != nil {
   109  		t.Fatal(err)
   110  	}
   111  
   112  	_, out, err = executeActionCommand(fmt.Sprintf("dependency update '%s' --repository-config %s --repository-cache %s", dir(chartname), dir("repositories.yaml"), dir()))
   113  	if err != nil {
   114  		t.Logf("Output: %s", out)
   115  		t.Fatal(err)
   116  	}
   117  
   118  	// In this second run, we should see compressedchart-0.3.0.tgz, and not
   119  	// the 0.1.0 version.
   120  	expect = dir(chartname, "charts/compressedchart-0.3.0.tgz")
   121  	if _, err := os.Stat(expect); err != nil {
   122  		t.Fatalf("Expected %q: %s", expect, err)
   123  	}
   124  	unexpected := dir(chartname, "charts/compressedchart-0.1.0.tgz")
   125  	if _, err := os.Stat(unexpected); err == nil {
   126  		t.Fatalf("Unexpected %q", unexpected)
   127  	}
   128  
   129  	// test for OCI charts
   130  	if err := chartutil.SaveDir(c, dir()); err != nil {
   131  		t.Fatal(err)
   132  	}
   133  	cmd := fmt.Sprintf("dependency update '%s' --repository-config %s --repository-cache %s --registry-config %s/config.json",
   134  		dir(ociChartName),
   135  		dir("repositories.yaml"),
   136  		dir(),
   137  		dir())
   138  	_, out, err = executeActionCommand(cmd)
   139  	if err != nil {
   140  		t.Logf("Output: %s", out)
   141  		t.Fatal(err)
   142  	}
   143  	expect = dir(ociChartName, "charts/oci-dependent-chart-0.1.0.tgz")
   144  	if _, err := os.Stat(expect); err != nil {
   145  		t.Fatal(err)
   146  	}
   147  }
   148  
   149  func TestDependencyUpdateCmd_DoNotDeleteOldChartsOnError(t *testing.T) {
   150  	defer resetEnv()()
   151  	// defer ensure.HelmHome(t)()
   152  
   153  	srv, err := repotest.NewTempServerWithCleanup(t, "testdata/testcharts/*.tgz")
   154  	if err != nil {
   155  		t.Fatal(err)
   156  	}
   157  	defer srv.Stop()
   158  	t.Logf("Listening on directory %s", srv.Root())
   159  
   160  	if err := srv.LinkIndices(); err != nil {
   161  		t.Fatal(err)
   162  	}
   163  
   164  	chartname := "depupdelete"
   165  
   166  	dir := func(p ...string) string {
   167  		return filepath.Join(append([]string{srv.Root()}, p...)...)
   168  	}
   169  	createTestingChart(t, dir(), chartname, srv.URL())
   170  
   171  	_, output, err := executeActionCommand(fmt.Sprintf("dependency update %s --repository-config %s --repository-cache %s", dir(chartname), dir("repositories.yaml"), dir()))
   172  	if err != nil {
   173  		t.Logf("Output: %s", output)
   174  		t.Fatal(err)
   175  	}
   176  
   177  	// Chart repo is down
   178  	srv.Stop()
   179  
   180  	_, output, err = executeActionCommand(fmt.Sprintf("dependency update %s --repository-config %s --repository-cache %s", dir(chartname), dir("repositories.yaml"), dir()))
   181  	if err == nil {
   182  		t.Logf("Output: %s", output)
   183  		t.Fatal("Expected error, got nil")
   184  	}
   185  
   186  	// Make sure charts dir still has dependencies
   187  	files, err := os.ReadDir(filepath.Join(dir(chartname), "charts"))
   188  	if err != nil {
   189  		t.Fatal(err)
   190  	}
   191  	dependencies := []string{"compressedchart-0.1.0.tgz", "reqtest-0.1.0.tgz"}
   192  
   193  	if len(dependencies) != len(files) {
   194  		t.Fatalf("Expected %d chart dependencies, got %d", len(dependencies), len(files))
   195  	}
   196  	for index, file := range files {
   197  		if dependencies[index] != file.Name() {
   198  			t.Fatalf("Chart dependency %s not matching %s", dependencies[index], file.Name())
   199  		}
   200  	}
   201  
   202  	// Make sure tmpcharts is deleted
   203  	if _, err := os.Stat(filepath.Join(dir(chartname), "tmpcharts")); !os.IsNotExist(err) {
   204  		t.Fatalf("tmpcharts dir still exists")
   205  	}
   206  }
   207  
   208  // createTestingMetadata creates a basic chart that depends on reqtest-0.1.0
   209  //
   210  // The baseURL can be used to point to a particular repository server.
   211  func createTestingMetadata(name, baseURL string) *chart.Chart {
   212  	return &chart.Chart{
   213  		Metadata: &chart.Metadata{
   214  			APIVersion: chart.APIVersionV2,
   215  			Name:       name,
   216  			Version:    "1.2.3",
   217  			Dependencies: []*chart.Dependency{
   218  				{Name: "reqtest", Version: "0.1.0", Repository: baseURL},
   219  				{Name: "compressedchart", Version: "0.1.0", Repository: baseURL},
   220  			},
   221  		},
   222  	}
   223  }
   224  
   225  func createTestingMetadataForOCI(name, registryURL string) *chart.Chart {
   226  	return &chart.Chart{
   227  		Metadata: &chart.Metadata{
   228  			APIVersion: chart.APIVersionV2,
   229  			Name:       name,
   230  			Version:    "1.2.3",
   231  			Dependencies: []*chart.Dependency{
   232  				{Name: "oci-dependent-chart", Version: "0.1.0", Repository: fmt.Sprintf("oci://%s/u/ocitestuser", registryURL)},
   233  			},
   234  		},
   235  	}
   236  }
   237  
   238  // createTestingChart creates a basic chart that depends on reqtest-0.1.0
   239  //
   240  // The baseURL can be used to point to a particular repository server.
   241  func createTestingChart(t *testing.T, dest, name, baseURL string) {
   242  	t.Helper()
   243  	cfile := createTestingMetadata(name, baseURL)
   244  	if err := chartutil.SaveDir(cfile, dest); err != nil {
   245  		t.Fatal(err)
   246  	}
   247  }