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  }