github.com/amtisyAts/helm@v2.17.0+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  	resetName := req.Dependencies[0].Name
   374  	req.Dependencies[0].Name = "something-else"
   375  	if aliasChart := getAliasDependency(c.Dependencies, req.Dependencies[0]); aliasChart != nil {
   376  		t.Fatalf("expected no chart but got %s", aliasChart.Metadata.Name)
   377  	}
   378  
   379  	// Add a bad alias name
   380  	req.Dependencies[0].Name = resetName
   381  	req.Dependencies[0].Alias = "$foobar"
   382  	if aliasChart := getAliasDependency(c.Dependencies, req.Dependencies[0]); aliasChart != nil {
   383  		t.Fatalf("expected no chart but got %s", aliasChart.Metadata.Name)
   384  	}
   385  
   386  	req.Dependencies[0].Version = "something else which is not in the compatible range"
   387  	if version.IsCompatibleRange(req.Dependencies[0].Version, aliasChart.Metadata.Version) {
   388  		t.Fatalf("Dependency chart version which is not in the compatible range should cause a failure other than a success ")
   389  	}
   390  
   391  }
   392  
   393  func TestDependentChartAliases(t *testing.T) {
   394  	c, err := Load("testdata/dependent-chart-alias")
   395  	if err != nil {
   396  		t.Fatalf("Failed to load testdata: %s", err)
   397  	}
   398  
   399  	if len(c.Dependencies) == 0 {
   400  		t.Fatal("There are no dependencies to run this test")
   401  	}
   402  
   403  	origLength := len(c.Dependencies)
   404  	if err := ProcessRequirementsEnabled(c, c.Values); err != nil {
   405  		t.Fatalf("Expected no errors but got %q", err)
   406  	}
   407  
   408  	if len(c.Dependencies) == origLength {
   409  		t.Fatal("Expected alias dependencies to be added, but did not got that")
   410  	}
   411  
   412  	reqmts, err := LoadRequirements(c)
   413  	if err != nil {
   414  		t.Fatalf("Cannot load requirements for test chart, %v", err)
   415  	}
   416  
   417  	if len(c.Dependencies) != len(reqmts.Dependencies) {
   418  		t.Fatalf("Expected number of chart dependencies %d, but got %d", len(reqmts.Dependencies), len(c.Dependencies))
   419  	}
   420  
   421  }
   422  
   423  func TestDependentChartWithSubChartsAbsentInRequirements(t *testing.T) {
   424  	c, err := Load("testdata/dependent-chart-no-requirements-yaml")
   425  	if err != nil {
   426  		t.Fatalf("Failed to load testdata: %s", err)
   427  	}
   428  
   429  	if len(c.Dependencies) != 2 {
   430  		t.Fatalf("Expected 2 dependencies for this chart, but got %d", len(c.Dependencies))
   431  	}
   432  
   433  	origLength := len(c.Dependencies)
   434  	if err := ProcessRequirementsEnabled(c, c.Values); err != nil {
   435  		t.Fatalf("Expected no errors but got %q", err)
   436  	}
   437  
   438  	if len(c.Dependencies) != origLength {
   439  		t.Fatal("Expected no changes in dependencies to be, but did something got changed")
   440  	}
   441  
   442  }
   443  
   444  func TestDependentChartWithSubChartsHelmignore(t *testing.T) {
   445  	if _, err := Load("testdata/dependent-chart-helmignore"); err != nil {
   446  		t.Fatalf("Failed to load testdata: %s", err)
   447  	}
   448  }
   449  
   450  func TestDependentChartsWithSubChartsSymlink(t *testing.T) {
   451  	joonix := "testdata/joonix"
   452  	if err := os.Symlink(filepath.Join("..", "..", "frobnitz"), filepath.Join(joonix, "charts", "frobnitz")); err != nil {
   453  		t.Fatal(err)
   454  	}
   455  	defer os.RemoveAll(filepath.Join(joonix, "charts", "frobnitz"))
   456  	c, err := Load(joonix)
   457  	if err != nil {
   458  		t.Fatalf("Failed to load testdata: %s", err)
   459  	}
   460  	if c.Metadata.Name != "joonix" {
   461  		t.Fatalf("Unexpected chart name: %s", c.Metadata.Name)
   462  	}
   463  	if n := len(c.Dependencies); n != 1 {
   464  		t.Fatalf("Expected 1 dependency for this chart, but got %d", n)
   465  	}
   466  }
   467  
   468  func TestDependentChartsWithSubchartsAllSpecifiedInRequirements(t *testing.T) {
   469  	c, err := Load("testdata/dependent-chart-with-all-in-requirements-yaml")
   470  	if err != nil {
   471  		t.Fatalf("Failed to load testdata: %s", err)
   472  	}
   473  
   474  	if len(c.Dependencies) == 0 {
   475  		t.Fatal("There are no dependencies to run this test")
   476  	}
   477  
   478  	origLength := len(c.Dependencies)
   479  	if err := ProcessRequirementsEnabled(c, c.Values); err != nil {
   480  		t.Fatalf("Expected no errors but got %q", err)
   481  	}
   482  
   483  	if len(c.Dependencies) != origLength {
   484  		t.Fatal("Expected no changes in dependencies to be, but did something got changed")
   485  	}
   486  
   487  	reqmts, err := LoadRequirements(c)
   488  	if err != nil {
   489  		t.Fatalf("Cannot load requirements for test chart, %v", err)
   490  	}
   491  
   492  	if len(c.Dependencies) != len(reqmts.Dependencies) {
   493  		t.Fatalf("Expected number of chart dependencies %d, but got %d", len(reqmts.Dependencies), len(c.Dependencies))
   494  	}
   495  
   496  }
   497  
   498  func TestDependentChartsWithSomeSubchartsSpecifiedInRequirements(t *testing.T) {
   499  	c, err := Load("testdata/dependent-chart-with-mixed-requirements-yaml")
   500  	if err != nil {
   501  		t.Fatalf("Failed to load testdata: %s", err)
   502  	}
   503  
   504  	if len(c.Dependencies) == 0 {
   505  		t.Fatal("There are no dependencies to run this test")
   506  	}
   507  
   508  	origLength := len(c.Dependencies)
   509  	if err := ProcessRequirementsEnabled(c, c.Values); err != nil {
   510  		t.Fatalf("Expected no errors but got %q", err)
   511  	}
   512  
   513  	if len(c.Dependencies) != origLength {
   514  		t.Fatal("Expected no changes in dependencies to be, but did something got changed")
   515  	}
   516  
   517  	reqmts, err := LoadRequirements(c)
   518  	if err != nil {
   519  		t.Fatalf("Cannot load requirements for test chart, %v", err)
   520  	}
   521  
   522  	if len(c.Dependencies) <= len(reqmts.Dependencies) {
   523  		t.Fatalf("Expected more dependencies than specified in requirements.yaml(%d), but got %d", len(reqmts.Dependencies), len(c.Dependencies))
   524  	}
   525  
   526  }
   527  
   528  func TestAliasRegexp(t *testing.T) {
   529  	for name, shouldPass := range map[string]bool{
   530  		"abcdefghijklmnopqrstuvwxyzABCDEFG0987654321_-": true,
   531  		"$foo":     false,
   532  		"bar$":     false,
   533  		"foo\nbar": false,
   534  	} {
   535  		if aliasRegexp.MatchString(name) != shouldPass {
   536  			t.Errorf("name %q failed to pass its test", name)
   537  		}
   538  	}
   539  }