diff --git a/go.mod b/go.mod index fc9b8561a..64ebfe307 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,8 @@ go 1.13 require ( github.com/BurntSushi/toml v0.3.1 github.com/DATA-DOG/go-sqlmock v1.4.1 - github.com/Masterminds/semver/v3 v3.0.3 - github.com/Masterminds/sprig/v3 v3.0.2 + github.com/Masterminds/semver/v3 v3.1.0 + github.com/Masterminds/sprig/v3 v3.1.0 github.com/Masterminds/squirrel v1.2.0 github.com/Masterminds/vcs v1.13.1 github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 @@ -31,9 +31,9 @@ require ( github.com/sirupsen/logrus v1.4.2 github.com/spf13/cobra v1.0.0 github.com/spf13/pflag v1.0.5 - github.com/stretchr/testify v1.4.0 + github.com/stretchr/testify v1.5.1 github.com/xeipuuv/gojsonschema v1.1.0 - golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 + golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 k8s.io/api v0.18.0 k8s.io/apiextensions-apiserver v0.18.0 k8s.io/apimachinery v0.18.0 diff --git a/go.sum b/go.sum index 3745d908a..0a51a72e8 100644 --- a/go.sum +++ b/go.sum @@ -31,8 +31,10 @@ github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RP github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver/v3 v3.0.3 h1:znjIyLfpXEDQjOIEWh+ehwpTU14UzUPub3c3sm36u14= github.com/Masterminds/semver/v3 v3.0.3/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Masterminds/sprig/v3 v3.0.2 h1:wz22D0CiSctrliXiI9ZO3HoNApweeRGftyDN+BQa3B8= -github.com/Masterminds/sprig/v3 v3.0.2/go.mod h1:oesJ8kPONMONaZgtiHNzUShJbksypC5kWczhZAf6+aU= +github.com/Masterminds/semver/v3 v3.1.0 h1:Y2lUDsFKVRSYGojLJ1yLxSXdMmMYTYls0rCvoqmMUQk= +github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Masterminds/sprig/v3 v3.1.0 h1:j7GpgZ7PdFqNsmncycTHsLmVPf5/3wJtlgW9TNDYD9Y= +github.com/Masterminds/sprig/v3 v3.1.0/go.mod h1:ONGMf7UfYGAbMXCZmQLy8x3lCDIPrEZE/rU8pmrbihA= github.com/Masterminds/squirrel v1.2.0 h1:K1NhbTO21BWG47IVR0OnIZuE0LZcXAYqywrC3Ko53KI= github.com/Masterminds/squirrel v1.2.0/go.mod h1:yaPeOnPG5ZRwL9oKdTsO/prlkPbXWZlRVMQ/gGlzIuA= github.com/Masterminds/vcs v1.13.1 h1:NL3G1X7/7xduQtA2sJLpVpfHTNBALVNSjob6KEjPXNQ= @@ -153,6 +155,8 @@ github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QL github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e h1:p1yVGRW3nmb85p1Sh1ZJSDm4A4iKLS5QNbvUHMgGu/M= +github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= @@ -319,9 +323,13 @@ github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0= github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= +github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs= +github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI= github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= +github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= @@ -503,6 +511,8 @@ github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= @@ -525,6 +535,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -580,6 +592,8 @@ golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d h1:9FCpayM9Egr1baVnV1SX0H golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo= golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 h1:bXoxMPcSLOq08zI3/c5dEBT6lE4eh+jOh886GHrn6V8= +golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= diff --git a/pkg/chartutil/coalesce.go b/pkg/chartutil/coalesce.go index bbdd5f21c..a5ff66aed 100644 --- a/pkg/chartutil/coalesce.go +++ b/pkg/chartutil/coalesce.go @@ -47,10 +47,7 @@ func CoalesceValues(chrt *chart.Chart, vals map[string]interface{}) (Values, err if valsCopy == nil { valsCopy = make(map[string]interface{}) } - if _, err := coalesce(chrt, valsCopy); err != nil { - return valsCopy, err - } - return coalesceDeps(chrt, valsCopy) + return coalesce(chrt, valsCopy) } // coalesce coalesces the dest values and the chart values, giving priority to the dest values. @@ -186,19 +183,18 @@ func CoalesceTables(dst, src map[string]interface{}) map[string]interface{} { // Because dest has higher precedence than src, dest values override src // values. for key, val := range src { - if istable(val) { - switch innerdst, ok := dst[key]; { - case !ok: - dst[key] = val - case istable(innerdst): - CoalesceTables(innerdst.(map[string]interface{}), val.(map[string]interface{})) - default: + if dv, ok := dst[key]; ok && dv == nil { + delete(dst, key) + } else if !ok { + dst[key] = val + } else if istable(val) { + if istable(dv) { + CoalesceTables(dv.(map[string]interface{}), val.(map[string]interface{})) + } else { log.Printf("warning: cannot overwrite table with non table for %s (%v)", key, val) } - } else if dv, ok := dst[key]; ok && istable(dv) { + } else if istable(dv) { log.Printf("warning: destination for %s is a table. Ignoring non-table value %v", key, val) - } else if !ok { // <- ok is still in scope from preceding conditional. - dst[key] = val } } return dst diff --git a/pkg/chartutil/coalesce_test.go b/pkg/chartutil/coalesce_test.go index 6e82de590..dc1017385 100644 --- a/pkg/chartutil/coalesce_test.go +++ b/pkg/chartutil/coalesce_test.go @@ -31,6 +31,8 @@ right: Null left: NULL front: ~ back: "" +nested: + boat: null global: name: Ishmael @@ -47,6 +49,10 @@ pequod: sail: true ahab: scope: whale + boat: null + nested: + foo: true + bar: null `) func TestCoalesceValues(t *testing.T) { @@ -86,6 +92,7 @@ func TestCoalesceValues(t *testing.T) { {"{{.pequod.name}}", "pequod"}, {"{{.pequod.ahab.name}}", "ahab"}, {"{{.pequod.ahab.scope}}", "whale"}, + {"{{.pequod.ahab.nested.foo}}", "true"}, {"{{.pequod.ahab.global.name}}", "Ishmael"}, {"{{.pequod.ahab.global.subject}}", "Queequeg"}, {"{{.pequod.ahab.global.harpooner}}", "Tashtego"}, @@ -114,6 +121,19 @@ func TestCoalesceValues(t *testing.T) { } } + if _, ok := v["nested"].(map[string]interface{})["boat"]; ok { + t.Error("Expected nested boat key to be removed, still present") + } + + subchart := v["pequod"].(map[string]interface{})["ahab"].(map[string]interface{}) + if _, ok := subchart["boat"]; ok { + t.Error("Expected subchart boat key to be removed, still present") + } + + if _, ok := subchart["nested"].(map[string]interface{})["bar"]; ok { + t.Error("Expected subchart nested bar key to be removed, still present") + } + // CoalesceValues should not mutate the passed arguments is.Equal(valsCopy, vals) } @@ -122,24 +142,28 @@ func TestCoalesceTables(t *testing.T) { dst := map[string]interface{}{ "name": "Ishmael", "address": map[string]interface{}{ - "street": "123 Spouter Inn Ct.", - "city": "Nantucket", + "street": "123 Spouter Inn Ct.", + "city": "Nantucket", + "country": nil, }, "details": map[string]interface{}{ "friends": []string{"Tashtego"}, }, "boat": "pequod", + "hole": nil, } src := map[string]interface{}{ "occupation": "whaler", "address": map[string]interface{}{ - "state": "MA", - "street": "234 Spouter Inn Ct.", + "state": "MA", + "street": "234 Spouter Inn Ct.", + "country": "US", }, "details": "empty", "boat": map[string]interface{}{ "mast": true, }, + "hole": "black", } // What we expect is that anything in dst overrides anything in src, but that @@ -170,6 +194,10 @@ func TestCoalesceTables(t *testing.T) { t.Errorf("Unexpected state: %v", addr["state"]) } + if _, ok = addr["country"]; ok { + t.Error("The country is not left out.") + } + if det, ok := dst["details"].(map[string]interface{}); !ok { t.Fatalf("Details is the wrong type: %v", dst["details"]) } else if _, ok := det["friends"]; !ok { @@ -179,4 +207,8 @@ func TestCoalesceTables(t *testing.T) { if dst["boat"].(string) != "pequod" { t.Errorf("Expected boat string, got %v", dst["boat"]) } + + if _, ok = dst["hole"]; ok { + t.Error("The hole still exists.") + } } diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go index a02cf4b98..28fb28e00 100644 --- a/pkg/chartutil/create.go +++ b/pkg/chartutil/create.go @@ -113,7 +113,7 @@ serviceAccount: annotations: {} # The name of the service account to use. # If not set and create is true, a name is generated using the fullname template - name: + name: "" podAnnotations: {} diff --git a/pkg/chartutil/testdata/moby/charts/pequod/charts/ahab/values.yaml b/pkg/chartutil/testdata/moby/charts/pequod/charts/ahab/values.yaml index 86c3f63aa..eee6980fa 100644 --- a/pkg/chartutil/testdata/moby/charts/pequod/charts/ahab/values.yaml +++ b/pkg/chartutil/testdata/moby/charts/pequod/charts/ahab/values.yaml @@ -1,2 +1,6 @@ scope: ahab name: ahab +boat: true +nested: + foo: false + bar: true diff --git a/pkg/chartutil/testdata/moby/values.yaml b/pkg/chartutil/testdata/moby/values.yaml index 54e1ce463..2169d7566 100644 --- a/pkg/chartutil/testdata/moby/values.yaml +++ b/pkg/chartutil/testdata/moby/values.yaml @@ -7,3 +7,5 @@ right: exists left: exists front: exists back: exists +nested: + boat: true diff --git a/pkg/engine/funcs_test.go b/pkg/engine/funcs_test.go index a405c1c47..ddcf6624c 100644 --- a/pkg/engine/funcs_test.go +++ b/pkg/engine/funcs_test.go @@ -103,3 +103,71 @@ func TestFuncs(t *testing.T) { assert.Equal(t, tt.expect, b.String(), tt.tpl) } } + +// This test to check a function provided by sprig is due to a change in a +// dependency of sprig. mergo in v0.3.9 changed the way it merges and only does +// public fields (i.e. those starting with a capital letter). This test, from +// sprig, fails in the new version. This is a behavior change for mergo that +// impacts sprig and Helm users. This test will help us to not update to a +// version of mergo (even accidentally) that causes a breaking change. See +// sprig changelog and notes for more details. +// Note, Go modules assume semver is never broken. So, there is no way to tell +// the tooling to not update to a minor or patch version. `go get -u` could be +// used to accidentally update mergo. This test and message should catch the +// problem and explain why it's happening. +func TestMerge(t *testing.T) { + dict := map[string]interface{}{ + "src2": map[string]interface{}{ + "h": 10, + "i": "i", + "j": "j", + }, + "src1": map[string]interface{}{ + "a": 1, + "b": 2, + "d": map[string]interface{}{ + "e": "four", + }, + "g": []int{6, 7}, + "i": "aye", + "j": "jay", + "k": map[string]interface{}{ + "l": false, + }, + }, + "dst": map[string]interface{}{ + "a": "one", + "c": 3, + "d": map[string]interface{}{ + "f": 5, + }, + "g": []int{8, 9}, + "i": "eye", + "k": map[string]interface{}{ + "l": true, + }, + }, + } + tpl := `{{merge .dst .src1 .src2}}` + var b strings.Builder + err := template.Must(template.New("test").Funcs(funcMap()).Parse(tpl)).Execute(&b, dict) + assert.NoError(t, err) + + expected := map[string]interface{}{ + "a": "one", // key overridden + "b": 2, // merged from src1 + "c": 3, // merged from dst + "d": map[string]interface{}{ // deep merge + "e": "four", + "f": 5, + }, + "g": []int{8, 9}, // overridden - arrays are not merged + "h": 10, // merged from src2 + "i": "eye", // overridden twice + "j": "jay", // overridden and merged + "k": map[string]interface{}{ + "l": true, // overridden + }, + } + assert.Equal(t, expected, dict["dst"]) +}