feat: (6715) Add --env-values argument to helm command

Signed-off-by: Oscar Mauricio Forero Carrillo <oforero@ieee.org>
pull/6742/head
Oscar Mauricio Forero Carrillo 6 years ago
parent bf8318ea0b
commit 119996b50d

@ -35,6 +35,7 @@ func addValueOptionsFlags(f *pflag.FlagSet, v *values.Options) {
f.StringArrayVar(&v.Values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
f.StringArrayVar(&v.StringValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
f.StringArrayVar(&v.FileValues, "set-file", []string{}, "set values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2)")
f.StringVarP(&v.EnvValuesFile, "env-values", "e", "", "specify the name of a YAML file inside (the path should be relative to the root of the chart) the chart to override the default values, it won't fail if it does not exist")
}
func addChartPathOptionsFlags(f *pflag.FlagSet, c *action.ChartPathOptions) {

@ -171,7 +171,7 @@ func runInstall(args []string, client *action.Install, valueOpts *values.Options
}
// Check chart dependencies to make sure all are present in /charts
chartRequested, err := loader.Load(cp)
chartRequested, err := loader.LoadWithEnvValues(cp, valueOpts.EnvValuesFile)
if err != nil {
return nil, err
}

@ -59,6 +59,12 @@ func TestInstall(t *testing.T) {
cmd: "install virgil testdata/testcharts/alpine -f testdata/testcharts/alpine/extra_values.yaml -f testdata/testcharts/alpine/more_values.yaml",
golden: "output/install-with-multiple-values-files.txt",
},
// Install, values environment values yaml
{
name: "install with environment values",
cmd: "install virgil testdata/testcharts/alpine -e more_values.yaml",
golden: "output/install-with-multiple-values-files.txt",
},
// Install, no charts
{
name: "install with no chart specified",

@ -117,7 +117,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
}
// Check chart dependencies to make sure all are present in /charts
ch, err := loader.Load(chartPath)
ch, err := loader.LoadWithEnvValues(chartPath, valueOpts.EnvValuesFile)
if err != nil {
return err
}

@ -127,3 +127,24 @@ func (ch *Chart) CRDs() []*File {
}
return files
}
// MergeValues takes two maps from string to anything and returns a new map with the combined key:value pairs
// the values in the second map argument will override those in the first
func MergeValues(a, b map[string]interface{}) map[string]interface{} {
out := make(map[string]interface{}, len(a))
for k, v := range a {
out[k] = v
}
for k, v := range b {
if v, ok := v.(map[string]interface{}); ok {
if bv, ok := out[k]; ok {
if bv, ok := bv.(map[string]interface{}); ok {
out[k] = MergeValues(bv, v)
continue
}
}
}
out[k] = v
}
return out
}

@ -39,12 +39,17 @@ var drivePathPattern = regexp.MustCompile(`^[a-zA-Z]:/`)
type FileLoader string
// Load loads a chart
func (l FileLoader) Load() (*chart.Chart, error) {
return LoadFile(string(l))
func (l FileLoader) Load(envYaml string) (*chart.Chart, error) {
return LoadFileWithEnvValues(string(l), envYaml)
}
// LoadFile loads from an archive file.
func LoadFile(name string) (*chart.Chart, error) {
return LoadFileWithEnvValues(name, "")
}
// LoadFileWithEnvValues loads from an archive file.
func LoadFileWithEnvValues(name string, envYaml string) (*chart.Chart, error) {
if fi, err := os.Stat(name); err != nil {
return nil, err
} else if fi.IsDir() {
@ -62,7 +67,7 @@ func LoadFile(name string) (*chart.Chart, error) {
return nil, err
}
c, err := LoadArchive(raw)
c, err := LoadArchiveWithEnvValues(raw, envYaml)
if err != nil {
if err == gzip.ErrHeader {
return nil, fmt.Errorf("file '%s' does not appear to be a valid chart file (details: %s)", name, err)
@ -179,10 +184,16 @@ func LoadArchiveFiles(in io.Reader) ([]*BufferedFile, error) {
// LoadArchive loads from a reader containing a compressed tar archive.
func LoadArchive(in io.Reader) (*chart.Chart, error) {
return LoadArchiveWithEnvValues(in, "")
}
// LoadArchiveWithEnvValues loads from a reader containing a compressed tar archive,
// overriding the default values with the values from the environment file in the chart.
func LoadArchiveWithEnvValues(in io.Reader, envYaml string) (*chart.Chart, error) {
files, err := LoadArchiveFiles(in)
if err != nil {
return nil, err
}
return LoadFiles(files)
return LoadFilesWithEnvValues(files, envYaml)
}

@ -34,14 +34,22 @@ import (
type DirLoader string
// Load loads the chart
func (l DirLoader) Load() (*chart.Chart, error) {
return LoadDir(string(l))
func (l DirLoader) Load(envYaml string) (*chart.Chart, error) {
return LoadDirWithEnvValues(string(l), envYaml)
}
// LoadDir loads from a directory.
//
// This loads charts only from directories.
func LoadDir(dir string) (*chart.Chart, error) {
return LoadDirWithEnvValues(dir, "")
}
// LoadDirWithEnvValues loads from a directory,
// overriding the default values with the values from the environment file in the chart.
//
// This loads charts only from directories.
func LoadDirWithEnvValues(dir string, envYaml string) (*chart.Chart, error) {
topdir, err := filepath.Abs(dir)
if err != nil {
return nil, err
@ -111,5 +119,5 @@ func LoadDir(dir string) (*chart.Chart, error) {
return c, err
}
return LoadFiles(files)
return LoadFilesWithEnvValues(files, envYaml)
}

@ -31,7 +31,7 @@ import (
// ChartLoader loads a chart.
type ChartLoader interface {
Load() (*chart.Chart, error)
Load(envYaml string) (*chart.Chart, error)
}
// Loader returns a new ChartLoader appropriate for the given chart name
@ -55,11 +55,22 @@ func Loader(name string) (ChartLoader, error) {
// If a .helmignore file is present, the directory loader will skip loading any files
// matching it. But .helmignore is not evaluated when reading out of an archive.
func Load(name string) (*chart.Chart, error) {
return LoadWithEnvValues(name, "")
}
// LoadWithEnvValues takes a string name, tries to resolve it to a file or directory, and then loads it.
//
// This is the preferred way to load a chart. It will discover the chart encoding
// and hand off to the appropriate chart reader.
//
// If a .helmignore file is present, the directory loader will skip loading any files
// matching it. But .helmignore is not evaluated when reading out of an archive.
func LoadWithEnvValues(name string, envYaml string) (*chart.Chart, error) {
l, err := Loader(name)
if err != nil {
return nil, err
}
return l.Load()
return l.Load(envYaml)
}
// BufferedFile represents an archive file buffered for later processing.
@ -70,8 +81,15 @@ type BufferedFile struct {
// LoadFiles loads from in-memory files.
func LoadFiles(files []*BufferedFile) (*chart.Chart, error) {
return LoadFilesWithEnvValues(files, "")
}
// LoadFilesWithEnvValues loads from in-memory files.
func LoadFilesWithEnvValues(files []*BufferedFile, envYaml string) (*chart.Chart, error) {
c := new(chart.Chart)
subcharts := make(map[string][]*BufferedFile)
values := make(map[string]interface{})
env := make(map[string]interface{})
for _, f := range files {
switch {
@ -94,10 +112,13 @@ func LoadFiles(files []*BufferedFile) (*chart.Chart, error) {
return c, errors.Wrap(err, "cannot load Chart.lock")
}
case f.Name == "values.yaml":
c.Values = make(map[string]interface{})
if err := yaml.Unmarshal(f.Data, &c.Values); err != nil {
if err := yaml.Unmarshal(f.Data, &values); err != nil {
return c, errors.Wrap(err, "cannot load values.yaml")
}
case f.Name == envYaml:
if err := yaml.Unmarshal(f.Data, &env); err != nil {
return c, errors.Wrapf(err, "cannot load %s", envYaml)
}
case f.Name == "values.schema.json":
c.Schema = f.Data
@ -136,6 +157,9 @@ func LoadFiles(files []*BufferedFile) (*chart.Chart, error) {
}
}
if len(values) != 0 || len(env) != 0 {
c.Values = chart.MergeValues(values, env)
}
if err := c.Validate(); err != nil {
return c, err
}
@ -152,7 +176,7 @@ func LoadFiles(files []*BufferedFile) (*chart.Chart, error) {
return c, errors.Errorf("error unpacking tar in %s: expected %s, got %s", c.Name(), n, file.Name)
}
// Untar the chart and add to c.Dependencies
sc, err = LoadArchive(bytes.NewBuffer(file.Data))
sc, err = LoadArchiveWithEnvValues(bytes.NewBuffer(file.Data), envYaml)
default:
// We have to trim the prefix off of every file, and ignore any file
// that is in charts/, but isn't actually a chart.
@ -165,7 +189,7 @@ func LoadFiles(files []*BufferedFile) (*chart.Chart, error) {
f.Name = parts[1]
buff = append(buff, f)
}
sc, err = LoadFiles(buff)
sc, err = LoadFilesWithEnvValues(buff, envYaml)
}
if err != nil {

@ -36,7 +36,7 @@ func TestLoadDir(t *testing.T) {
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
c, err := l.Load()
c, err := l.Load("")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
@ -55,7 +55,7 @@ func TestLoadDirWithDevNull(t *testing.T) {
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
if _, err := l.Load(); err == nil {
if _, err := l.Load(""); err == nil {
t.Errorf("packages with an irregular file (/dev/null) should not load")
}
}
@ -75,7 +75,7 @@ func TestLoadDirWithSymlink(t *testing.T) {
t.Fatalf("Failed to load testdata: %s", err)
}
c, err := l.Load()
c, err := l.Load("")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
@ -90,7 +90,7 @@ func TestLoadV1(t *testing.T) {
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
c, err := l.Load()
c, err := l.Load("")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
@ -103,7 +103,7 @@ func TestLoadFileV1(t *testing.T) {
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
c, err := l.Load()
c, err := l.Load("")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
@ -116,7 +116,7 @@ func TestLoadFile(t *testing.T) {
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
c, err := l.Load()
c, err := l.Load("")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
@ -212,7 +212,7 @@ func TestLoadV2WithReqs(t *testing.T) {
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
c, err := l.Load()
c, err := l.Load("")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}

@ -30,10 +30,11 @@ import (
)
type Options struct {
ValueFiles []string
StringValues []string
Values []string
FileValues []string
ValueFiles []string
StringValues []string
Values []string
FileValues []string
EnvValuesFile string
}
// MergeValues merges values from files specified via -f/--values and directly

@ -31,6 +31,6 @@ func All(basedir string, values map[string]interface{}, namespace string, strict
linter := support.Linter{ChartDir: chartDir}
rules.Chartfile(&linter)
rules.Values(&linter)
rules.Templates(&linter, values, namespace, strict)
rules.Templates(&linter, values, namespace, strict, "")
return linter
}

@ -36,7 +36,7 @@ var (
)
// Templates lints the templates in the Linter.
func Templates(linter *support.Linter, values map[string]interface{}, namespace string, strict bool) {
func Templates(linter *support.Linter, values map[string]interface{}, namespace string, strict bool, envYaml string) {
path := "templates/"
templatesPath := filepath.Join(linter.ChartDir, path)
@ -48,7 +48,7 @@ func Templates(linter *support.Linter, values map[string]interface{}, namespace
}
// Load chart and parse templates, based on tiller/release_server
chart, err := loader.Load(linter.ChartDir)
chart, err := loader.LoadWithEnvValues(linter.ChartDir, envYaml)
chartLoaded := linter.RunLinterRule(support.ErrorSev, path, err)

@ -51,7 +51,7 @@ const strict = false
func TestTemplateParsing(t *testing.T) {
linter := support.Linter{ChartDir: templateTestBasedir}
Templates(&linter, values, namespace, strict)
Templates(&linter, values, namespace, strict, "")
res := linter.Messages
if len(res) != 1 {
@ -74,7 +74,7 @@ func TestTemplateIntegrationHappyPath(t *testing.T) {
defer os.Rename(ignoredTemplatePath, wrongTemplatePath)
linter := support.Linter{ChartDir: templateTestBasedir}
Templates(&linter, values, namespace, strict)
Templates(&linter, values, namespace, strict, "")
res := linter.Messages
if len(res) != 0 {
@ -84,7 +84,7 @@ func TestTemplateIntegrationHappyPath(t *testing.T) {
func TestV3Fail(t *testing.T) {
linter := support.Linter{ChartDir: "./testdata/v3-fail"}
Templates(&linter, values, namespace, strict)
Templates(&linter, values, namespace, strict, "")
res := linter.Messages
if len(res) != 3 {

Loading…
Cancel
Save