diff --git a/.golangci.yml b/.golangci.yml index 311c0a093..d6defa743 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -110,6 +110,7 @@ linters-settings: gomnd: ignored-functions: - os.WriteFile + - make gomoddirectives: replace-local: true gosec: diff --git a/api/internal/builtins/HelmChartInflationGenerator.go b/api/internal/builtins/HelmChartInflationGenerator.go index 24533ab69..7dc603b52 100644 --- a/api/internal/builtins/HelmChartInflationGenerator.go +++ b/api/internal/builtins/HelmChartInflationGenerator.go @@ -16,6 +16,7 @@ import ( "sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/kyaml/errors" + "sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/yaml" ) @@ -86,12 +87,20 @@ func (p *HelmChartInflationGeneratorPlugin) validateArgs() (err error) { p.ChartHome = types.HelmDefaultHome } - // The ValuesFile may be consulted by the plugin, so it must + // The ValuesFile(s) may be consulted by the plugin, so it must // be under the loader root (unless root restrictions are // disabled). if p.ValuesFile == "" { p.ValuesFile = filepath.Join(p.ChartHome, p.Name, "values.yaml") } + for i, file := range p.AdditionalValuesFiles { + // use Load() to enforce root restrictions + if _, err := p.h.Loader().Load(file); err != nil { + return errors.WrapPrefixf(err, "could not load additionalValuesFile") + } + // the additional values filepaths must be relative to the kust root + p.AdditionalValuesFiles[i] = filepath.Join(p.h.Loader().Root(), file) + } if err = p.errIfIllegalValuesMerge(); err != nil { return err @@ -240,49 +249,28 @@ func (p *HelmChartInflationGeneratorPlugin) Generate() (rm resmap.ResMap, err er return nil, err } var stdout []byte - stdout, err = p.runHelmCommand(p.templateCommand()) + stdout, err = p.runHelmCommand(p.AsHelmArgs(p.absChartHome())) if err != nil { return nil, err } - rm, err = p.h.ResmapFactory().NewResMapFromBytes(stdout) - if err == nil { + rm, resMapErr := p.h.ResmapFactory().NewResMapFromBytes(stdout) + if resMapErr == nil { return rm, nil } // try to remove the contents before first "---" because // helm may produce messages to stdout before it - stdoutStr := string(stdout) - if idx := strings.Index(stdoutStr, "\n---\n"); idx != -1 { - return p.h.ResmapFactory().NewResMapFromBytes([]byte(stdoutStr[idx:])) - } - return nil, err -} + r := &kio.ByteReader{Reader: bytes.NewBufferString(string(stdout)), OmitReaderAnnotations: true} + nodes, err := r.Read() -func (p *HelmChartInflationGeneratorPlugin) templateCommand() []string { - args := []string{"template"} - if p.ReleaseName != "" { - args = append(args, p.ReleaseName) + if len(nodes) != 0 { + rm, err = p.h.ResmapFactory().NewResMapFromRNodeSlice(nodes) + if err != nil { + return nil, fmt.Errorf("could not parse rnode slice into resource map: %w\n", err) + } + return rm, nil } - if p.Namespace != "" { - args = append(args, "--namespace", p.Namespace) - } - args = append(args, filepath.Join(p.absChartHome(), p.Name)) - if p.ValuesFile != "" { - args = append(args, "--values", p.ValuesFile) - } - if p.ReleaseName == "" { - // AFAICT, this doesn't work as intended due to a bug in helm. - // See https://github.com/helm/helm/issues/6019 - // I've tried placing the flag before and after the name argument. - args = append(args, "--generate-name") - } - if p.IncludeCRDs { - args = append(args, "--include-crds") - } - if p.SkipHooks { - args = append(args, "--no-hooks") - } - return args + return nil, fmt.Errorf("could not parse bytes into resource map: %w\n", resMapErr) } func (p *HelmChartInflationGeneratorPlugin) pullCommand() []string { diff --git a/api/krusty/helmchartinflationgenerator_test.go b/api/krusty/helmchartinflationgenerator_test.go index 2f867b378..adc1c7d42 100644 --- a/api/krusty/helmchartinflationgenerator_test.go +++ b/api/krusty/helmchartinflationgenerator_test.go @@ -4,9 +4,12 @@ package krusty_test import ( + "path/filepath" "testing" + "github.com/stretchr/testify/require" kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" + "sigs.k8s.io/kustomize/kyaml/copyutil" ) const expectedHelm = ` @@ -233,3 +236,193 @@ spec: type: ClusterIP `) } + +func TestHelmChartInflationGeneratorMultipleValuesFiles(t *testing.T) { + th := kusttest_test.MakeEnhancedHarnessWithTmpRoot(t) + defer th.Reset() + if err := th.ErrIfNoHelm(); err != nil { + t.Skip("skipping: " + err.Error()) + } + + copyValuesFilesTestChartsIntoHarness(t, th) + + th.WriteK(th.GetRoot(), ` +helmCharts: + - name: test-chart + releaseName: test-chart + additionalValuesFiles: + - charts/valuesFiles/file1.yaml + - charts/valuesFiles/file2.yaml +`) + + m := th.Run(th.GetRoot(), th.MakeOptionsPluginsEnabled()) + asYaml, err := m.AsYaml() + require.NoError(t, err) + require.Equal(t, string(asYaml), `apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + chart: test-1.0.0 + name: my-deploy + namespace: file-2 +spec: + replicas: 1 + selector: + matchLabels: + app: test + template: + spec: + containers: + - image: test-image-file1:file1 + imagePullPolicy: Never +--- +apiVersion: apps/v1 +kind: Pod +metadata: + annotations: + helm.sh/hook: test + name: test-chart +`) +} + +func TestHelmChartInflationGeneratorApiVersions(t *testing.T) { + th := kusttest_test.MakeEnhancedHarnessWithTmpRoot(t) + defer th.Reset() + if err := th.ErrIfNoHelm(); err != nil { + t.Skip("skipping: " + err.Error()) + } + + copyValuesFilesTestChartsIntoHarness(t, th) + + th.WriteK(th.GetRoot(), ` +helmCharts: + - name: test-chart + releaseName: test-chart + apiVersions: + - foo/v1 +`) + + m := th.Run(th.GetRoot(), th.MakeOptionsPluginsEnabled()) + asYaml, err := m.AsYaml() + require.NoError(t, err) + require.Equal(t, string(asYaml), `apiVersion: foo/v1 +kind: Deployment +metadata: + labels: + chart: test-1.0.0 + name: my-deploy + namespace: default +spec: + replicas: 1 + selector: + matchLabels: + app: test + template: + spec: + containers: + - image: test-image:v1.0.0 + imagePullPolicy: Always +--- +apiVersion: foo/v1 +kind: Pod +metadata: + annotations: + helm.sh/hook: test + name: test-chart +`) +} + +func TestHelmChartInflationGeneratorSkipTests(t *testing.T) { + th := kusttest_test.MakeEnhancedHarnessWithTmpRoot(t) + defer th.Reset() + if err := th.ErrIfNoHelm(); err != nil { + t.Skip("skipping: " + err.Error()) + } + + copyValuesFilesTestChartsIntoHarness(t, th) + + th.WriteK(th.GetRoot(), ` +helmCharts: + - name: test-chart + releaseName: test-chart + skipTests: true +`) + + m := th.Run(th.GetRoot(), th.MakeOptionsPluginsEnabled()) + asYaml, err := m.AsYaml() + require.NoError(t, err) + require.Equal(t, string(asYaml), `apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + chart: test-1.0.0 + name: my-deploy + namespace: default +spec: + replicas: 1 + selector: + matchLabels: + app: test + template: + spec: + containers: + - image: test-image:v1.0.0 + imagePullPolicy: Always +`) +} + +func TestHelmChartInflationGeneratorNameTemplate(t *testing.T) { + th := kusttest_test.MakeEnhancedHarnessWithTmpRoot(t) + defer th.Reset() + if err := th.ErrIfNoHelm(); err != nil { + t.Skip("skipping: " + err.Error()) + } + + copyValuesFilesTestChartsIntoHarness(t, th) + + th.WriteK(th.GetRoot(), ` +helmCharts: + - name: test-chart + nameTemplate: name-template +`) + + m := th.Run(th.GetRoot(), th.MakeOptionsPluginsEnabled()) + asYaml, err := m.AsYaml() + require.NoError(t, err) + require.Equal(t, string(asYaml), `apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + chart: test-1.0.0 + name: my-deploy + namespace: default +spec: + replicas: 1 + selector: + matchLabels: + app: test + template: + spec: + containers: + - image: test-image:v1.0.0 + imagePullPolicy: Always +--- +apiVersion: apps/v1 +kind: Pod +metadata: + annotations: + helm.sh/hook: test + name: name-template +`) +} + +func copyValuesFilesTestChartsIntoHarness(t *testing.T, th *kusttest_test.HarnessEnhanced) { + t.Helper() + + thDir := filepath.Join(th.GetRoot(), "charts") + chartDir := "testdata/helmcharts" + + fs := th.GetFSys() + require.NoError(t, fs.MkdirAll(filepath.Join(thDir, "templates"))) + require.NoError(t, copyutil.CopyDir(th.GetFSys(), chartDir, thDir)) +} diff --git a/api/krusty/testdata/helmcharts/test-chart/Chart.yaml b/api/krusty/testdata/helmcharts/test-chart/Chart.yaml new file mode 100644 index 000000000..73eee9813 --- /dev/null +++ b/api/krusty/testdata/helmcharts/test-chart/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A simple test helm chart. +name: test +version: 1.0.0 diff --git a/api/krusty/testdata/helmcharts/test-chart/README.md b/api/krusty/testdata/helmcharts/test-chart/README.md new file mode 100644 index 000000000..3b1383634 --- /dev/null +++ b/api/krusty/testdata/helmcharts/test-chart/README.md @@ -0,0 +1 @@ +This is a simple test chart. \ No newline at end of file diff --git a/api/krusty/testdata/helmcharts/test-chart/templates/_helpers.tpl b/api/krusty/testdata/helmcharts/test-chart/templates/_helpers.tpl new file mode 100644 index 000000000..d3b980142 --- /dev/null +++ b/api/krusty/testdata/helmcharts/test-chart/templates/_helpers.tpl @@ -0,0 +1,7 @@ +{{- define "apiversion" -}} +{{- if .Capabilities.APIVersions.Has "foo/v1" -}} +foo/v1 +{{- else -}} +apps/v1 +{{- end -}} +{{- end -}} \ No newline at end of file diff --git a/api/krusty/testdata/helmcharts/test-chart/templates/deployment.yaml b/api/krusty/testdata/helmcharts/test-chart/templates/deployment.yaml new file mode 100644 index 000000000..ed48472be --- /dev/null +++ b/api/krusty/testdata/helmcharts/test-chart/templates/deployment.yaml @@ -0,0 +1,18 @@ +--- +apiVersion: {{ template "apiversion" . }} +kind: Deployment +metadata: + labels: + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + name: my-deploy + namespace: {{ .Values.data.namespace }} +spec: + replicas: 1 + selector: + matchLabels: + app: {{ .Chart.Name }} + template: + spec: + containers: + - image: "{{ .Values.data.image.name }}:{{ .Values.data.image.tag }}" + imagePullPolicy: {{ .Values.data.image.imagePullPolicy }} \ No newline at end of file diff --git a/api/krusty/testdata/helmcharts/test-chart/templates/tests/test-pod.yaml b/api/krusty/testdata/helmcharts/test-chart/templates/tests/test-pod.yaml new file mode 100644 index 000000000..0ea8d4ee6 --- /dev/null +++ b/api/krusty/testdata/helmcharts/test-chart/templates/tests/test-pod.yaml @@ -0,0 +1,6 @@ +apiVersion: {{ template "apiversion" . }} +kind: Pod +metadata: + name: {{ .Release.Name }} + annotations: + "helm.sh/hook": test \ No newline at end of file diff --git a/api/krusty/testdata/helmcharts/test-chart/values.yaml b/api/krusty/testdata/helmcharts/test-chart/values.yaml new file mode 100644 index 000000000..631876f80 --- /dev/null +++ b/api/krusty/testdata/helmcharts/test-chart/values.yaml @@ -0,0 +1,6 @@ +data: + namespace: default + image: + name: test-image + tag: v1.0.0 + imagePullPolicy: Always diff --git a/api/krusty/testdata/helmcharts/valuesFiles/file1.yaml b/api/krusty/testdata/helmcharts/valuesFiles/file1.yaml new file mode 100644 index 000000000..fd44cc046 --- /dev/null +++ b/api/krusty/testdata/helmcharts/valuesFiles/file1.yaml @@ -0,0 +1,5 @@ +data: + image: + name: test-image-file1 + tag: file1 + imagePullPolicy: Never \ No newline at end of file diff --git a/api/krusty/testdata/helmcharts/valuesFiles/file2.yaml b/api/krusty/testdata/helmcharts/valuesFiles/file2.yaml new file mode 100644 index 000000000..dab61a9ed --- /dev/null +++ b/api/krusty/testdata/helmcharts/valuesFiles/file2.yaml @@ -0,0 +1,2 @@ +data: + namespace: file-2 \ No newline at end of file diff --git a/api/types/helmchartargs.go b/api/types/helmchartargs.go index bbf7f5ff5..15ea7178f 100644 --- a/api/types/helmchartargs.go +++ b/api/types/helmchartargs.go @@ -3,6 +3,8 @@ package types +import "path/filepath" + const HelmDefaultHome = "charts" type HelmGlobals struct { @@ -57,7 +59,11 @@ type HelmChart struct { // in the helm template Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` - // ValuesFile is local file path to a values file to use _instead of_ + // AdditionalValuesFiles are local file paths to values files to be used in + // addition to either the default values file or the values specified in ValuesFile. + AdditionalValuesFiles []string `json:"additionalValuesFiles,omitempty" yaml:"additionalValuesFiles,omitempty"` + + // ValuesFile is a local file path to a values file to use _instead of_ // the default values that accompanied the chart. // The default values are in '{ChartHome}/{Name}/values.yaml'. ValuesFile string `json:"valuesFile,omitempty" yaml:"valuesFile,omitempty"` @@ -78,6 +84,15 @@ type HelmChart struct { // SkipHooks sets the --no-hooks flag when calling helm template. This prevents // helm from erroneously rendering test templates. SkipHooks bool `json:"skipHooks,omitempty" yaml:"skipHooks,omitempty"` + + // ApiVersions is the kubernetes apiversions used for Capabilities.APIVersions + ApiVersions []string `json:"apiVersions,omitempty" yaml:"apiVersions,omitempty"` + + // NameTemplate is for specifying the name template used to name the release. + NameTemplate string `json:"nameTemplate,omitempty" yaml:"nameTemplate,omitempty"` + + // SkipTests skips tests from templated output. + SkipTests bool `json:"skipTests,omitempty" yaml:"skipTests,omitempty"` } // HelmChartArgs contains arguments to helm. @@ -126,3 +141,45 @@ func makeHelmChartFromHca(old *HelmChartArgs) (c HelmChart) { c.ReleaseName = old.ReleaseName return } + +func (h HelmChart) AsHelmArgs(absChartHome string) []string { + args := []string{"template"} + if h.ReleaseName != "" { + args = append(args, h.ReleaseName) + } else { + // AFAICT, this doesn't work as intended due to a bug in helm. + // See https://github.com/helm/helm/issues/6019 + // I've tried placing the flag before and after the name argument. + args = append(args, "--generate-name") + } + if h.Name != "" { + args = append(args, filepath.Join(absChartHome, h.Name)) + } + if h.Namespace != "" { + args = append(args, "--namespace", h.Namespace) + } + if h.NameTemplate != "" { + args = append(args, "--name-template", h.NameTemplate) + } + + if h.ValuesFile != "" { + args = append(args, "-f", h.ValuesFile) + } + for _, valuesFile := range h.AdditionalValuesFiles { + args = append(args, "-f", valuesFile) + } + + for _, apiVer := range h.ApiVersions { + args = append(args, "--api-versions", apiVer) + } + if h.IncludeCRDs { + args = append(args, "--include-crds") + } + if h.SkipTests { + args = append(args, "--skip-tests") + } + if h.SkipHooks { + args = append(args, "--no-hooks") + } + return args +} diff --git a/api/types/helmchartargs_test.go b/api/types/helmchartargs_test.go new file mode 100644 index 000000000..765a28a05 --- /dev/null +++ b/api/types/helmchartargs_test.go @@ -0,0 +1,61 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "sigs.k8s.io/kustomize/api/types" +) + +func TestAsHelmArgs(t *testing.T) { + t.Run("use generate-name", func(t *testing.T) { + p := types.HelmChart{ + Name: "chart-name", + Version: "1.0.0", + Repo: "https://helm.releases.hashicorp.com", + ApiVersions: []string{"foo", "bar"}, + NameTemplate: "template", + SkipTests: true, + IncludeCRDs: true, + SkipHooks: true, + ValuesFile: "values", + AdditionalValuesFiles: []string{"values1", "values2"}, + Namespace: "my-ns", + } + require.Equal(t, p.AsHelmArgs("/home/charts"), + []string{"template", "--generate-name", + "/home/charts/chart-name", + "--namespace", "my-ns", + "--name-template", "template", + "-f", "values", + "-f", "values1", "-f", "values2", + "--api-versions", "foo", "--api-versions", "bar", + "--include-crds", + "--skip-tests", + "--no-hooks"}) + }) + + t.Run("use release-name", func(t *testing.T) { + p := types.HelmChart{ + Name: "chart-name", + Version: "1.0.0", + Repo: "https://helm.releases.hashicorp.com", + ApiVersions: []string{"foo", "bar"}, + NameTemplate: "template", + ValuesFile: "values", + AdditionalValuesFiles: []string{"values1", "values2"}, + Namespace: "my-ns", + ReleaseName: "test", + } + require.Equal(t, p.AsHelmArgs("/home/charts"), + []string{"template", "test", "/home/charts/chart-name", + "--namespace", "my-ns", + "--name-template", "template", + "-f", "values", + "-f", "values1", "-f", "values2", + "--api-versions", "foo", "--api-versions", "bar"}) + }) +} diff --git a/cmd/config/internal/commands/cat_test.go b/cmd/config/internal/commands/cat_test.go index 59d5da563..5c723cf2a 100644 --- a/cmd/config/internal/commands/cat_test.go +++ b/cmd/config/internal/commands/cat_test.go @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/assert" "sigs.k8s.io/kustomize/cmd/config/internal/commands" "sigs.k8s.io/kustomize/kyaml/copyutil" + "sigs.k8s.io/kustomize/kyaml/filesys" "sigs.k8s.io/kustomize/kyaml/openapi" ) @@ -614,7 +615,7 @@ spec: defer openapi.ResetOpenAPI() sourceDir := filepath.Join("test", "testdata", test.dataset) baseDir := t.TempDir() - copyutil.CopyDir(sourceDir, baseDir) + assert.NoError(t, copyutil.CopyDir(filesys.MakeFsOnDisk(), sourceDir, baseDir)) runner := commands.GetCatRunner("") actual := &bytes.Buffer{} runner.Command.SetOut(actual) diff --git a/cmd/config/internal/commands/count_test.go b/cmd/config/internal/commands/count_test.go index 3890bede3..77b117605 100644 --- a/cmd/config/internal/commands/count_test.go +++ b/cmd/config/internal/commands/count_test.go @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/assert" "sigs.k8s.io/kustomize/cmd/config/internal/commands" "sigs.k8s.io/kustomize/kyaml/copyutil" + "sigs.k8s.io/kustomize/kyaml/filesys" "sigs.k8s.io/kustomize/kyaml/openapi" ) @@ -117,7 +118,7 @@ Deployment: 1 defer openapi.ResetOpenAPI() sourceDir := filepath.Join("test", "testdata", test.dataset) baseDir := t.TempDir() - copyutil.CopyDir(sourceDir, baseDir) + assert.NoError(t, copyutil.CopyDir(filesys.MakeFsOnDisk(), sourceDir, baseDir)) runner := commands.GetCountRunner("") actual := &bytes.Buffer{} runner.Command.SetOut(actual) diff --git a/cmd/config/internal/commands/grep_test.go b/cmd/config/internal/commands/grep_test.go index 60f751b5e..478de3637 100644 --- a/cmd/config/internal/commands/grep_test.go +++ b/cmd/config/internal/commands/grep_test.go @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/assert" "sigs.k8s.io/kustomize/cmd/config/internal/commands" "sigs.k8s.io/kustomize/kyaml/copyutil" + "sigs.k8s.io/kustomize/kyaml/filesys" ) // TestGrepCommand_files verifies grep reads the files and filters them @@ -396,7 +397,7 @@ spec: t.Run(test.name, func(t *testing.T) { sourceDir := filepath.Join("test", "testdata", test.dataset) baseDir := t.TempDir() - copyutil.CopyDir(sourceDir, baseDir) + assert.NoError(t, copyutil.CopyDir(filesys.MakeFsOnDisk(), sourceDir, baseDir)) runner := commands.GetGrepRunner("") actual := &bytes.Buffer{} runner.Command.SetOut(actual) diff --git a/kyaml/copyutil/copyutil.go b/kyaml/copyutil/copyutil.go index 602ff8a7c..98cb8314d 100644 --- a/kyaml/copyutil/copyutil.go +++ b/kyaml/copyutil/copyutil.go @@ -12,12 +12,14 @@ import ( "strings" "github.com/sergi/go-diff/diffmatchpatch" + "sigs.k8s.io/kustomize/kyaml/errors" + "sigs.k8s.io/kustomize/kyaml/filesys" "sigs.k8s.io/kustomize/kyaml/sets" ) // CopyDir copies a src directory to a dst directory. CopyDir skips copying the .git directory from the src. -func CopyDir(src string, dst string) error { - return filepath.Walk(src, func(path string, info os.FileInfo, err error) error { +func CopyDir(fSys filesys.FileSystem, src string, dst string) error { + return errors.Wrap(fSys.Walk(src, func(path string, info os.FileInfo, err error) error { if err != nil { return err } @@ -36,21 +38,21 @@ func CopyDir(src string, dst string) error { // make directories that don't exist if info.IsDir() { - return os.MkdirAll(filepath.Join(dst, copyTo), info.Mode()) + return errors.Wrap(fSys.MkdirAll(filepath.Join(dst, copyTo))) } // copy file by reading and writing it - b, err := os.ReadFile(filepath.Join(src, copyTo)) + b, err := fSys.ReadFile(filepath.Join(src, copyTo)) if err != nil { - return err + return errors.Wrap(err) } - err = os.WriteFile(filepath.Join(dst, copyTo), b, info.Mode()) + err = fSys.WriteFile(filepath.Join(dst, copyTo), b) if err != nil { - return err + return errors.Wrap(err) } return nil - }) + })) } // Diff returns a list of files that differ between the source and destination. diff --git a/kyaml/copyutil/copyutil_test.go b/kyaml/copyutil/copyutil_test.go index c3ab97157..82bdebc02 100644 --- a/kyaml/copyutil/copyutil_test.go +++ b/kyaml/copyutil/copyutil_test.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/assert" . "sigs.k8s.io/kustomize/kyaml/copyutil" + "sigs.k8s.io/kustomize/kyaml/filesys" ) // TestDiff_identical verifies identical directories return an empty set @@ -306,7 +307,7 @@ func TestCopyDir(t *testing.T) { d := t.TempDir() - err = CopyDir(s, d) + err = CopyDir(filesys.MakeFsOnDisk(), s, d) assert.NoError(t, err) diff, err := Diff(d, v) diff --git a/kyaml/kio/filters/merge3_test.go b/kyaml/kio/filters/merge3_test.go index b51fad776..9f45d561c 100644 --- a/kyaml/kio/filters/merge3_test.go +++ b/kyaml/kio/filters/merge3_test.go @@ -8,11 +8,11 @@ import ( "runtime" "testing" - "sigs.k8s.io/kustomize/kyaml/testutil" - "github.com/stretchr/testify/assert" "sigs.k8s.io/kustomize/kyaml/copyutil" + "sigs.k8s.io/kustomize/kyaml/filesys" "sigs.k8s.io/kustomize/kyaml/kio/filters" + "sigs.k8s.io/kustomize/kyaml/testutil" ) func TestMerge3_Merge(t *testing.T) { @@ -29,6 +29,7 @@ func TestMerge3_Merge(t *testing.T) { dir := t.TempDir() if !assert.NoError(t, copyutil.CopyDir( + filesys.MakeFsOnDisk(), filepath.Join(datadir, "dataset1-localupdates"), filepath.Join(dir, "dataset1"))) { t.FailNow() @@ -71,6 +72,7 @@ func TestMerge3_Merge_path(t *testing.T) { dir := t.TempDir() if !assert.NoError(t, copyutil.CopyDir( + filesys.MakeFsOnDisk(), filepath.Join(datadir, "dataset1-localupdates"), filepath.Join(dir, "dataset1"))) { t.FailNow() @@ -112,6 +114,7 @@ func TestMerge3_Merge_fail(t *testing.T) { dir := t.TempDir() if !assert.NoError(t, copyutil.CopyDir( + filesys.MakeFsOnDisk(), filepath.Join(datadir, "dataset1-localupdates"), filepath.Join(dir, "dataset1"))) { t.FailNow() diff --git a/kyaml/runfn/runfn_test.go b/kyaml/runfn/runfn_test.go index dbd2d6f0a..34ef67f7b 100644 --- a/kyaml/runfn/runfn_test.go +++ b/kyaml/runfn/runfn_test.go @@ -14,9 +14,9 @@ import ( "testing" "github.com/stretchr/testify/assert" - "sigs.k8s.io/kustomize/kyaml/copyutil" "sigs.k8s.io/kustomize/kyaml/errors" + "sigs.k8s.io/kustomize/kyaml/filesys" "sigs.k8s.io/kustomize/kyaml/fn/runtime/container" "sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil" "sigs.k8s.io/kustomize/kyaml/kio" @@ -1272,7 +1272,7 @@ func setupTest(t *testing.T) string { if !assert.NoError(t, err) { t.FailNow() } - if !assert.NoError(t, copyutil.CopyDir(ds, dir)) { + if !assert.NoError(t, copyutil.CopyDir(filesys.MakeFsOnDisk(), ds, dir)) { t.FailNow() } diff --git a/plugin/builtin/helmchartinflationgenerator/HelmChartInflationGenerator.go b/plugin/builtin/helmchartinflationgenerator/HelmChartInflationGenerator.go index d0dee8bae..ac8e0aaaa 100644 --- a/plugin/builtin/helmchartinflationgenerator/HelmChartInflationGenerator.go +++ b/plugin/builtin/helmchartinflationgenerator/HelmChartInflationGenerator.go @@ -20,6 +20,7 @@ import ( "sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/kyaml/errors" + "sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/yaml" ) @@ -92,12 +93,20 @@ func (p *plugin) validateArgs() (err error) { p.ChartHome = types.HelmDefaultHome } - // The ValuesFile may be consulted by the plugin, so it must + // The ValuesFile(s) may be consulted by the plugin, so it must // be under the loader root (unless root restrictions are // disabled). if p.ValuesFile == "" { p.ValuesFile = filepath.Join(p.ChartHome, p.Name, "values.yaml") } + for i, file := range p.AdditionalValuesFiles { + // use Load() to enforce root restrictions + if _, err := p.h.Loader().Load(file); err != nil { + return errors.WrapPrefixf(err, "could not load additionalValuesFile") + } + // the additional values filepaths must be relative to the kust root + p.AdditionalValuesFiles[i] = filepath.Join(p.h.Loader().Root(), file) + } if err = p.errIfIllegalValuesMerge(); err != nil { return err @@ -246,49 +255,28 @@ func (p *plugin) Generate() (rm resmap.ResMap, err error) { return nil, err } var stdout []byte - stdout, err = p.runHelmCommand(p.templateCommand()) + stdout, err = p.runHelmCommand(p.AsHelmArgs(p.absChartHome())) if err != nil { return nil, err } - rm, err = p.h.ResmapFactory().NewResMapFromBytes(stdout) - if err == nil { + rm, resMapErr := p.h.ResmapFactory().NewResMapFromBytes(stdout) + if resMapErr == nil { return rm, nil } // try to remove the contents before first "---" because // helm may produce messages to stdout before it - stdoutStr := string(stdout) - if idx := strings.Index(stdoutStr, "\n---\n"); idx != -1 { - return p.h.ResmapFactory().NewResMapFromBytes([]byte(stdoutStr[idx:])) - } - return nil, err -} + r := &kio.ByteReader{Reader: bytes.NewBufferString(string(stdout)), OmitReaderAnnotations: true} + nodes, err := r.Read() -func (p *plugin) templateCommand() []string { - args := []string{"template"} - if p.ReleaseName != "" { - args = append(args, p.ReleaseName) + if len(nodes) != 0 { + rm, err = p.h.ResmapFactory().NewResMapFromRNodeSlice(nodes) + if err != nil { + return nil, fmt.Errorf("could not parse rnode slice into resource map: %w\n", err) + } + return rm, nil } - if p.Namespace != "" { - args = append(args, "--namespace", p.Namespace) - } - args = append(args, filepath.Join(p.absChartHome(), p.Name)) - if p.ValuesFile != "" { - args = append(args, "--values", p.ValuesFile) - } - if p.ReleaseName == "" { - // AFAICT, this doesn't work as intended due to a bug in helm. - // See https://github.com/helm/helm/issues/6019 - // I've tried placing the flag before and after the name argument. - args = append(args, "--generate-name") - } - if p.IncludeCRDs { - args = append(args, "--include-crds") - } - if p.SkipHooks { - args = append(args, "--no-hooks") - } - return args + return nil, fmt.Errorf("could not parse bytes into resource map: %w\n", resMapErr) } func (p *plugin) pullCommand() []string {