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  }