github.com/Beeketing/helm@v2.12.1+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: "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  
   179  func verifyRequirementsEnabled(t *testing.T, c *chart.Chart, v *chart.Config, e []string) {
   180  	out := []*chart.Chart{}
   181  	err := ProcessRequirementsEnabled(c, v)
   182  	if err != nil {
   183  		t.Errorf("Error processing enabled requirements %v", err)
   184  	}
   185  	out = extractCharts(c, out)
   186  	// build list of chart names
   187  	p := []string{}
   188  	for _, r := range out {
   189  		p = append(p, r.Metadata.Name)
   190  	}
   191  	//sort alphanumeric and compare to expectations
   192  	sort.Strings(p)
   193  	if len(p) != len(e) {
   194  		t.Errorf("Error slice lengths do not match got %v, expected %v", len(p), len(e))
   195  		return
   196  	}
   197  	for i := range p {
   198  		if p[i] != e[i] {
   199  			t.Errorf("Error slice values do not match got %v, expected %v", p[i], e[i])
   200  		}
   201  	}
   202  }
   203  
   204  // extractCharts recursively searches chart dependencies returning all charts found
   205  func extractCharts(c *chart.Chart, out []*chart.Chart) []*chart.Chart {
   206  
   207  	if len(c.Metadata.Name) > 0 {
   208  		out = append(out, c)
   209  	}
   210  	for _, d := range c.Dependencies {
   211  		out = extractCharts(d, out)
   212  	}
   213  	return out
   214  }
   215  func TestProcessRequirementsImportValues(t *testing.T) {
   216  	c, err := Load("testdata/subpop")
   217  	if err != nil {
   218  		t.Fatalf("Failed to load testdata: %s", err)
   219  	}
   220  
   221  	v := &chart.Config{Raw: ""}
   222  
   223  	e := make(map[string]string)
   224  
   225  	e["imported-chart1.SC1bool"] = "true"
   226  	e["imported-chart1.SC1float"] = "3.14"
   227  	e["imported-chart1.SC1int"] = "100"
   228  	e["imported-chart1.SC1string"] = "dollywood"
   229  	e["imported-chart1.SC1extra1"] = "11"
   230  	e["imported-chart1.SPextra1"] = "helm rocks"
   231  	e["imported-chart1.SC1extra1"] = "11"
   232  
   233  	e["imported-chartA.SCAbool"] = "false"
   234  	e["imported-chartA.SCAfloat"] = "3.1"
   235  	e["imported-chartA.SCAint"] = "55"
   236  	e["imported-chartA.SCAstring"] = "jabba"
   237  	e["imported-chartA.SPextra3"] = "1.337"
   238  	e["imported-chartA.SC1extra2"] = "1.337"
   239  	e["imported-chartA.SCAnested1.SCAnested2"] = "true"
   240  
   241  	e["imported-chartA-B.SCAbool"] = "false"
   242  	e["imported-chartA-B.SCAfloat"] = "3.1"
   243  	e["imported-chartA-B.SCAint"] = "55"
   244  	e["imported-chartA-B.SCAstring"] = "jabba"
   245  
   246  	e["imported-chartA-B.SCBbool"] = "true"
   247  	e["imported-chartA-B.SCBfloat"] = "7.77"
   248  	e["imported-chartA-B.SCBint"] = "33"
   249  	e["imported-chartA-B.SCBstring"] = "boba"
   250  	e["imported-chartA-B.SPextra5"] = "k8s"
   251  	e["imported-chartA-B.SC1extra5"] = "tiller"
   252  
   253  	e["overridden-chart1.SC1bool"] = "false"
   254  	e["overridden-chart1.SC1float"] = "3.141592"
   255  	e["overridden-chart1.SC1int"] = "99"
   256  	e["overridden-chart1.SC1string"] = "pollywog"
   257  	e["overridden-chart1.SPextra2"] = "42"
   258  
   259  	e["overridden-chartA.SCAbool"] = "true"
   260  	e["overridden-chartA.SCAfloat"] = "41.3"
   261  	e["overridden-chartA.SCAint"] = "808"
   262  	e["overridden-chartA.SCAstring"] = "jaberwocky"
   263  	e["overridden-chartA.SPextra4"] = "true"
   264  
   265  	e["overridden-chartA-B.SCAbool"] = "true"
   266  	e["overridden-chartA-B.SCAfloat"] = "41.3"
   267  	e["overridden-chartA-B.SCAint"] = "808"
   268  	e["overridden-chartA-B.SCAstring"] = "jaberwocky"
   269  	e["overridden-chartA-B.SCBbool"] = "false"
   270  	e["overridden-chartA-B.SCBfloat"] = "1.99"
   271  	e["overridden-chartA-B.SCBint"] = "77"
   272  	e["overridden-chartA-B.SCBstring"] = "jango"
   273  	e["overridden-chartA-B.SPextra6"] = "111"
   274  	e["overridden-chartA-B.SCAextra1"] = "23"
   275  	e["overridden-chartA-B.SCBextra1"] = "13"
   276  	e["overridden-chartA-B.SC1extra6"] = "77"
   277  
   278  	// `exports` style
   279  	e["SCBexported1B"] = "1965"
   280  	e["SC1extra7"] = "true"
   281  	e["SCBexported2A"] = "blaster"
   282  	e["global.SC1exported2.all.SC1exported3"] = "SC1expstr"
   283  
   284  	verifyRequirementsImportValues(t, c, v, e)
   285  }
   286  func verifyRequirementsImportValues(t *testing.T, c *chart.Chart, v *chart.Config, e map[string]string) {
   287  
   288  	err := ProcessRequirementsImportValues(c)
   289  	if err != nil {
   290  		t.Errorf("Error processing import values requirements %v", err)
   291  	}
   292  	cv := c.GetValues()
   293  	cc, err := ReadValues([]byte(cv.Raw))
   294  	if err != nil {
   295  		t.Errorf("Error reading import values %v", err)
   296  	}
   297  	for kk, vv := range e {
   298  		pv, err := cc.PathValue(kk)
   299  		if err != nil {
   300  			t.Fatalf("Error retrieving import values table %v %v", kk, err)
   301  			return
   302  		}
   303  
   304  		switch pv.(type) {
   305  		case float64:
   306  			s := strconv.FormatFloat(pv.(float64), 'f', -1, 64)
   307  			if s != vv {
   308  				t.Errorf("Failed to match imported float value %v with expected %v", s, vv)
   309  				return
   310  			}
   311  		case bool:
   312  			b := strconv.FormatBool(pv.(bool))
   313  			if b != vv {
   314  				t.Errorf("Failed to match imported bool value %v with expected %v", b, vv)
   315  				return
   316  			}
   317  		default:
   318  			if pv.(string) != vv {
   319  				t.Errorf("Failed to match imported string value %v with expected %v", pv, vv)
   320  				return
   321  			}
   322  		}
   323  
   324  	}
   325  }
   326  
   327  func TestGetAliasDependency(t *testing.T) {
   328  	c, err := Load("testdata/frobnitz")
   329  	if err != nil {
   330  		t.Fatalf("Failed to load testdata: %s", err)
   331  	}
   332  	req, err := LoadRequirements(c)
   333  	if err != nil {
   334  		t.Fatalf("Failed to load requirement for testdata: %s", err)
   335  	}
   336  	if len(req.Dependencies) == 0 {
   337  		t.Fatalf("There are no requirements to test")
   338  	}
   339  
   340  	// Success case
   341  	aliasChart := getAliasDependency(c.Dependencies, req.Dependencies[0])
   342  	if aliasChart == nil {
   343  		t.Fatalf("Failed to get dependency chart for alias %s", req.Dependencies[0].Name)
   344  	}
   345  	if req.Dependencies[0].Alias != "" {
   346  		if aliasChart.Metadata.Name != req.Dependencies[0].Alias {
   347  			t.Fatalf("Dependency chart name should be %s but got %s", req.Dependencies[0].Alias, aliasChart.Metadata.Name)
   348  		}
   349  	} else if aliasChart.Metadata.Name != req.Dependencies[0].Name {
   350  		t.Fatalf("Dependency chart name should be %s but got %s", req.Dependencies[0].Name, aliasChart.Metadata.Name)
   351  	}
   352  
   353  	if req.Dependencies[0].Version != "" {
   354  		if !version.IsCompatibleRange(req.Dependencies[0].Version, aliasChart.Metadata.Version) {
   355  			t.Fatalf("Dependency chart version is not in the compatible range")
   356  		}
   357  
   358  	}
   359  
   360  	// Failure case
   361  	req.Dependencies[0].Name = "something-else"
   362  	if aliasChart := getAliasDependency(c.Dependencies, req.Dependencies[0]); aliasChart != nil {
   363  		t.Fatalf("expected no chart but got %s", aliasChart.Metadata.Name)
   364  	}
   365  
   366  	req.Dependencies[0].Version = "something else which is not in the compatible range"
   367  	if version.IsCompatibleRange(req.Dependencies[0].Version, aliasChart.Metadata.Version) {
   368  		t.Fatalf("Dependency chart version which is not in the compatible range should cause a failure other than a success ")
   369  	}
   370  
   371  }
   372  
   373  func TestDependentChartAliases(t *testing.T) {
   374  	c, err := Load("testdata/dependent-chart-alias")
   375  	if err != nil {
   376  		t.Fatalf("Failed to load testdata: %s", err)
   377  	}
   378  
   379  	if len(c.Dependencies) == 0 {
   380  		t.Fatal("There are no dependencies to run this test")
   381  	}
   382  
   383  	origLength := len(c.Dependencies)
   384  	if err := ProcessRequirementsEnabled(c, c.Values); err != nil {
   385  		t.Fatalf("Expected no errors but got %q", err)
   386  	}
   387  
   388  	if len(c.Dependencies) == origLength {
   389  		t.Fatal("Expected alias dependencies to be added, but did not got that")
   390  	}
   391  
   392  	reqmts, err := LoadRequirements(c)
   393  	if err != nil {
   394  		t.Fatalf("Cannot load requirements for test chart, %v", err)
   395  	}
   396  
   397  	if len(c.Dependencies) != len(reqmts.Dependencies) {
   398  		t.Fatalf("Expected number of chart dependencies %d, but got %d", len(reqmts.Dependencies), len(c.Dependencies))
   399  	}
   400  
   401  }
   402  
   403  func TestDependentChartWithSubChartsAbsentInRequirements(t *testing.T) {
   404  	c, err := Load("testdata/dependent-chart-no-requirements-yaml")
   405  	if err != nil {
   406  		t.Fatalf("Failed to load testdata: %s", err)
   407  	}
   408  
   409  	if len(c.Dependencies) != 2 {
   410  		t.Fatalf("Expected 2 dependencies for this chart, but got %d", len(c.Dependencies))
   411  	}
   412  
   413  	origLength := len(c.Dependencies)
   414  	if err := ProcessRequirementsEnabled(c, c.Values); err != nil {
   415  		t.Fatalf("Expected no errors but got %q", err)
   416  	}
   417  
   418  	if len(c.Dependencies) != origLength {
   419  		t.Fatal("Expected no changes in dependencies to be, but did something got changed")
   420  	}
   421  
   422  }
   423  
   424  func TestDependentChartWithSubChartsHelmignore(t *testing.T) {
   425  	if _, err := Load("testdata/dependent-chart-helmignore"); err != nil {
   426  		t.Fatalf("Failed to load testdata: %s", err)
   427  	}
   428  }
   429  
   430  func TestDependentChartsWithSubChartsSymlink(t *testing.T) {
   431  	joonix := "testdata/joonix"
   432  	if err := os.Symlink(filepath.Join("..", "..", "frobnitz"), filepath.Join(joonix, "charts", "frobnitz")); err != nil {
   433  		t.Fatal(err)
   434  	}
   435  	defer os.RemoveAll(filepath.Join(joonix, "charts", "frobnitz"))
   436  	c, err := Load(joonix)
   437  	if err != nil {
   438  		t.Fatalf("Failed to load testdata: %s", err)
   439  	}
   440  	if c.Metadata.Name != "joonix" {
   441  		t.Fatalf("Unexpected chart name: %s", c.Metadata.Name)
   442  	}
   443  	if n := len(c.Dependencies); n != 1 {
   444  		t.Fatalf("Expected 1 dependency for this chart, but got %d", n)
   445  	}
   446  }
   447  
   448  func TestDependentChartsWithSubchartsAllSpecifiedInRequirements(t *testing.T) {
   449  	c, err := Load("testdata/dependent-chart-with-all-in-requirements-yaml")
   450  	if err != nil {
   451  		t.Fatalf("Failed to load testdata: %s", err)
   452  	}
   453  
   454  	if len(c.Dependencies) == 0 {
   455  		t.Fatal("There are no dependencies to run this test")
   456  	}
   457  
   458  	origLength := len(c.Dependencies)
   459  	if err := ProcessRequirementsEnabled(c, c.Values); err != nil {
   460  		t.Fatalf("Expected no errors but got %q", err)
   461  	}
   462  
   463  	if len(c.Dependencies) != origLength {
   464  		t.Fatal("Expected no changes in dependencies to be, but did something got changed")
   465  	}
   466  
   467  	reqmts, err := LoadRequirements(c)
   468  	if err != nil {
   469  		t.Fatalf("Cannot load requirements for test chart, %v", err)
   470  	}
   471  
   472  	if len(c.Dependencies) != len(reqmts.Dependencies) {
   473  		t.Fatalf("Expected number of chart dependencies %d, but got %d", len(reqmts.Dependencies), len(c.Dependencies))
   474  	}
   475  
   476  }
   477  
   478  func TestDependentChartsWithSomeSubchartsSpecifiedInRequirements(t *testing.T) {
   479  	c, err := Load("testdata/dependent-chart-with-mixed-requirements-yaml")
   480  	if err != nil {
   481  		t.Fatalf("Failed to load testdata: %s", err)
   482  	}
   483  
   484  	if len(c.Dependencies) == 0 {
   485  		t.Fatal("There are no dependencies to run this test")
   486  	}
   487  
   488  	origLength := len(c.Dependencies)
   489  	if err := ProcessRequirementsEnabled(c, c.Values); err != nil {
   490  		t.Fatalf("Expected no errors but got %q", err)
   491  	}
   492  
   493  	if len(c.Dependencies) != origLength {
   494  		t.Fatal("Expected no changes in dependencies to be, but did something got changed")
   495  	}
   496  
   497  	reqmts, err := LoadRequirements(c)
   498  	if err != nil {
   499  		t.Fatalf("Cannot load requirements for test chart, %v", err)
   500  	}
   501  
   502  	if len(c.Dependencies) <= len(reqmts.Dependencies) {
   503  		t.Fatalf("Expected more dependencies than specified in requirements.yaml(%d), but got %d", len(reqmts.Dependencies), len(c.Dependencies))
   504  	}
   505  
   506  }