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 }