github.com/koderover/helm@v2.17.0+incompatible/pkg/chartutil/load_test.go (about)

     1  /*
     2  Copyright The Helm Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package chartutil
    18  
    19  import (
    20  	"archive/tar"
    21  	"compress/gzip"
    22  	"io/ioutil"
    23  	"os"
    24  	"path"
    25  	"path/filepath"
    26  	"runtime"
    27  	"strings"
    28  	"testing"
    29  	"time"
    30  
    31  	"k8s.io/helm/pkg/proto/hapi/chart"
    32  )
    33  
    34  func TestLoadDir(t *testing.T) {
    35  	c, err := Load("testdata/frobnitz")
    36  	if err != nil {
    37  		t.Fatalf("Failed to load testdata: %s", err)
    38  	}
    39  	verifyFrobnitz(t, c)
    40  	verifyChart(t, c)
    41  	verifyRequirements(t, c)
    42  }
    43  
    44  func TestLoadNonV1Chart(t *testing.T) {
    45  	_, err := Load("testdata/frobnitz.v2")
    46  	if err != nil {
    47  		if strings.Compare(err.Error(), "apiVersion 'v2' is not valid. The value must be \"v1\"") != 0 {
    48  			t.Errorf("Unexpected message: %s", err)
    49  		}
    50  		return
    51  	}
    52  	t.Fatalf("chart with v2 apiVersion should not load")
    53  }
    54  
    55  func TestLoadDirWithSymlinks(t *testing.T) {
    56  	sym := filepath.Join("..", "frobnitz", "README.md")
    57  	link := filepath.Join("testdata", "frobnitz_symlinks", "README.md")
    58  
    59  	if err := os.Symlink(sym, link); err != nil {
    60  		t.Fatal(err)
    61  	}
    62  
    63  	defer os.Remove(link)
    64  
    65  	c, err := Load("testdata/frobnitz_symlinks")
    66  	if err != nil {
    67  		t.Fatalf("Failed to load testdata: %s", err)
    68  	}
    69  	verifyFrobnitz(t, c)
    70  	verifyChart(t, c)
    71  	verifyRequirements(t, c)
    72  }
    73  
    74  func TestLoadDirWithBadSymlinks(t *testing.T) {
    75  	if runtime.GOOS == "windows" {
    76  		t.Skip("test only works on unix systems with /dev/null present")
    77  	}
    78  
    79  	_, err := Load("testdata/bad_symlink")
    80  	if err == nil {
    81  		t.Fatal("Failed to detect bad symlink")
    82  	}
    83  
    84  	if !strings.HasPrefix(err.Error(), "cannot load irregular file") {
    85  		t.Errorf("Expected bad symlink error got %q", err)
    86  	}
    87  }
    88  
    89  func TestLoadFile(t *testing.T) {
    90  	c, err := Load("testdata/frobnitz-1.2.3.tgz")
    91  	if err != nil {
    92  		t.Fatalf("Failed to load testdata: %s", err)
    93  	}
    94  	verifyFrobnitz(t, c)
    95  	verifyChart(t, c)
    96  	verifyRequirements(t, c)
    97  }
    98  
    99  func TestLoadArchive_InvalidArchive(t *testing.T) {
   100  	tmpdir, err := ioutil.TempDir("", "helm-test-")
   101  	if err != nil {
   102  		t.Fatal(err)
   103  	}
   104  	defer os.Remove(tmpdir)
   105  
   106  	writeTar := func(filename, internalPath string, body []byte) {
   107  		dest, err := os.Create(filename)
   108  		if err != nil {
   109  			t.Fatal(err)
   110  		}
   111  		zipper := gzip.NewWriter(dest)
   112  		tw := tar.NewWriter(zipper)
   113  
   114  		h := &tar.Header{
   115  			Name:    internalPath,
   116  			Mode:    0755,
   117  			Size:    int64(len(body)),
   118  			ModTime: time.Now(),
   119  		}
   120  		if err := tw.WriteHeader(h); err != nil {
   121  			t.Fatal(err)
   122  		}
   123  		if _, err := tw.Write(body); err != nil {
   124  			t.Fatal(err)
   125  		}
   126  		tw.Close()
   127  		zipper.Close()
   128  		dest.Close()
   129  	}
   130  
   131  	for _, tt := range []struct {
   132  		chartname   string
   133  		internal    string
   134  		expectError string
   135  	}{
   136  		{"illegal-dots.tgz", "../../malformed-helm-test", "chart illegally references parent directory"},
   137  		{"illegal-dots2.tgz", "/foo/../../malformed-helm-test", "chart illegally references parent directory"},
   138  		{"illegal-dots3.tgz", "/../../malformed-helm-test", "chart illegally references parent directory"},
   139  		{"illegal-dots4.tgz", "./../../malformed-helm-test", "chart illegally references parent directory"},
   140  		{"illegal-name.tgz", "./.", "chart illegally contains content outside the base directory: \"./.\""},
   141  		{"illegal-name2.tgz", "/./.", "chart illegally contains content outside the base directory: \"/./.\""},
   142  		{"illegal-name3.tgz", "missing-leading-slash", "chart illegally contains content outside the base directory: \"missing-leading-slash\""},
   143  		{"illegal-name5.tgz", "content-outside-base-dir", "chart illegally contains content outside the base directory: \"content-outside-base-dir\""},
   144  		{"illegal-name4.tgz", "/missing-leading-slash", "chart metadata (Chart.yaml) missing"},
   145  		{"illegal-abspath.tgz", "//foo", "chart illegally contains absolute paths"},
   146  		{"illegal-abspath2.tgz", "///foo", "chart illegally contains absolute paths"},
   147  		{"illegal-abspath3.tgz", "\\\\foo", "chart illegally contains absolute paths"},
   148  		{"illegal-abspath3.tgz", "\\..\\..\\foo", "chart illegally references parent directory"},
   149  
   150  		// Under special circumstances, this can get normalized to things that look like absolute Windows paths
   151  		{"illegal-abspath4.tgz", "\\.\\c:\\\\foo", "chart contains illegally named files"},
   152  		{"illegal-abspath5.tgz", "/./c://foo", "chart contains illegally named files"},
   153  		{"illegal-abspath6.tgz", "\\\\?\\Some\\windows\\magic", "chart illegally contains absolute paths"},
   154  	} {
   155  		illegalChart := filepath.Join(tmpdir, tt.chartname)
   156  		writeTar(illegalChart, tt.internal, []byte("hello: world"))
   157  		_, err = Load(illegalChart)
   158  		if err == nil {
   159  			t.Fatal("expected error when unpacking illegal files")
   160  		}
   161  		if err.Error() != tt.expectError {
   162  			t.Errorf("Expected %q, got %q for %s", tt.expectError, err.Error(), tt.chartname)
   163  		}
   164  	}
   165  
   166  	// Make sure that absolute path gets interpreted as relative
   167  	illegalChart := filepath.Join(tmpdir, "abs-path.tgz")
   168  	writeTar(illegalChart, "/Chart.yaml", []byte("hello: world"))
   169  	_, err = Load(illegalChart)
   170  	if err.Error() != "invalid chart (Chart.yaml): name must not be empty" {
   171  		t.Error(err)
   172  	}
   173  
   174  	// And just to validate that the above was not spurious
   175  	illegalChart = filepath.Join(tmpdir, "abs-path2.tgz")
   176  	writeTar(illegalChart, "files/whatever.yaml", []byte("hello: world"))
   177  	_, err = Load(illegalChart)
   178  	if err.Error() != "chart metadata (Chart.yaml) missing" {
   179  		t.Error(err)
   180  	}
   181  
   182  	// Finally, test that drive letter gets stripped off on Windows
   183  	illegalChart = filepath.Join(tmpdir, "abs-winpath.tgz")
   184  	writeTar(illegalChart, "c:\\Chart.yaml", []byte("hello: world"))
   185  	_, err = Load(illegalChart)
   186  	if err.Error() != "invalid chart (Chart.yaml): name must not be empty" {
   187  		t.Error(err)
   188  	}
   189  }
   190  
   191  func TestLoadFiles(t *testing.T) {
   192  	goodFiles := []*BufferedFile{
   193  		{
   194  			Name: ChartfileName,
   195  			Data: []byte(`apiVersion: v1
   196  name: frobnitz
   197  description: This is a frobnitz.
   198  version: "1.2.3"
   199  keywords:
   200    - frobnitz
   201    - sprocket
   202    - dodad
   203  maintainers:
   204    - name: The Helm Team
   205      email: helm@example.com
   206    - name: Someone Else
   207      email: nobody@example.com
   208  sources:
   209    - https://example.com/foo/bar
   210  home: http://example.com
   211  icon: https://example.com/64x64.png
   212  `),
   213  		},
   214  		{
   215  			Name: ValuesfileName,
   216  			Data: []byte(defaultValues),
   217  		},
   218  		{
   219  			Name: path.Join("templates", DeploymentName),
   220  			Data: []byte(defaultDeployment),
   221  		},
   222  		{
   223  			Name: path.Join("templates", ServiceName),
   224  			Data: []byte(defaultService),
   225  		},
   226  	}
   227  
   228  	c, err := LoadFiles(goodFiles)
   229  	if err != nil {
   230  		t.Errorf("Expected good files to be loaded, got %v", err)
   231  	}
   232  
   233  	if c.Metadata.Name != "frobnitz" {
   234  		t.Errorf("Expected chart name to be 'frobnitz', got %s", c.Metadata.Name)
   235  	}
   236  
   237  	if c.Values.Raw != defaultValues {
   238  		t.Error("Expected chart values to be populated with default values")
   239  	}
   240  
   241  	if len(c.Templates) != 2 {
   242  		t.Errorf("Expected number of templates == 2, got %d", len(c.Templates))
   243  	}
   244  
   245  	c, err = LoadFiles([]*BufferedFile{})
   246  	if err == nil {
   247  		t.Fatal("Expected err to be non-nil")
   248  	}
   249  	if err.Error() != "chart metadata (Chart.yaml) missing" {
   250  		t.Errorf("Expected chart metadata missing error, got '%s'", err.Error())
   251  	}
   252  
   253  	// legacy check
   254  	c, err = LoadFiles([]*BufferedFile{
   255  		{
   256  			Name: "values.toml",
   257  			Data: []byte{},
   258  		},
   259  	})
   260  	if err == nil {
   261  		t.Fatal("Expected err to be non-nil")
   262  	}
   263  	if err.Error() != "values.toml is illegal as of 2.0.0-alpha.2" {
   264  		t.Errorf("Expected values.toml to be illegal, got '%s'", err.Error())
   265  	}
   266  }
   267  
   268  // Packaging the chart on a Windows machine will produce an
   269  // archive that has \\ as delimiters. Test that we support these archives
   270  func TestLoadFileBackslash(t *testing.T) {
   271  	c, err := Load("testdata/frobnitz_backslash-1.2.3.tgz")
   272  	if err != nil {
   273  		t.Fatalf("Failed to load testdata: %s", err)
   274  	}
   275  	verifyChartFileAndTemplate(t, c, "frobnitz_backslash")
   276  	verifyChart(t, c)
   277  	verifyRequirements(t, c)
   278  }
   279  
   280  func verifyChart(t *testing.T, c *chart.Chart) {
   281  	if c.Metadata.Name == "" {
   282  		t.Fatalf("No chart metadata found on %v", c)
   283  	}
   284  	t.Logf("Verifying chart %s", c.Metadata.Name)
   285  	if len(c.Templates) != 1 {
   286  		t.Errorf("Expected 1 template, got %d", len(c.Templates))
   287  	}
   288  
   289  	numfiles := 8
   290  	if len(c.Files) != numfiles {
   291  		t.Errorf("Expected %d extra files, got %d", numfiles, len(c.Files))
   292  		for _, n := range c.Files {
   293  			t.Logf("\t%s", n.TypeUrl)
   294  		}
   295  	}
   296  
   297  	if len(c.Dependencies) != 2 {
   298  		t.Errorf("Expected 2 dependencies, got %d (%v)", len(c.Dependencies), c.Dependencies)
   299  		for _, d := range c.Dependencies {
   300  			t.Logf("\tSubchart: %s\n", d.Metadata.Name)
   301  		}
   302  	}
   303  
   304  	expect := map[string]map[string]string{
   305  		"alpine": {
   306  			"version": "0.1.0",
   307  		},
   308  		"mariner": {
   309  			"version": "4.3.2",
   310  		},
   311  	}
   312  
   313  	for _, dep := range c.Dependencies {
   314  		if dep.Metadata == nil {
   315  			t.Fatalf("expected metadata on dependency: %v", dep)
   316  		}
   317  		exp, ok := expect[dep.Metadata.Name]
   318  		if !ok {
   319  			t.Fatalf("Unknown dependency %s", dep.Metadata.Name)
   320  		}
   321  		if exp["version"] != dep.Metadata.Version {
   322  			t.Errorf("Expected %s version %s, got %s", dep.Metadata.Name, exp["version"], dep.Metadata.Version)
   323  		}
   324  	}
   325  
   326  }
   327  
   328  func verifyRequirements(t *testing.T, c *chart.Chart) {
   329  	r, err := LoadRequirements(c)
   330  	if err != nil {
   331  		t.Fatal(err)
   332  	}
   333  	if len(r.Dependencies) != 2 {
   334  		t.Errorf("Expected 2 requirements, got %d", len(r.Dependencies))
   335  	}
   336  	tests := []*Dependency{
   337  		{Name: "alpine", Version: "0.1.0", Repository: "https://example.com/charts"},
   338  		{Name: "mariner", Version: "4.3.2", Repository: "https://example.com/charts"},
   339  	}
   340  	for i, tt := range tests {
   341  		d := r.Dependencies[i]
   342  		if d.Name != tt.Name {
   343  			t.Errorf("Expected dependency named %q, got %q", tt.Name, d.Name)
   344  		}
   345  		if d.Version != tt.Version {
   346  			t.Errorf("Expected dependency named %q to have version %q, got %q", tt.Name, tt.Version, d.Version)
   347  		}
   348  		if d.Repository != tt.Repository {
   349  			t.Errorf("Expected dependency named %q to have repository %q, got %q", tt.Name, tt.Repository, d.Repository)
   350  		}
   351  	}
   352  }
   353  func verifyRequirementsLock(t *testing.T, c *chart.Chart) {
   354  	r, err := LoadRequirementsLock(c)
   355  	if err != nil {
   356  		t.Fatal(err)
   357  	}
   358  	if len(r.Dependencies) != 2 {
   359  		t.Errorf("Expected 2 requirements, got %d", len(r.Dependencies))
   360  	}
   361  	tests := []*Dependency{
   362  		{Name: "alpine", Version: "0.1.0", Repository: "https://example.com/charts"},
   363  		{Name: "mariner", Version: "4.3.2", Repository: "https://example.com/charts"},
   364  	}
   365  	for i, tt := range tests {
   366  		d := r.Dependencies[i]
   367  		if d.Name != tt.Name {
   368  			t.Errorf("Expected dependency named %q, got %q", tt.Name, d.Name)
   369  		}
   370  		if d.Version != tt.Version {
   371  			t.Errorf("Expected dependency named %q to have version %q, got %q", tt.Name, tt.Version, d.Version)
   372  		}
   373  		if d.Repository != tt.Repository {
   374  			t.Errorf("Expected dependency named %q to have repository %q, got %q", tt.Name, tt.Repository, d.Repository)
   375  		}
   376  	}
   377  }
   378  
   379  func verifyFrobnitz(t *testing.T, c *chart.Chart) {
   380  	verifyChartFileAndTemplate(t, c, "frobnitz")
   381  }
   382  
   383  func verifyChartFileAndTemplate(t *testing.T, c *chart.Chart, name string) {
   384  
   385  	verifyChartfile(t, c.Metadata, name)
   386  
   387  	if len(c.Templates) != 1 {
   388  		t.Fatalf("Expected 1 template, got %d", len(c.Templates))
   389  	}
   390  
   391  	if c.Templates[0].Name != "templates/template.tpl" {
   392  		t.Errorf("Unexpected template: %s", c.Templates[0].Name)
   393  	}
   394  
   395  	if len(c.Templates[0].Data) == 0 {
   396  		t.Error("No template data.")
   397  	}
   398  }