github.com/doitroot/helm@v3.0.0-beta.3+incompatible/pkg/chartutil/dependencies_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 package chartutil 16 17 import ( 18 "os" 19 "path/filepath" 20 "sort" 21 "strconv" 22 "testing" 23 24 "helm.sh/helm/pkg/chart" 25 "helm.sh/helm/pkg/chart/loader" 26 ) 27 28 func loadChart(t *testing.T, path string) *chart.Chart { 29 t.Helper() 30 c, err := loader.Load(path) 31 if err != nil { 32 t.Fatalf("failed to load testdata: %s", err) 33 } 34 return c 35 } 36 37 func TestLoadDependency(t *testing.T) { 38 tests := []*chart.Dependency{ 39 {Name: "alpine", Version: "0.1.0", Repository: "https://example.com/charts"}, 40 {Name: "mariner", Version: "4.3.2", Repository: "https://example.com/charts"}, 41 } 42 43 check := func(deps []*chart.Dependency) { 44 if len(deps) != 2 { 45 t.Errorf("expected 2 dependencies, got %d", len(deps)) 46 } 47 for i, tt := range tests { 48 if deps[i].Name != tt.Name { 49 t.Errorf("expected dependency named %q, got %q", tt.Name, deps[i].Name) 50 } 51 if deps[i].Version != tt.Version { 52 t.Errorf("expected dependency named %q to have version %q, got %q", tt.Name, tt.Version, deps[i].Version) 53 } 54 if deps[i].Repository != tt.Repository { 55 t.Errorf("expected dependency named %q to have repository %q, got %q", tt.Name, tt.Repository, deps[i].Repository) 56 } 57 } 58 } 59 c := loadChart(t, "testdata/frobnitz") 60 check(c.Metadata.Dependencies) 61 check(c.Lock.Dependencies) 62 } 63 64 func TestDependencyEnabled(t *testing.T) { 65 type M = map[string]interface{} 66 tests := []struct { 67 name string 68 v M 69 e []string // expected charts including duplicates in alphanumeric order 70 }{{ 71 "tags with no effect", 72 M{"tags": M{"nothinguseful": false}}, 73 []string{"parentchart", "parentchart.subchart1", "parentchart.subchart1.subcharta", "parentchart.subchart1.subchartb"}, 74 }, { 75 "tags disabling a group", 76 M{"tags": M{"front-end": false}}, 77 []string{"parentchart"}, 78 }, { 79 "tags disabling a group and enabling a different group", 80 M{"tags": M{"front-end": false, "back-end": true}}, 81 []string{"parentchart", "parentchart.subchart2", "parentchart.subchart2.subchartb", "parentchart.subchart2.subchartc"}, 82 }, { 83 "tags disabling only children, children still enabled since tag front-end=true in values.yaml", 84 M{"tags": M{"subcharta": false, "subchartb": false}}, 85 []string{"parentchart", "parentchart.subchart1", "parentchart.subchart1.subcharta", "parentchart.subchart1.subchartb"}, 86 }, { 87 "tags disabling all parents/children with additional tag re-enabling a parent", 88 M{"tags": M{"front-end": false, "subchart1": true, "back-end": false}}, 89 []string{"parentchart", "parentchart.subchart1"}, 90 }, { 91 "conditions enabling the parent charts, but back-end (b, c) is still disabled via values.yaml", 92 M{"subchart1": M{"enabled": true}, "subchart2": M{"enabled": true}}, 93 []string{"parentchart", "parentchart.subchart1", "parentchart.subchart1.subcharta", "parentchart.subchart1.subchartb", "parentchart.subchart2"}, 94 }, { 95 "conditions disabling the parent charts, effectively disabling children", 96 M{"subchart1": M{"enabled": false}, "subchart2": M{"enabled": false}}, 97 []string{"parentchart"}, 98 }, { 99 "conditions a child using the second condition path of child's condition", 100 M{"subchart1": M{"subcharta": M{"enabled": false}}}, 101 []string{"parentchart", "parentchart.subchart1", "parentchart.subchart1.subchartb"}, 102 }, { 103 "tags enabling a parent/child group with condition disabling one child", 104 M{"subchartc": M{"enabled": false}, "tags": M{"back-end": true}}, 105 []string{"parentchart", "parentchart.subchart1", "parentchart.subchart1.subcharta", "parentchart.subchart1.subchartb", "parentchart.subchart2", "parentchart.subchart2.subchartb"}, 106 }, { 107 "tags will not enable a child if parent is explicitly disabled with condition", 108 M{"subchart1": M{"enabled": false}, "tags": M{"front-end": true}}, 109 []string{"parentchart"}, 110 }} 111 112 for _, tc := range tests { 113 c := loadChart(t, "testdata/subpop") 114 t.Run(tc.name, func(t *testing.T) { 115 if err := processDependencyEnabled(c, tc.v); err != nil { 116 t.Fatalf("error processing enabled dependencies %v", err) 117 } 118 119 names := extractChartNames(c) 120 if len(names) != len(tc.e) { 121 t.Fatalf("slice lengths do not match got %v, expected %v", len(names), len(tc.e)) 122 } 123 for i := range names { 124 if names[i] != tc.e[i] { 125 t.Fatalf("slice values do not match got %v, expected %v", names, tc.e) 126 } 127 } 128 }) 129 } 130 } 131 132 // extractCharts recursively searches chart dependencies returning all charts found 133 func extractChartNames(c *chart.Chart) []string { 134 var out []string 135 var fn func(c *chart.Chart) 136 fn = func(c *chart.Chart) { 137 out = append(out, c.ChartPath()) 138 for _, d := range c.Dependencies() { 139 fn(d) 140 } 141 } 142 fn(c) 143 sort.Strings(out) 144 return out 145 } 146 147 func TestProcessDependencyImportValues(t *testing.T) { 148 c := loadChart(t, "testdata/subpop") 149 150 e := make(map[string]string) 151 152 e["imported-chart1.SC1bool"] = "true" 153 e["imported-chart1.SC1float"] = "3.14" 154 e["imported-chart1.SC1int"] = "100" 155 e["imported-chart1.SC1string"] = "dollywood" 156 e["imported-chart1.SC1extra1"] = "11" 157 e["imported-chart1.SPextra1"] = "helm rocks" 158 e["imported-chart1.SC1extra1"] = "11" 159 160 e["imported-chartA.SCAbool"] = "false" 161 e["imported-chartA.SCAfloat"] = "3.1" 162 e["imported-chartA.SCAint"] = "55" 163 e["imported-chartA.SCAstring"] = "jabba" 164 e["imported-chartA.SPextra3"] = "1.337" 165 e["imported-chartA.SC1extra2"] = "1.337" 166 e["imported-chartA.SCAnested1.SCAnested2"] = "true" 167 168 e["imported-chartA-B.SCAbool"] = "false" 169 e["imported-chartA-B.SCAfloat"] = "3.1" 170 e["imported-chartA-B.SCAint"] = "55" 171 e["imported-chartA-B.SCAstring"] = "jabba" 172 173 e["imported-chartA-B.SCBbool"] = "true" 174 e["imported-chartA-B.SCBfloat"] = "7.77" 175 e["imported-chartA-B.SCBint"] = "33" 176 e["imported-chartA-B.SCBstring"] = "boba" 177 e["imported-chartA-B.SPextra5"] = "k8s" 178 e["imported-chartA-B.SC1extra5"] = "tiller" 179 180 e["overridden-chart1.SC1bool"] = "false" 181 e["overridden-chart1.SC1float"] = "3.141592" 182 e["overridden-chart1.SC1int"] = "99" 183 e["overridden-chart1.SC1string"] = "pollywog" 184 e["overridden-chart1.SPextra2"] = "42" 185 186 e["overridden-chartA.SCAbool"] = "true" 187 e["overridden-chartA.SCAfloat"] = "41.3" 188 e["overridden-chartA.SCAint"] = "808" 189 e["overridden-chartA.SCAstring"] = "jaberwocky" 190 e["overridden-chartA.SPextra4"] = "true" 191 192 e["overridden-chartA-B.SCAbool"] = "true" 193 e["overridden-chartA-B.SCAfloat"] = "41.3" 194 e["overridden-chartA-B.SCAint"] = "808" 195 e["overridden-chartA-B.SCAstring"] = "jaberwocky" 196 e["overridden-chartA-B.SCBbool"] = "false" 197 e["overridden-chartA-B.SCBfloat"] = "1.99" 198 e["overridden-chartA-B.SCBint"] = "77" 199 e["overridden-chartA-B.SCBstring"] = "jango" 200 e["overridden-chartA-B.SPextra6"] = "111" 201 e["overridden-chartA-B.SCAextra1"] = "23" 202 e["overridden-chartA-B.SCBextra1"] = "13" 203 e["overridden-chartA-B.SC1extra6"] = "77" 204 205 // `exports` style 206 e["SCBexported1B"] = "1965" 207 e["SC1extra7"] = "true" 208 e["SCBexported2A"] = "blaster" 209 e["global.SC1exported2.all.SC1exported3"] = "SC1expstr" 210 211 if err := processDependencyImportValues(c); err != nil { 212 t.Fatalf("processing import values dependencies %v", err) 213 } 214 cc := Values(c.Values) 215 for kk, vv := range e { 216 pv, err := cc.PathValue(kk) 217 if err != nil { 218 t.Fatalf("retrieving import values table %v %v", kk, err) 219 } 220 221 switch pv := pv.(type) { 222 case float64: 223 if s := strconv.FormatFloat(pv, 'f', -1, 64); s != vv { 224 t.Errorf("failed to match imported float value %v with expected %v", s, vv) 225 } 226 case bool: 227 if b := strconv.FormatBool(pv); b != vv { 228 t.Errorf("failed to match imported bool value %v with expected %v", b, vv) 229 } 230 default: 231 if pv != vv { 232 t.Errorf("failed to match imported string value %q with expected %q", pv, vv) 233 } 234 } 235 } 236 } 237 238 func TestGetAliasDependency(t *testing.T) { 239 c := loadChart(t, "testdata/frobnitz") 240 req := c.Metadata.Dependencies 241 242 if len(req) == 0 { 243 t.Fatalf("there are no dependencies to test") 244 } 245 246 // Success case 247 aliasChart := getAliasDependency(c.Dependencies(), req[0]) 248 if aliasChart == nil { 249 t.Fatalf("failed to get dependency chart for alias %s", req[0].Name) 250 } 251 if req[0].Alias != "" { 252 if aliasChart.Name() != req[0].Alias { 253 t.Fatalf("dependency chart name should be %s but got %s", req[0].Alias, aliasChart.Name()) 254 } 255 } else if aliasChart.Name() != req[0].Name { 256 t.Fatalf("dependency chart name should be %s but got %s", req[0].Name, aliasChart.Name()) 257 } 258 259 if req[0].Version != "" { 260 if !IsCompatibleRange(req[0].Version, aliasChart.Metadata.Version) { 261 t.Fatalf("dependency chart version is not in the compatible range") 262 } 263 } 264 265 // Failure case 266 req[0].Name = "something-else" 267 if aliasChart := getAliasDependency(c.Dependencies(), req[0]); aliasChart != nil { 268 t.Fatalf("expected no chart but got %s", aliasChart.Name()) 269 } 270 271 req[0].Version = "something else which is not in the compatible range" 272 if IsCompatibleRange(req[0].Version, aliasChart.Metadata.Version) { 273 t.Fatalf("dependency chart version which is not in the compatible range should cause a failure other than a success ") 274 } 275 } 276 277 func TestDependentChartAliases(t *testing.T) { 278 c := loadChart(t, "testdata/dependent-chart-alias") 279 280 if len(c.Dependencies()) != 2 { 281 t.Fatalf("expected 2 dependencies for this chart, but got %d", len(c.Dependencies())) 282 } 283 284 if err := processDependencyEnabled(c, c.Values); err != nil { 285 t.Fatalf("expected no errors but got %q", err) 286 } 287 288 if len(c.Dependencies()) != 3 { 289 t.Fatal("expected alias dependencies to be added") 290 } 291 292 if len(c.Dependencies()) != len(c.Metadata.Dependencies) { 293 t.Fatalf("expected number of chart dependencies %d, but got %d", len(c.Metadata.Dependencies), len(c.Dependencies())) 294 } 295 // FIXME test for correct aliases 296 } 297 298 func TestDependentChartWithSubChartsAbsentInDependency(t *testing.T) { 299 c := loadChart(t, "testdata/dependent-chart-no-requirements-yaml") 300 301 if len(c.Dependencies()) != 2 { 302 t.Fatalf("expected 2 dependencies for this chart, but got %d", len(c.Dependencies())) 303 } 304 305 if err := processDependencyEnabled(c, c.Values); err != nil { 306 t.Fatalf("expected no errors but got %q", err) 307 } 308 309 if len(c.Dependencies()) != 2 { 310 t.Fatal("expected no changes in dependencies") 311 } 312 } 313 314 func TestDependentChartWithSubChartsHelmignore(t *testing.T) { 315 // FIXME what does this test? 316 loadChart(t, "testdata/dependent-chart-helmignore") 317 } 318 319 func TestDependentChartsWithSubChartsSymlink(t *testing.T) { 320 joonix := filepath.Join("testdata", "joonix") 321 if err := os.Symlink(filepath.Join("..", "..", "frobnitz"), filepath.Join(joonix, "charts", "frobnitz")); err != nil { 322 t.Fatal(err) 323 } 324 defer os.RemoveAll(filepath.Join(joonix, "charts", "frobnitz")) 325 c := loadChart(t, joonix) 326 327 if c.Name() != "joonix" { 328 t.Fatalf("unexpected chart name: %s", c.Name()) 329 } 330 if n := len(c.Dependencies()); n != 1 { 331 t.Fatalf("expected 1 dependency for this chart, but got %d", n) 332 } 333 } 334 335 func TestDependentChartsWithSubchartsAllSpecifiedInDependency(t *testing.T) { 336 c := loadChart(t, "testdata/dependent-chart-with-all-in-requirements-yaml") 337 338 if len(c.Dependencies()) != 2 { 339 t.Fatalf("expected 2 dependencies for this chart, but got %d", len(c.Dependencies())) 340 } 341 342 if err := processDependencyEnabled(c, c.Values); err != nil { 343 t.Fatalf("expected no errors but got %q", err) 344 } 345 346 if len(c.Dependencies()) != 2 { 347 t.Fatal("expected no changes in dependencies") 348 } 349 350 if len(c.Dependencies()) != len(c.Metadata.Dependencies) { 351 t.Fatalf("expected number of chart dependencies %d, but got %d", len(c.Metadata.Dependencies), len(c.Dependencies())) 352 } 353 } 354 355 func TestDependentChartsWithSomeSubchartsSpecifiedInDependency(t *testing.T) { 356 c := loadChart(t, "testdata/dependent-chart-with-mixed-requirements-yaml") 357 358 if len(c.Dependencies()) != 2 { 359 t.Fatalf("expected 2 dependencies for this chart, but got %d", len(c.Dependencies())) 360 } 361 362 if err := processDependencyEnabled(c, c.Values); err != nil { 363 t.Fatalf("expected no errors but got %q", err) 364 } 365 366 if len(c.Dependencies()) != 2 { 367 t.Fatal("expected no changes in dependencies") 368 } 369 370 if len(c.Metadata.Dependencies) != 1 { 371 t.Fatalf("expected 1 dependency specified in Chart.yaml, got %d", len(c.Metadata.Dependencies)) 372 } 373 }