github.com/hiddeco/helm@v3.0.0-beta.3+incompatible/cmd/helm/package_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  
    16  package main
    17  
    18  import (
    19  	"bytes"
    20  	"fmt"
    21  	"io/ioutil"
    22  	"os"
    23  	"path/filepath"
    24  	"regexp"
    25  	"runtime"
    26  	"strings"
    27  	"testing"
    28  
    29  	"github.com/spf13/cobra"
    30  
    31  	"helm.sh/helm/internal/test/ensure"
    32  	"helm.sh/helm/pkg/chart"
    33  	"helm.sh/helm/pkg/chart/loader"
    34  	"helm.sh/helm/pkg/chartutil"
    35  )
    36  
    37  func TestPackage(t *testing.T) {
    38  	statFileMsg := "no such file or directory"
    39  	if runtime.GOOS == "windows" {
    40  		statFileMsg = "The system cannot find the file specified."
    41  	}
    42  
    43  	tests := []struct {
    44  		name    string
    45  		flags   map[string]string
    46  		args    []string
    47  		expect  string
    48  		hasfile string
    49  		err     bool
    50  	}{
    51  		{
    52  			name:   "package without chart path",
    53  			args:   []string{},
    54  			flags:  map[string]string{},
    55  			expect: "need at least one argument, the path to the chart",
    56  			err:    true,
    57  		},
    58  		{
    59  			name:   "package --sign, no --key",
    60  			args:   []string{"testdata/testcharts/alpine"},
    61  			flags:  map[string]string{"sign": "1"},
    62  			expect: "key is required for signing a package",
    63  			err:    true,
    64  		},
    65  		{
    66  			name:   "package --sign, no --keyring",
    67  			args:   []string{"testdata/testcharts/alpine"},
    68  			flags:  map[string]string{"sign": "1", "key": "nosuchkey", "keyring": ""},
    69  			expect: "keyring is required for signing a package",
    70  			err:    true,
    71  		},
    72  		{
    73  			name:    "package testdata/testcharts/alpine, no save",
    74  			args:    []string{"testdata/testcharts/alpine"},
    75  			flags:   map[string]string{"save": "0"},
    76  			expect:  "",
    77  			hasfile: "alpine-0.1.0.tgz",
    78  		},
    79  		{
    80  			name:    "package testdata/testcharts/alpine",
    81  			args:    []string{"testdata/testcharts/alpine"},
    82  			expect:  "",
    83  			hasfile: "alpine-0.1.0.tgz",
    84  		},
    85  		{
    86  			name:    "package testdata/testcharts/issue1979",
    87  			args:    []string{"testdata/testcharts/issue1979"},
    88  			expect:  "",
    89  			hasfile: "alpine-0.1.0.tgz",
    90  		},
    91  		{
    92  			name:    "package --destination toot",
    93  			args:    []string{"testdata/testcharts/alpine"},
    94  			flags:   map[string]string{"destination": "toot"},
    95  			expect:  "",
    96  			hasfile: "toot/alpine-0.1.0.tgz",
    97  		},
    98  		{
    99  			name:    "package --sign --key=KEY --keyring=KEYRING testdata/testcharts/alpine",
   100  			args:    []string{"testdata/testcharts/alpine"},
   101  			flags:   map[string]string{"sign": "1", "keyring": "testdata/helm-test-key.secret", "key": "helm-test"},
   102  			expect:  "",
   103  			hasfile: "alpine-0.1.0.tgz",
   104  		},
   105  		{
   106  			name:    "package testdata/testcharts/chart-missing-deps",
   107  			args:    []string{"testdata/testcharts/chart-missing-deps"},
   108  			hasfile: "chart-missing-deps-0.1.0.tgz",
   109  			err:     true,
   110  		},
   111  		{
   112  			name:   "package --values does-not-exist",
   113  			args:   []string{"testdata/testcharts/alpine"},
   114  			flags:  map[string]string{"values": "does-not-exist"},
   115  			expect: fmt.Sprintf("does-not-exist: %s", statFileMsg),
   116  			err:    true,
   117  		},
   118  		{
   119  			name: "package testdata/testcharts/chart-bad-type",
   120  			args: []string{"testdata/testcharts/chart-bad-type"},
   121  			err:  true,
   122  		},
   123  	}
   124  
   125  	origDir, err := os.Getwd()
   126  	if err != nil {
   127  		t.Fatal(err)
   128  	}
   129  
   130  	for _, tt := range tests {
   131  		t.Run(tt.name, func(t *testing.T) {
   132  			cachePath := ensure.TempDir(t)
   133  			defer testChdir(t, cachePath)()
   134  
   135  			if err := os.MkdirAll("toot", 0777); err != nil {
   136  				t.Fatal(err)
   137  			}
   138  			var buf bytes.Buffer
   139  			c := newPackageCmd(&buf)
   140  
   141  			// This is an unfortunate byproduct of the tmpdir
   142  			if v, ok := tt.flags["keyring"]; ok && len(v) > 0 {
   143  				tt.flags["keyring"] = filepath.Join(origDir, v)
   144  			}
   145  
   146  			setFlags(c, tt.flags)
   147  			re := regexp.MustCompile(tt.expect)
   148  
   149  			adjustedArgs := make([]string, len(tt.args))
   150  			for i, f := range tt.args {
   151  				adjustedArgs[i] = filepath.Join(origDir, f)
   152  			}
   153  
   154  			err := c.RunE(c, adjustedArgs)
   155  			if err != nil {
   156  				if tt.err && re.MatchString(err.Error()) {
   157  					return
   158  				}
   159  				t.Fatalf("%q: expected error %q, got %q", tt.name, tt.expect, err)
   160  			}
   161  
   162  			if !re.Match(buf.Bytes()) {
   163  				t.Errorf("%q: expected output %q, got %q", tt.name, tt.expect, buf.String())
   164  			}
   165  
   166  			if len(tt.hasfile) > 0 {
   167  				if fi, err := os.Stat(tt.hasfile); err != nil {
   168  					t.Errorf("%q: expected file %q, got err %q", tt.name, tt.hasfile, err)
   169  				} else if fi.Size() == 0 {
   170  					t.Errorf("%q: file %q has zero bytes.", tt.name, tt.hasfile)
   171  				}
   172  			}
   173  
   174  			if v, ok := tt.flags["sign"]; ok && v == "1" {
   175  				if fi, err := os.Stat(tt.hasfile + ".prov"); err != nil {
   176  					t.Errorf("%q: expected provenance file", tt.name)
   177  				} else if fi.Size() == 0 {
   178  					t.Errorf("%q: provenance file is empty", tt.name)
   179  				}
   180  			}
   181  		})
   182  	}
   183  }
   184  
   185  func TestSetAppVersion(t *testing.T) {
   186  	var ch *chart.Chart
   187  	expectedAppVersion := "app-version-foo"
   188  
   189  	dir := ensure.TempDir(t)
   190  
   191  	c := newPackageCmd(&bytes.Buffer{})
   192  	flags := map[string]string{
   193  		"destination": dir,
   194  		"app-version": expectedAppVersion,
   195  	}
   196  	setFlags(c, flags)
   197  	if err := c.RunE(c, []string{"testdata/testcharts/alpine"}); err != nil {
   198  		t.Errorf("unexpected error %q", err)
   199  	}
   200  
   201  	chartPath := filepath.Join(dir, "alpine-0.1.0.tgz")
   202  	if fi, err := os.Stat(chartPath); err != nil {
   203  		t.Errorf("expected file %q, got err %q", chartPath, err)
   204  	} else if fi.Size() == 0 {
   205  		t.Errorf("file %q has zero bytes.", chartPath)
   206  	}
   207  	ch, err := loader.Load(chartPath)
   208  	if err != nil {
   209  		t.Fatalf("unexpected error loading packaged chart: %v", err)
   210  	}
   211  	if ch.Metadata.AppVersion != expectedAppVersion {
   212  		t.Errorf("expected app-version %q, found %q", expectedAppVersion, ch.Metadata.AppVersion)
   213  	}
   214  }
   215  
   216  func TestPackageValues(t *testing.T) {
   217  	defer resetEnv()()
   218  
   219  	repoFile := "testdata/helmhome/helm/repositories.yaml"
   220  
   221  	testCases := []struct {
   222  		desc               string
   223  		args               []string
   224  		valuefilesContents []string
   225  		flags              map[string]string
   226  		expected           []string
   227  	}{
   228  		{
   229  			desc:               "helm package, single values file",
   230  			args:               []string{"testdata/testcharts/alpine"},
   231  			flags:              map[string]string{"repository-config": repoFile},
   232  			valuefilesContents: []string{"Name: chart-name-foo"},
   233  			expected:           []string{"Name: chart-name-foo"},
   234  		},
   235  		{
   236  			desc:               "helm package, multiple values files",
   237  			args:               []string{"testdata/testcharts/alpine"},
   238  			flags:              map[string]string{"repository-config": repoFile},
   239  			valuefilesContents: []string{"Name: chart-name-foo", "foo: bar"},
   240  			expected:           []string{"Name: chart-name-foo", "foo: bar"},
   241  		},
   242  		{
   243  			desc:     "helm package, with set option",
   244  			args:     []string{"testdata/testcharts/alpine"},
   245  			flags:    map[string]string{"set": "Name=chart-name-foo", "repository-config": repoFile},
   246  			expected: []string{"Name: chart-name-foo"},
   247  		},
   248  		{
   249  			desc:               "helm package, set takes precedence over value file",
   250  			args:               []string{"testdata/testcharts/alpine"},
   251  			valuefilesContents: []string{"Name: chart-name-foo"},
   252  			flags:              map[string]string{"set": "Name=chart-name-bar", "repository-config": repoFile},
   253  			expected:           []string{"Name: chart-name-bar"},
   254  		},
   255  	}
   256  
   257  	for _, tc := range testCases {
   258  		var files []string
   259  		for _, contents := range tc.valuefilesContents {
   260  			f := createValuesFile(t, contents)
   261  			files = append(files, f)
   262  		}
   263  		valueFiles := strings.Join(files, ",")
   264  
   265  		expected, err := chartutil.ReadValues([]byte(strings.Join(tc.expected, "\n")))
   266  		if err != nil {
   267  			t.Errorf("unexpected error parsing values: %q", err)
   268  		}
   269  
   270  		outputDir := ensure.TempDir(t)
   271  
   272  		if len(tc.flags) == 0 {
   273  			tc.flags = make(map[string]string)
   274  		}
   275  		tc.flags["destination"] = outputDir
   276  
   277  		if len(valueFiles) > 0 {
   278  			tc.flags["values"] = valueFiles
   279  		}
   280  
   281  		cmd := newPackageCmd(&bytes.Buffer{})
   282  		setFlags(cmd, tc.flags)
   283  		if err := cmd.RunE(cmd, tc.args); err != nil {
   284  			t.Fatalf("unexpected error: %q", err)
   285  		}
   286  
   287  		outputFile := filepath.Join(outputDir, "alpine-0.1.0.tgz")
   288  		verifyOutputChartExists(t, outputFile)
   289  
   290  		actual, err := getChartValues(outputFile)
   291  		if err != nil {
   292  			t.Fatalf("unexpected error extracting chart values: %q", err)
   293  		}
   294  
   295  		verifyValues(t, actual, expected)
   296  	}
   297  }
   298  
   299  func createValuesFile(t *testing.T, data string) string {
   300  	outputDir := ensure.TempDir(t)
   301  
   302  	outputFile := filepath.Join(outputDir, "values.yaml")
   303  	if err := ioutil.WriteFile(outputFile, []byte(data), 0644); err != nil {
   304  		t.Fatalf("err: %s", err)
   305  	}
   306  	return outputFile
   307  }
   308  
   309  func getChartValues(chartPath string) (chartutil.Values, error) {
   310  	chart, err := loader.Load(chartPath)
   311  	if err != nil {
   312  		return nil, err
   313  	}
   314  	return chart.Values, nil
   315  }
   316  
   317  func verifyValues(t *testing.T, actual, expected chartutil.Values) {
   318  	t.Helper()
   319  	for key, value := range expected.AsMap() {
   320  		if got := actual[key]; got != value {
   321  			t.Errorf("Expected %q, got %q (%v)", value, got, actual)
   322  		}
   323  	}
   324  }
   325  
   326  func verifyOutputChartExists(t *testing.T, chartPath string) {
   327  	if chartFile, err := os.Stat(chartPath); err != nil {
   328  		t.Errorf("expected file %q, got err %q", chartPath, err)
   329  	} else if chartFile.Size() == 0 {
   330  		t.Errorf("file %q has zero bytes.", chartPath)
   331  	}
   332  }
   333  
   334  func setFlags(cmd *cobra.Command, flags map[string]string) {
   335  	dest := cmd.Flags()
   336  	for f, v := range flags {
   337  		dest.Set(f, v)
   338  	}
   339  }