Use gopkg.in/yaml.v2 for Helmfile v0.x (#609)

This should fix #435 for Helmfile v0.x releases since the next v0.150.0.
We introduce a new envvar to opt-in to the new YAML library, so that you can give it a shot before upgrading your Helmfile to v1. The same envvar can be used to opt-out of the new YAML library after you upgrade to Helmfile v1, giving you a more flexible migration story.

Signed-off-by: Yusuke Kuoka <ykuoka@gmail.com>
This commit is contained in:
Yusuke Kuoka
2023-01-04 18:17:24 +09:00
committed by GitHub
parent 490bb5d147
commit 4688cf0132
28 changed files with 357 additions and 246 deletions
+8 -1
View File
@@ -17,6 +17,7 @@ import (
"github.com/helmfile/helmfile/pkg/envvar"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/maputil"
"github.com/helmfile/helmfile/pkg/yaml"
)
@@ -286,11 +287,17 @@ func ToYaml(v interface{}) (string, error) {
}
func FromYaml(str string) (Values, error) {
m := Values{}
m := map[string]interface{}{}
if err := yaml.Unmarshal([]byte(str), &m); err != nil {
return nil, fmt.Errorf("%s, offending yaml: %s", err, str)
}
m, err := maputil.CastKeysToStrings(m)
if err != nil {
return nil, fmt.Errorf("%s, offending yaml: %s", err, str)
}
return m, nil
}
+56 -12
View File
@@ -5,12 +5,13 @@ import (
"fmt"
"io/fs"
"path/filepath"
"runtime"
goruntime "runtime"
"testing"
"github.com/stretchr/testify/require"
"github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/runtime"
)
func TestCreateFuncMap(t *testing.T) {
@@ -126,7 +127,7 @@ func TestReadDir(t *testing.T) {
"sampleDirectory/file3.yaml",
}
var expectedArray []string
if runtime.GOOS == "windows" {
if goruntime.GOOS == "windows" {
expectedArray = expectedArrayWindows
} else {
expectedArray = expectedArrayUnix
@@ -177,16 +178,29 @@ func TestReadFile_PassAbsPath(t *testing.T) {
require.Equal(t, actual, expected)
}
func TestToYaml_UnsupportedNestedMapKey(t *testing.T) {
expected := "foo:\n bar: BAR\n"
func TestToYaml_NestedMapInterfaceKey(t *testing.T) {
v := runtime.GoccyGoYaml
t.Cleanup(func() {
runtime.GoccyGoYaml = v
})
// nolint: unconvert
vals := Values(map[string]interface{}{
"foo": map[interface{}]interface{}{
"bar": "BAR",
},
})
runtime.GoccyGoYaml = true
actual, err := ToYaml(vals)
require.Equal(t, expected, actual)
require.Equal(t, "foo:\n bar: BAR\n", actual)
require.NoError(t, err, "expected nil, but got: %v, when type: map[interface {}]interface {}", err)
runtime.GoccyGoYaml = false
actual, err = ToYaml(vals)
require.Equal(t, "foo:\n bar: BAR\n", actual)
require.NoError(t, err, "expected nil, but got: %v, when type: map[interface {}]interface {}", err)
}
@@ -205,21 +219,51 @@ func TestToYaml(t *testing.T) {
require.Equal(t, expected, actual)
}
func TestFromYaml(t *testing.T) {
func testFromYaml(t *testing.T, goccyGoYaml bool, expected Values) {
t.Helper()
v := runtime.GoccyGoYaml
runtime.GoccyGoYaml = goccyGoYaml
t.Cleanup(func() {
runtime.GoccyGoYaml = v
})
raw := `foo:
bar: BAR
`
// nolint: unconvert
expected := Values(map[string]interface{}{
"foo": map[string]interface{}{
"bar": "BAR",
},
})
actual, err := FromYaml(raw)
require.NoError(t, err)
require.Equal(t, expected, actual)
}
func TestFromYaml(t *testing.T) {
t.Run("with goccy/go-yaml", func(t *testing.T) {
testFromYaml(
t,
true,
// nolint: unconvert
Values(map[string]interface{}{
"foo": map[string]interface{}{
"bar": "BAR",
},
}),
)
})
t.Run("with gopkg.in/yaml.v2", func(t *testing.T) {
testFromYaml(
t,
false,
// nolint: unconvert
Values(map[string]interface{}{
"foo": map[string]interface{}{
"bar": "BAR",
},
}),
)
})
}
func TestFromYamlToJson(t *testing.T) {
input := `foo:
bar: BAR