github.com/sdbaiguanghe/helm@v2.16.7+incompatible/pkg/chartutil/requirements_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  	"testing"
    22  
    23  	"strconv"
    24  
    25  	"k8s.io/helm/pkg/proto/hapi/chart"
    26  	"k8s.io/helm/pkg/version"
    27  )
    28  
    29  func TestLoadRequirements(t *testing.T) {
    30  	c, err := Load("testdata/frobnitz")
    31  	if err != nil {
    32  		t.Fatalf("Failed to load testdata: %s", err)
    33  	}
    34  	verifyRequirements(t, c)
    35  }
    36  
    37  func TestLoadRequirementsLock(t *testing.T) {
    38  	c, err := Load("testdata/frobnitz")
    39  	if err != nil {
    40  		t.Fatalf("Failed to load testdata: %s", err)
    41  	}
    42  	verifyRequirementsLock(t, c)
    43  }
    44  func TestRequirementsTagsNonValue(t *testing.T) {
    45  	c, err := Load("testdata/subpop")
    46  	if err != nil {
    47  		t.Fatalf("Failed to load testdata: %s", err)
    48  	}
    49  	// tags with no effect
    50  	v := &chart.Config{Raw: "tags:\n  nothinguseful: false\n\n"}
    51  	// expected charts including duplicates in alphanumeric order
    52  	e := []string{"parentchart", "subchart1", "subcharta", "subchartb"}
    53  
    54  	verifyRequirementsEnabled(t, c, v, e)
    55  }
    56  func TestRequirementsTagsDisabledL1(t *testing.T) {
    57  	c, err := Load("testdata/subpop")
    58  	if err != nil {
    59  		t.Fatalf("Failed to load testdata: %s", err)
    60  	}
    61  	// tags disabling a group
    62  	v := &chart.Config{Raw: "tags:\n  front-end: false\n\n"}
    63  	// expected charts including duplicates in alphanumeric order
    64  	e := []string{"parentchart"}
    65  
    66  	verifyRequirementsEnabled(t, c, v, e)
    67  }
    68  func TestRequirementsTagsEnabledL1(t *testing.T) {
    69  	c, err := Load("testdata/subpop")
    70  	if err != nil {
    71  		t.Fatalf("Failed to load testdata: %s", err)
    72  	}
    73  	// tags disabling a group and enabling a different group
    74  	v := &chart.Config{Raw: "tags:\n  front-end: false\n\n  back-end: true\n"}
    75  	// expected charts including duplicates in alphanumeric order
    76  	e := []string{"parentchart", "subchart2", "subchartb", "subchartc"}
    77  
    78  	verifyRequirementsEnabled(t, c, v, e)
    79  }
    80  
    81  func TestRequirementsTagsDisabledL2(t *testing.T) {
    82  	c, err := Load("testdata/subpop")
    83  	if err != nil {
    84  		t.Fatalf("Failed to load testdata: %s", err)
    85  	}
    86  	// tags disabling only children, children still enabled since tag front-end=true in values.yaml
    87  	v := &chart.Config{Raw: "tags:\n  subcharta: false\n\n  subchartb: false\n"}
    88  	// expected charts including duplicates in alphanumeric order
    89  	e := []string{"parentchart", "subchart1", "subcharta", "subchartb"}
    90  
    91  	verifyRequirementsEnabled(t, c, v, e)
    92  }
    93  func TestRequirementsTagsDisabledL1Mixed(t *testing.T) {
    94  	c, err := Load("testdata/subpop")
    95  	if err != nil {
    96  		t.Fatalf("Failed to load testdata: %s", err)
    97  	}
    98  	// tags disabling all parents/children with additional tag re-enabling a parent
    99  	v := &chart.Config{Raw: "tags:\n  front-end: false\n\n  subchart1: true\n\n  back-end: false\n"}
   100  	// expected charts including duplicates in alphanumeric order
   101  	e := []string{"parentchart", "subchart1"}
   102  
   103  	verifyRequirementsEnabled(t, c, v, e)
   104  }
   105  func TestRequirementsConditionsNonValue(t *testing.T) {
   106  	c, err := Load("testdata/subpop")
   107  	if err != nil {
   108  		t.Fatalf("Failed to load testdata: %s", err)
   109  	}
   110  	// tags with no effect
   111  	v := &chart.Config{Raw: "subchart1:\n  nothinguseful: false\n\n"}
   112  	// expected charts including duplicates in alphanumeric order
   113  	e := []string{"parentchart", "subchart1", "subcharta", "subchartb"}
   114  
   115  	verifyRequirementsEnabled(t, c, v, e)
   116  }
   117  func TestRequirementsConditionsEnabledL1Both(t *testing.T) {
   118  	c, err := Load("testdata/subpop")
   119  	if err != nil {
   120  		t.Fatalf("Failed to load testdata: %s", err)
   121  	}
   122  	// conditions enabling the parent charts, but back-end (b, c) is still disabled via values.yaml
   123  	v := &chart.Config{Raw: "subchart1:\n  enabled: true\nsubchart2:\n  enabled: true\n"}
   124  	// expected charts including duplicates in alphanumeric order
   125  	e := []string{"parentchart", "subchart1", "subchart2", "subcharta", "subchartb"}
   126  
   127  	verifyRequirementsEnabled(t, c, v, e)
   128  }
   129  func TestRequirementsConditionsDisabledL1Both(t *testing.T) {
   130  	c, err := Load("testdata/subpop")
   131  	if err != nil {
   132  		t.Fatalf("Failed to load testdata: %s", err)
   133  	}
   134  	// conditions disabling the parent charts, effectively disabling children
   135  	v := &chart.Config{Raw: "subchart1:\n  enabled: false\nsubchart2:\n  enabled: false\n"}
   136  	// expected charts including duplicates in alphanumeric order
   137  	e := []string{"parentchart"}
   138  
   139  	verifyRequirementsEnabled(t, c, v, e)
   140  }
   141  
   142  func TestRequirementsConditionsSecond(t *testing.T) {
   143  	c, err := Load("testdata/subpop")
   144  	if err != nil {
   145  		t.Fatalf("Failed to load testdata: %s", err)
   146  	}
   147  	// conditions a child using the second condition path of child's condition
   148  	v := &chart.Config{Raw: "subchart1:\n  subcharta:\n    enabled: false\n"}
   149  	// expected charts including duplicates in alphanumeric order
   150  	e := []string{"parentchart", "subchart1", "subchartb"}
   151  
   152  	verifyRequirementsEnabled(t, c, v, e)
   153  }
   154  func TestRequirementsCombinedDisabledL2(t *testing.T) {
   155  	c, err := Load("testdata/subpop")
   156  	if err != nil {
   157  		t.Fatalf("Failed to load testdata: %s", err)
   158  	}
   159  	// tags enabling a parent/child group with condition disabling one child
   160  	v := &chart.Config{Raw: "subchart2:\n  subchartc:\n    enabled: false\ntags:\n  back-end: true\n"}
   161  	// expected charts including duplicates in alphanumeric order
   162  	e := []string{"parentchart", "subchart1", "subchart2", "subcharta", "subchartb", "subchartb"}
   163  
   164  	verifyRequirementsEnabled(t, c, v, e)
   165  }
   166  func TestRequirementsCombinedDisabledL1(t *testing.T) {
   167  	c, err := Load("testdata/subpop")
   168  	if err != nil {
   169  		t.Fatalf("Failed to load testdata: %s", err)
   170  	}
   171  	// tags will not enable a child if parent is explicitly disabled with condition
   172  	v := &chart.Config{Raw: "subchart1:\n  enabled: false\ntags:\n  front-end: true\n"}
   173  	// expected charts including duplicates in alphanumeric order
   174  	e := []string{"parentchart"}
   175  
   176  	verifyRequirementsEnabled(t, c, v, e)
   177  }
   178  func TestRequirementsAliasCondition(t *testing.T) {
   179  	c, err := Load("testdata/subpop")
   180  	if err != nil {
   181  		t.Fatalf("Failed to load testdata: %s", err)
   182  	}
   183  	v := &chart.Config{Raw: "subchart1:\n  enabled: false\nsubchart2alias:\n  enabled: true\n  subchartb:\n    enabled: true\n"}
   184  	e := []string{"parentchart", "subchart2alias", "subchartb"}
   185  	verifyRequirementsEnabled(t, c, v, e)
   186  }
   187  
   188  func verifyRequirementsEnabled(t *testing.T, c *chart.Chart, v *chart.Config, e []string) {
   189  	out := []*chart.Chart{}
   190  	err := ProcessRequirementsEnabled(c, v)
   191  	if err != nil {
   192  		t.Errorf("Error processing enabled requirements %v", err)
   193  	}
   194  	out = extractCharts(c, out)
   195  	// build list of chart names
   196  	p := []string{}
   197  	for _, r := range out {
   198  		p = append(p, r.Metadata.Name)
   199  	}
   200  	//sort alphanumeric and compare to expectations
   201  	sort.Strings(p)
   202  	if len(p) != len(e) {
   203  		t.Errorf("Error slice lengths do not match got %v, expected %v", len(p), len(e))
   204  		return
   205  	}
   206  	for i := range p {
   207  		if p[i] != e[i] {
   208  			t.Errorf("Error slice values do not match got %v, expected %v", p[i], e[i])
   209  		}
   210  	}
   211  }
   212  
   213  // extractCharts recursively searches chart dependencies returning all charts found
   214  func extractCharts(c *chart.Chart, out []*chart.Chart) []*chart.Chart {
   215  
   216  	if len(c.Metadata.Name) > 0 {
   217  		out = append(out, c)
   218  	}
   219  	for _, d := range c.Dependencies {
   220  		out = extractCharts(d, out)
   221  	}
   222  	return out
   223  }
   224  func TestProcessRequirementsImportValues(t *testing.T) {
   225  	c, err := Load("testdata/subpop")
   226  	if err != nil {
   227  		t.Fatalf("Failed to load testdata: %s", err)
   228  	}
   229  
   230  	v := &chart.Config{Raw: ""}
   231  
   232  	e := make(map[string]string)
   233  
   234  	e["imported-chart1.SC1bool"] = "true"
   235  	e["imported-chart1.SC1float"] = "3.14"
   236  	e["imported-chart1.SC1int"] = "100"
   237  	e["imported-chart1.SC1string"] = "dollywood"
   238  	e["imported-chart1.SC1extra1"] = "11"
   239  	e["imported-chart1.SPextra1"] = "helm rocks"
   240  	e["imported-chart1.SC1extra1"] = "11"
   241  
   242  	e["imported-chartA.SCAbool"] = "false"
   243  	e["imported-chartA.SCAfloat"] = "3.1"
   244  	e["imported-chartA.SCAint"] = "55"
   245  	e["imported-chartA.SCAstring"] = "jabba"
   246  	e["imported-chartA.SPextra3"] = "1.337"
   247  	e["imported-chartA.SC1extra2"] = "1.337"
   248  	e["imported-chartA.SCAnested1.SCAnested2"] = "true"
   249  
   250  	e["imported-chartA-B.SCAbool"] = "false"
   251  	e["imported-chartA-B.SCAfloat"] = "3.1"
   252  	e["imported-chartA-B.SCAint"] = "55"
   253  	e["imported-chartA-B.SCAstring"] = "jabba"
   254  
   255  	e["imported-chartA-B.SCBbool"] = "true"
   256  	e["imported-chartA-B.SCBfloat"] = "7.77"
   257  	e["imported-chartA-B.SCBint"] = "33"
   258  	e["imported-chartA-B.SCBstring"] = "boba"
   259  	e["imported-chartA-B.SPextra5"] = "k8s"
   260  	e["imported-chartA-B.SC1extra5"] = "tiller"
   261  
   262  	e["overridden-chart1.SC1bool"] = "true"
   263  	e["overridden-chart1.SC1float"] = "3.14"
   264  	e["overridden-chart1.SC1int"] = "100"
   265  	e["overridden-chart1.SC1string"] = "dollywood"
   266  	e["overridden-chart1.SPextra2"] = "42"
   267  
   268  	e["overridden-chartA.SCAbool"] = "true"
   269  	e["overridden-chartA.SCAfloat"] = "41.3"
   270  	e["overridden-chartA.SCAint"] = "808"
   271  	e["overridden-chartA.SCAstring"] = "jaberwocky"
   272  	e["overridden-chartA.SPextra4"] = "true"
   273  
   274  	e["overridden-chartA-B.SCAbool"] = "true"
   275  	e["overridden-chartA-B.SCAfloat"] = "3.33"
   276  	e["overridden-chartA-B.SCAint"] = "555"
   277  	e["overridden-chartA-B.SCAstring"] = "wormwood"
   278  	e["overridden-chartA-B.SCBbool"] = "true"
   279  	e["overridden-chartA-B.SCBfloat"] = "0.25"
   280  	e["overridden-chartA-B.SCBint"] = "98"
   281  	e["overridden-chartA-B.SCBstring"] = "murkwood"
   282  	e["overridden-chartA-B.SPextra6"] = "111"
   283  	e["overridden-chartA-B.SCAextra1"] = "23"
   284  	e["overridden-chartA-B.SCBextra1"] = "13"
   285  	e["overridden-chartA-B.SC1extra6"] = "77"
   286  
   287  	// `exports` style
   288  	e["SCBexported1B"] = "1965"
   289  	e["SC1extra7"] = "true"
   290  	e["SCBexported2A"] = "blaster"
   291  	e["global.SC1exported2.all.SC1exported3"] = "SC1expstr"
   292  
   293  	e["SCCdata.SCCstring"] = "mugwort"
   294  	e["SCCdata.SCCint"] = "42"
   295  
   296  	verifyRequirementsImportValues(t, c, v, e)
   297  }
   298  func verifyRequirementsImportValues(t *testing.T, c *chart.Chart, v *chart.Config, e map[string]string) {
   299  
   300  	err := ProcessRequirementsImportValues(c)
   301  	if err != nil {
   302  		t.Errorf("Error processing import values requirements %v", err)
   303  	}
   304  	cv := c.GetValues()
   305  	cc, err := ReadValues([]byte(cv.Raw))
   306  	if err != nil {
   307  		t.Errorf("Error reading import values %v", err)
   308  	}
   309  	for kk, vv := range e {
   310  		pv, err := cc.PathValue(kk)
   311  		if err != nil {
   312  			t.Fatalf("Error retrieving import values table %v %v", kk, err)
   313  			return
   314  		}
   315  
   316  		switch pv.(type) {
   317  		case float64:
   318  			s := strconv.FormatFloat(pv.(float64), 'f', -1, 64)
   319  			if s != vv {
   320  				t.Errorf("Failed to match imported float field %v with value %v with expected %v", kk, s, vv)
   321  				return
   322  			}
   323  		case bool:
   324  			b := strconv.FormatBool(pv.(bool))
   325  			if b != vv {
   326  				t.Errorf("Failed to match imported bool field %v with value %v with expected %v", kk, b, vv)
   327  				return
   328  			}
   329  		default:
   330  			if pv.(string) != vv {
   331  				t.Errorf("Failed to match imported string field %v with value %v with expected %v", kk, pv, vv)
   332  				return
   333  			}
   334  		}
   335  
   336  	}
   337  }
   338  
   339  func TestGetAliasDependency(t *testing.T) {
   340  	c, err := Load("testdata/frobnitz")
   341  	if err != nil {
   342  		t.Fatalf("Failed to load testdata: %s", err)
   343  	}
   344  	req, err := LoadRequirements(c)
   345  	if err != nil {
   346  		t.Fatalf("Failed to load requirement for testdata: %s", err)
   347  	}
   348  	if len(req.Dependencies) == 0 {
   349  		t.Fatalf("There are no requirements to test")
   350  	}
   351  
   352  	// Success case
   353  	aliasChart := getAliasDependency(c.Dependencies, req.Dependencies[0])
   354  	if aliasChart == nil {
   355  		t.Fatalf("Failed to get dependency chart for alias %s", req.Dependencies[0].Name)
   356  	}
   357  	if req.Dependencies[0].Alias != "" {
   358  		if aliasChart.Metadata.Name != req.Dependencies[0].Alias {
   359  			t.Fatalf("Dependency chart name should be %s but got %s", req.Dependencies[0].Alias, aliasChart.Metadata.Name)
   360  		}
   361  	} else if aliasChart.Metadata.Name != req.Dependencies[0].Name {
   362  		t.Fatalf("Dependency chart name should be %s but got %s", req.Dependencies[0].Name, aliasChart.Metadata.Name)
   363  	}
   364  
   365  	if req.Dependencies[0].Version != "" {
   366  		if !version.IsCompatibleRange(req.Dependencies[0].Version, aliasChart.Metadata.Version) {
   367  			t.Fatalf("Dependency chart version is not in the compatible range")
   368  		}
   369  
   370  	}
   371  
   372  	// Failure case
   373  	req.Dependencies[0].Name = "something-else"
   374  	if aliasChart := getAliasDependency(c.Dependencies, req.Dependencies[0]); aliasChart != nil {
   375  		t.Fatalf("expected no chart but got %s", aliasChart.Metadata.Name)
   376  	}
   377  
   378  	req.Dependencies[0].Version = "something else which is not in the compatible range"
   379  	if version.IsCompatibleRange(req.Dependencies[0].Version, aliasChart.Metadata.Version) {
   380  		t.Fatalf("Dependency chart version which is not in the compatible range should cause a failure other than a success ")
   381  	}
   382  
   383  }
   384  
   385  func TestDependentChartAliases(t *testing.T) {
   386  	c, err := Load("testdata/dependent-chart-alias")
   387  	if err != nil {
   388  		t.Fatalf("Failed to load testdata: %s", err)
   389  	}
   390  
   391  	if len(c.Dependencies) == 0 {
   392  		t.Fatal("There are no dependencies to run this test")
   393  	}
   394  
   395  	origLength := len(c.Dependencies)
   396  	if err := ProcessRequirementsEnabled(c, c.Values); err != nil {
   397  		t.Fatalf("Expected no errors but got %q", err)
   398  	}
   399  
   400  	if len(c.Dependencies) == origLength {
   401  		t.Fatal("Expected alias dependencies to be added, but did not got that")
   402  	}
   403  
   404  	reqmts, err := LoadRequirements(c)
   405  	if err != nil {
   406  		t.Fatalf("Cannot load requirements for test chart, %v", err)
   407  	}
   408  
   409  	if len(c.Dependencies) != len(reqmts.Dependencies) {
   410  		t.Fatalf("Expected number of chart dependencies %d, but got %d", len(reqmts.Dependencies), len(c.Dependencies))
   411  	}
   412  
   413  }
   414  
   415  func TestDependentChartWithSubChartsAbsentInRequirements(t *testing.T) {
   416  	c, err := Load("testdata/dependent-chart-no-requirements-yaml")
   417  	if err != nil {
   418  		t.Fatalf("Failed to load testdata: %s", err)
   419  	}
   420  
   421  	if len(c.Dependencies) != 2 {
   422  		t.Fatalf("Expected 2 dependencies for this chart, but got %d", len(c.Dependencies))
   423  	}
   424  
   425  	origLength := len(c.Dependencies)
   426  	if err := ProcessRequirementsEnabled(c, c.Values); err != nil {
   427  		t.Fatalf("Expected no errors but got %q", err)
   428  	}
   429  
   430  	if len(c.Dependencies) != origLength {
   431  		t.Fatal("Expected no changes in dependencies to be, but did something got changed")
   432  	}
   433  
   434  }
   435  
   436  func TestDependentChartWithSubChartsHelmignore(t *testing.T) {
   437  	if _, err := Load("testdata/dependent-chart-helmignore"); err != nil {
   438  		t.Fatalf("Failed to load testdata: %s", err)
   439  	}
   440  }
   441  
   442  func TestDependentChartsWithSubChartsSymlink(t *testing.T) {
   443  	joonix := "testdata/joonix"
   444  	if err := os.Symlink(filepath.Join("..", "..", "frobnitz"), filepath.Join(joonix, "charts", "frobnitz")); err != nil {
   445  		t.Fatal(err)
   446  	}
   447  	defer os.RemoveAll(filepath.Join(joonix, "charts", "frobnitz"))
   448  	c, err := Load(joonix)
   449  	if err != nil {
   450  		t.Fatalf("Failed to load testdata: %s", err)
   451  	}
   452  	if c.Metadata.Name != "joonix" {
   453  		t.Fatalf("Unexpected chart name: %s", c.Metadata.Name)
   454  	}
   455  	if n := len(c.Dependencies); n != 1 {
   456  		t.Fatalf("Expected 1 dependency for this chart, but got %d", n)
   457  	}
   458  }
   459  
   460  func TestDependentChartsWithSubchartsAllSpecifiedInRequirements(t *testing.T) {
   461  	c, err := Load("testdata/dependent-chart-with-all-in-requirements-yaml")
   462  	if err != nil {
   463  		t.Fatalf("Failed to load testdata: %s", err)
   464  	}
   465  
   466  	if len(c.Dependencies) == 0 {
   467  		t.Fatal("There are no dependencies to run this test")
   468  	}
   469  
   470  	origLength := len(c.Dependencies)
   471  	if err := ProcessRequirementsEnabled(c, c.Values); err != nil {
   472  		t.Fatalf("Expected no errors but got %q", err)
   473  	}
   474  
   475  	if len(c.Dependencies) != origLength {
   476  		t.Fatal("Expected no changes in dependencies to be, but did something got changed")
   477  	}
   478  
   479  	reqmts, err := LoadRequirements(c)
   480  	if err != nil {
   481  		t.Fatalf("Cannot load requirements for test chart, %v", err)
   482  	}
   483  
   484  	if len(c.Dependencies) != len(reqmts.Dependencies) {
   485  		t.Fatalf("Expected number of chart dependencies %d, but got %d", len(reqmts.Dependencies), len(c.Dependencies))
   486  	}
   487  
   488  }
   489  
   490  func TestDependentChartsWithSomeSubchartsSpecifiedInRequirements(t *testing.T) {
   491  	c, err := Load("testdata/dependent-chart-with-mixed-requirements-yaml")
   492  	if err != nil {
   493  		t.Fatalf("Failed to load testdata: %s", err)
   494  	}
   495  
   496  	if len(c.Dependencies) == 0 {
   497  		t.Fatal("There are no dependencies to run this test")
   498  	}
   499  
   500  	origLength := len(c.Dependencies)
   501  	if err := ProcessRequirementsEnabled(c, c.Values); err != nil {
   502  		t.Fatalf("Expected no errors but got %q", err)
   503  	}
   504  
   505  	if len(c.Dependencies) != origLength {
   506  		t.Fatal("Expected no changes in dependencies to be, but did something got changed")
   507  	}
   508  
   509  	reqmts, err := LoadRequirements(c)
   510  	if err != nil {
   511  		t.Fatalf("Cannot load requirements for test chart, %v", err)
   512  	}
   513  
   514  	if len(c.Dependencies) <= len(reqmts.Dependencies) {
   515  		t.Fatalf("Expected more dependencies than specified in requirements.yaml(%d), but got %d", len(reqmts.Dependencies), len(c.Dependencies))
   516  	}
   517  
   518  }