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