Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow : in --data-values-file path #609

Merged
merged 10 commits into from
Mar 30, 2022
71 changes: 63 additions & 8 deletions pkg/cmd/template/cmd_data_values_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package template_test

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -183,39 +184,58 @@ func TestDataValuesWithLibraryAttachedFlags(t *testing.T) {
tplBytes := []byte(`
#@ load("@ytt:library", "library")
#@ load("@ytt:template", "template")
#@ load("@ytt:data", "data")

root: #@ data.values
--- #@ template.replace(library.get("lib", alias="inst1").eval())`)

libTplBytes := []byte(`
#@ load("@ytt:library", "library")
#@ load("@ytt:template", "template")
#@ load("@ytt:data", "data")

lib-val: #@ data.values.lib_val
from_library: #@ data.values
--- #@ template.replace(library.get("nested-lib").eval())
`)

libValuesBytes := []byte(`
#@data/values
---
lib_val: override-me
val0: override-me
`)

nestedLibTplBytes := []byte(`
#@ load("@ytt:data", "data")

nested-lib-val: #@ data.values.nested_lib_val
from_nested_lib: #@ data.values
`)

nestedLibValuesBytes := []byte(`
#@data/values
---
nested_lib_val: override-me
val1: override-me
`)

expectedYAMLTplData := `lib-val: test
dvs2 := []byte(`val2: 2`)

dvs3 := []byte(`val3: 3`)

dvs4 := []byte(`val4: 4`)

dvs6 := []byte(`6`)

expectedYAMLTplData := `root:
val2: 2
---
from_library:
val0: 0
val3: 3
val4: 4
val5: "5"
val6: "6"
---
nested-lib-val: passes
from_nested_lib:
val1: 1
`

filesToProcess := files.NewSortedFiles([]*files.File{
Expand All @@ -230,8 +250,25 @@ nested-lib-val: passes
opts := cmdtpl.NewOptions()

opts.DataValuesFlags = cmdtpl.DataValuesFlags{
// TODO env and files?
KVsFromYAML: []string{"@~inst1:lib_val=test", "@~inst1@nested-lib:nested_lib_val=passes"},
KVsFromYAML: []string{"@~inst1:val0=0", "@~inst1@nested-lib:val1=1"},
FromFiles: []string{"c:\\User\\user\\dvs2.yml", "@~inst1:dvs3.yml", "@lib:D:\\User\\user\\dvs4.yml"},
EnvFromStrings: []string{"@lib:DVS"},
EnvironFunc: func() []string { return []string{"DVS_val5=5"} },
KVsFromFiles: []string{"@lib:val6=c:\\User\\user\\dvs6.yml"},
ReadFileFunc: func(path string) ([]byte, error) {
switch path {
case "c:\\User\\user\\dvs2.yml":
return dvs2, nil
case "dvs3.yml":
return dvs3, nil
case "D:\\User\\user\\dvs4.yml":
return dvs4, nil
case "c:\\User\\user\\dvs6.yml":
return dvs6, nil
default:
return nil, fmt.Errorf("Unknown file '%s'", path)
}
},
}

out := opts.RunWithFiles(cmdtpl.Input{Files: filesToProcess}, ui)
Expand Down Expand Up @@ -558,3 +595,21 @@ nested_val: nested_from_env
assert.Equal(t, "tpl.yml", file.RelativePath())
assert.Equal(t, expectedYAMLTplData, string(file.Bytes()))
}

func TestDataValuesWithInvalidFlagsFail(t *testing.T) {
t.Run("when `--data-value-yaml` has a `:` in the key name", func(t *testing.T) {

expectedErr := `Extracting data value from KV: Expected at most one library-key separator ':' in 'i:nt'`

ui := ui.NewTTY(false)
opts := cmdtpl.NewOptions()

opts.DataValuesFlags = cmdtpl.DataValuesFlags{
KVsFromYAML: []string{"i:nt=124"},
}

out := opts.RunWithFiles(cmdtpl.Input{}, ui)
require.Errorf(t, out.Err, expectedErr)
require.Equal(t, expectedErr, out.Err.Error())
})
}
52 changes: 32 additions & 20 deletions pkg/cmd/template/data_values_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ import (
"github.com/vmware-tanzu/carvel-ytt/pkg/files"
"github.com/vmware-tanzu/carvel-ytt/pkg/template"
"github.com/vmware-tanzu/carvel-ytt/pkg/workspace/datavalues"
"github.com/vmware-tanzu/carvel-ytt/pkg/workspace/ref"
"github.com/vmware-tanzu/carvel-ytt/pkg/yamlmeta"
yttoverlay "github.com/vmware-tanzu/carvel-ytt/pkg/yttlibrary/overlay"
)

const (
dvsKVSep = "="
dvsMapKeySep = "."
dvsKVSep = "="
dvsMapKeySep = "."
libraryKeySep = ":"
)

type DataValuesFlags struct {
Expand Down Expand Up @@ -135,8 +137,8 @@ func (s *DataValuesFlags) AsOverlays(strict bool) ([]*datavalues.Envelope, []*da
return overlayValues, libraryOverlays, nil
}

func (s *DataValuesFlags) file(path string, strict bool) ([]*datavalues.Envelope, error) {
libRef, path, err := s.libraryRefAndKey(path)
func (s *DataValuesFlags) file(fullPath string, strict bool) ([]*datavalues.Envelope, error) {
libRef, path, err := s.libraryRefAndString(fullPath)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -274,26 +276,36 @@ func (s *DataValuesFlags) kvFile(kv string) (*datavalues.Envelope, error) {
return datavalues.NewEnvelopeWithLibRef(overlay, libRef)
}

// libraryRefAndKey separates a library reference and a key and validates that no libraryKeySep exist in the key.
// libraryKeySep is disallowed in data value flag keys.
func (DataValuesFlags) libraryRefAndKey(key string) (string, string, error) {
cari-lynn marked this conversation as resolved.
Show resolved Hide resolved
const (
libraryKeySep = ":"
)

keyPieces := strings.Split(key, libraryKeySep)

switch len(keyPieces) {
case 1:
return "", key, nil
libRef, key, err := DataValuesFlags{}.libraryRefAndString(key)
if err != nil {
return "", "", err
}
if len(strings.Split(key, libraryKeySep)) > 1 {
// error on a common syntax mistake
return "", "", fmt.Errorf("Expected at most one library-key separator '%s' in '%s'", libraryKeySep, key)
}
return libRef, key, nil
}

case 2:
if len(keyPieces[0]) == 0 {
return "", "", fmt.Errorf("Expected library ref to not be empty")
// libraryRefAndString separates a library reference and a string.
// A library reference starts with ref.LibrarySep, and ends with the first occurrence of libraryKeySep.
func (DataValuesFlags) libraryRefAndString(str string) (string, string, error) {
cari-lynn marked this conversation as resolved.
Show resolved Hide resolved
if strings.HasPrefix(str, ref.LibrarySep) {
strPieces := strings.SplitN(str, libraryKeySep, 2)
switch len(strPieces) {
case 1:
return "", str, nil
case 2:
if len(strPieces[0]) == 1 {
return "", "", fmt.Errorf("Expected library ref to not be empty")
}
return strPieces[0], strPieces[1], nil
}
return keyPieces[0], keyPieces[1], nil

default:
return "", "", fmt.Errorf("Expected at most one library-key separator '%s' in '%s'", libraryKeySep, key)
}
return "", str, nil
}

func (s *DataValuesFlags) buildOverlay(keyPieces []string, value interface{}, desc string, line string) *yamlmeta.Document {
Expand Down