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