diff --git a/cmd/helm/template_test.go b/cmd/helm/template_test.go index 8fb8292e2..1f488117f 100644 --- a/cmd/helm/template_test.go +++ b/cmd/helm/template_test.go @@ -131,6 +131,11 @@ func TestTemplateCmd(t *testing.T) { cmd: fmt.Sprintf(`template '%s' --skip-tests`, chartPath), golden: "output/template-skip-tests.txt", }, + { + name: "chart with values as a directory", + cmd: fmt.Sprintf("template '%s' -f %s", "testdata/testcharts/chart-with-values-directory", "testdata/testcharts/chart-with-values-directory/values"), + golden: "output/template-with-values-directory.txt", + }, } runTestCmd(t, tests) } diff --git a/cmd/helm/testdata/output/template-with-values-directory.txt b/cmd/helm/testdata/output/template-with-values-directory.txt new file mode 100644 index 000000000..614d7752c --- /dev/null +++ b/cmd/helm/testdata/output/template-with-values-directory.txt @@ -0,0 +1,17 @@ +--- +# Source: chart-with-values-directory/templates/service.yaml +apiVersion: v1 +kind: Service +metadata: + name: chart-with-values-directory + labels: + helm.sh/chart: "chart-with-values-directory-0.1.0" +spec: + type: ClusterIP + ports: + - port: 80 + targetPort: 80 + protocol: TCP + name: apache + selector: + app.kubernetes.io/name: chart-with-values-directory diff --git a/cmd/helm/testdata/testcharts/chart-with-values-directory/Chart.yaml b/cmd/helm/testdata/testcharts/chart-with-values-directory/Chart.yaml new file mode 100644 index 000000000..3c586bf32 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-values-directory/Chart.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +description: A Helm chart for Kubernetes +name: chart-with-values-directory +version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/chart-with-values-directory/templates/service.yaml b/cmd/helm/testdata/testcharts/chart-with-values-directory/templates/service.yaml new file mode 100644 index 000000000..27501e1e0 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-values-directory/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Chart.Name }} + labels: + helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.externalPort }} + targetPort: {{ .Values.service.internalPort }} + protocol: TCP + name: {{ .Values.service.name }} + selector: + app.kubernetes.io/name: {{ .Chart.Name }} diff --git a/cmd/helm/testdata/testcharts/chart-with-values-directory/values/values1.yaml b/cmd/helm/testdata/testcharts/chart-with-values-directory/values/values1.yaml new file mode 100644 index 000000000..e40795666 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-values-directory/values/values1.yaml @@ -0,0 +1,3 @@ +service: + type: ClusterIP + externalPort: 80 diff --git a/cmd/helm/testdata/testcharts/chart-with-values-directory/values/values2.yaml b/cmd/helm/testdata/testcharts/chart-with-values-directory/values/values2.yaml new file mode 100644 index 000000000..e261f5591 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-values-directory/values/values2.yaml @@ -0,0 +1,3 @@ +service: + name: apache + internalPort: 80 diff --git a/pkg/cli/values/options.go b/pkg/cli/values/options.go index e6ad71767..5a9d422d9 100644 --- a/pkg/cli/values/options.go +++ b/pkg/cli/values/options.go @@ -20,6 +20,7 @@ import ( "io/ioutil" "net/url" "os" + "path/filepath" "strings" "github.com/pkg/errors" @@ -43,18 +44,32 @@ func (opts *Options) MergeValues(p getter.Providers) (map[string]interface{}, er // User specified a values files via -f/--values for _, filePath := range opts.ValueFiles { - currentMap := map[string]interface{}{} - - bytes, err := readFile(filePath, p) + isDir, err := isDirectory(filePath) if err != nil { return nil, err } + if isDir { + files, err := ioutil.ReadDir(filePath) + if err != nil { + return nil, err + } - if err := yaml.Unmarshal(bytes, ¤tMap); err != nil { - return nil, errors.Wrapf(err, "failed to parse %s", filePath) + for _, file := range files { + currentMap, err := unmarshallYaml(filepath.Join(filePath, file.Name()), p) + if err != nil { + return nil, err + } + // Merge with the previous map + base = mergeMaps(base, currentMap) + } + } else { + currentMap, err := unmarshallYaml(filePath, p) + if err != nil { + return nil, err + } + // Merge with the previous map + base = mergeMaps(base, currentMap) } - // Merge with the previous map - base = mergeMaps(base, currentMap) } // User specified a value via --set @@ -85,6 +100,31 @@ func (opts *Options) MergeValues(p getter.Providers) (map[string]interface{}, er return base, nil } +func isDirectory(filePath string) (bool, error) { + if strings.TrimSpace(filePath) == "-" { + return false, nil + } + + fileInfo, err := os.Stat(filePath) + if err != nil { + return false, err + } + return fileInfo.Mode().IsDir(), nil +} + +func unmarshallYaml(filePath string, p getter.Providers) (map[string]interface{}, error) { + currentMap := map[string]interface{}{} + bytes, err := readFile(filePath, p) + if err != nil { + return nil, err + } + + if err := yaml.Unmarshal(bytes, ¤tMap); err != nil { + return nil, errors.Wrapf(err, "failed to parse %s", filePath) + } + return currentMap, nil +} + func mergeMaps(a, b map[string]interface{}) map[string]interface{} { out := make(map[string]interface{}, len(a)) for k, v := range a {