mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-11 17:12:51 +00:00
Merge pull request #3673 from natasha41575/PanicDuplicateKeys
Return error instead of panicking for duplicate keys
This commit is contained in:
@@ -30,12 +30,16 @@ func (p *NamespaceTransformerPlugin) Transform(m resmap.ResMap) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
for _, r := range m.Resources() {
|
for _, r := range m.Resources() {
|
||||||
if r.IsEmpty() {
|
empty, err := r.IsEmpty()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if empty {
|
||||||
// Don't mutate empty objects?
|
// Don't mutate empty objects?
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
r.StorePreviousId()
|
r.StorePreviousId()
|
||||||
err := r.ApplyFilter(namespace.Filter{
|
err = r.ApplyFilter(namespace.Filter{
|
||||||
Namespace: p.Namespace,
|
Namespace: p.Namespace,
|
||||||
FsSlice: p.FieldSpecs,
|
FsSlice: p.FieldSpecs,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ type Kunstructured interface {
|
|||||||
GetString(string) (string, error)
|
GetString(string) (string, error)
|
||||||
|
|
||||||
// Several uses.
|
// Several uses.
|
||||||
Map() map[string]interface{}
|
Map() (map[string]interface{}, error)
|
||||||
|
|
||||||
// Used by Resource.AsYAML and Resource.String
|
// Used by Resource.AsYAML and Resource.String
|
||||||
MarshalJSON() ([]byte, error)
|
MarshalJSON() ([]byte, error)
|
||||||
|
|||||||
@@ -399,7 +399,8 @@ func find(name string, resMap resmap.ResMap) *resource.Resource {
|
|||||||
func getCommand(r *resource.Resource) string {
|
func getCommand(r *resource.Resource) string {
|
||||||
var m map[string]interface{}
|
var m map[string]interface{}
|
||||||
var c []interface{}
|
var c []interface{}
|
||||||
m, _ = r.Map()["spec"].(map[string]interface{})
|
resourceMap, _ := r.Map()
|
||||||
|
m, _ = resourceMap["spec"].(map[string]interface{})
|
||||||
m, _ = m["template"].(map[string]interface{})
|
m, _ = m["template"].(map[string]interface{})
|
||||||
m, _ = m["spec"].(map[string]interface{})
|
m, _ = m["spec"].(map[string]interface{})
|
||||||
c, _ = m["containers"].([]interface{})
|
c, _ = m["containers"].([]interface{})
|
||||||
|
|||||||
@@ -43,7 +43,11 @@ func (o *multiTransformer) transform(m resmap.ResMap) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, r := range m.Resources() {
|
for _, r := range m.Resources() {
|
||||||
if r.IsEmpty() {
|
empty, err := r.IsEmpty()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if empty {
|
||||||
err := m.Remove(r.CurId())
|
err := m.Remove(r.CurId())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -306,13 +306,16 @@ items:
|
|||||||
assert.False(t, tc.exp.isErr)
|
assert.False(t, tc.exp.isErr)
|
||||||
assert.Equal(t, len(tc.exp.out), len(rs))
|
assert.Equal(t, len(tc.exp.out), len(rs))
|
||||||
for i := range rs {
|
for i := range rs {
|
||||||
|
rsMap, err := rs[i].Map()
|
||||||
|
assert.NoError(t, err)
|
||||||
assert.Equal(
|
assert.Equal(
|
||||||
t, fmt.Sprintf("%v", tc.exp.out[i]), fmt.Sprintf("%v", rs[i].Map()))
|
t, fmt.Sprintf("%v", tc.exp.out[i]), fmt.Sprintf("%v", rsMap))
|
||||||
if n != "listWithAnchors" {
|
if n != "listWithAnchors" {
|
||||||
// https://github.com/kubernetes-sigs/kustomize/issues/3271
|
// https://github.com/kubernetes-sigs/kustomize/issues/3271
|
||||||
if !reflect.DeepEqual(tc.exp.out[i], rs[i].Map()) {
|
m, _ := rs[i].Map()
|
||||||
|
if !reflect.DeepEqual(tc.exp.out[i], m) {
|
||||||
t.Fatalf("%s:\nexpected: %v\n actual: %v",
|
t.Fatalf("%s:\nexpected: %v\n actual: %v",
|
||||||
n, tc.exp.out[i], rs[i].Map())
|
n, tc.exp.out[i], m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -220,7 +220,7 @@ func (wn *WNode) GetString(path string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Map implements ifc.Kunstructured.
|
// Map implements ifc.Kunstructured.
|
||||||
func (wn *WNode) Map() map[string]interface{} {
|
func (wn *WNode) Map() (map[string]interface{}, error) {
|
||||||
return wn.node.Map()
|
return wn.node.Map()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -559,7 +559,9 @@ func TestGetSlice(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMapEmpty(t *testing.T) {
|
func TestMapEmpty(t *testing.T) {
|
||||||
assert.Equal(t, 0, len(NewWNode().Map()))
|
newNodeMap, err := NewWNode().Map()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 0, len(newNodeMap))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMap(t *testing.T) {
|
func TestMap(t *testing.T) {
|
||||||
@@ -577,7 +579,8 @@ func TestMap(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
actual := wn.Map()
|
actual, err := wn.Map()
|
||||||
|
assert.NoError(t, err)
|
||||||
if diff := cmp.Diff(expected, actual); diff != "" {
|
if diff := cmp.Diff(expected, actual); diff != "" {
|
||||||
t.Fatalf("actual map does not deep equal expected map:\n%v", diff)
|
t.Fatalf("actual map does not deep equal expected map:\n%v", diff)
|
||||||
}
|
}
|
||||||
|
|||||||
39
api/krusty/duplicatekeys_test.go
Normal file
39
api/krusty/duplicatekeys_test.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package krusty_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDuplicateKeys(t *testing.T) {
|
||||||
|
th := kusttest_test.MakeHarness(t)
|
||||||
|
th.WriteK(".", `
|
||||||
|
resources:
|
||||||
|
- resources.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF("resources.yaml", `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: podinfo
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: podinfo
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: podinfod
|
||||||
|
image: ghcr.io/stefanprodan/podinfo:5.0.3
|
||||||
|
command:
|
||||||
|
- ./podinfo
|
||||||
|
env:
|
||||||
|
- name: PODINFO_UI_COLOR
|
||||||
|
value: "#34577c"
|
||||||
|
env:
|
||||||
|
- name: PODINFO_UI_COLOR
|
||||||
|
value: "#34577c"
|
||||||
|
`)
|
||||||
|
th.RunWithErr(".", th.MakeDefaultOptions())
|
||||||
|
}
|
||||||
@@ -90,9 +90,14 @@ func (m *merginator) makeError(cd resource.ConflictDetector, index int) error {
|
|||||||
if conflict == nil {
|
if conflict == nil {
|
||||||
return fmt.Errorf("expected conflict for %s", m.incoming[index].OrgId())
|
return fmt.Errorf("expected conflict for %s", m.incoming[index].OrgId())
|
||||||
}
|
}
|
||||||
|
conflictMap, _ := conflict.Map()
|
||||||
|
incomingIndexMap, _ := m.incoming[index].Map()
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"conflict between %#v at index %d and %#v",
|
"conflict between %#v at index %d and %#v",
|
||||||
m.incoming[index].Map(), index, conflict.Map())
|
incomingIndexMap,
|
||||||
|
index,
|
||||||
|
conflictMap,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// findConflict looks for a conflict in a resource slice.
|
// findConflict looks for a conflict in a resource slice.
|
||||||
|
|||||||
@@ -119,7 +119,11 @@ func (m *resWrangler) Debug(title string) {
|
|||||||
fmt.Println("---")
|
fmt.Println("---")
|
||||||
}
|
}
|
||||||
fmt.Printf("# %d %s\n", i, r.OrgId())
|
fmt.Printf("# %d %s\n", i, r.OrgId())
|
||||||
blob, err := yaml.Marshal(r.Map())
|
m, err := r.Map()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
blob, err := yaml.Marshal(m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -272,7 +276,8 @@ func (m *resWrangler) AsYaml() ([]byte, error) {
|
|||||||
for _, res := range m.Resources() {
|
for _, res := range m.Resources() {
|
||||||
out, err := res.AsYAML()
|
out, err := res.AsYAML()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "%#v", res.Map())
|
m, _ := res.Map()
|
||||||
|
return nil, errors.Wrapf(err, "%#v", m)
|
||||||
}
|
}
|
||||||
if firstObj {
|
if firstObj {
|
||||||
firstObj = false
|
firstObj = false
|
||||||
@@ -602,14 +607,23 @@ func (m *resWrangler) ApplySmPatch(
|
|||||||
// Some unknown error, let it through.
|
// Some unknown error, let it through.
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !res.IsEmpty() {
|
empty, err := res.IsEmpty()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !empty {
|
||||||
|
m, _ := res.Map()
|
||||||
return errors.Wrapf(
|
return errors.Wrapf(
|
||||||
err, "with unexpectedly non-empty object map of size %d",
|
err, "with unexpectedly non-empty object map of size %d",
|
||||||
len(res.Map()))
|
len(m))
|
||||||
}
|
}
|
||||||
// Fall through to handle deleted object.
|
// Fall through to handle deleted object.
|
||||||
}
|
}
|
||||||
if !res.IsEmpty() {
|
empty, err := res.IsEmpty()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !empty {
|
||||||
// IsEmpty means all fields have been removed from the object.
|
// IsEmpty means all fields have been removed from the object.
|
||||||
// This can happen if a patch required deletion of the
|
// This can happen if a patch required deletion of the
|
||||||
// entire resource (not just a part of it). This means
|
// entire resource (not just a part of it). This means
|
||||||
|
|||||||
@@ -114,7 +114,11 @@ func (rf *Factory) SliceFromBytes(in []byte) ([]*Resource, error) {
|
|||||||
u := kunStructs[0]
|
u := kunStructs[0]
|
||||||
kunStructs = kunStructs[1:]
|
kunStructs = kunStructs[1:]
|
||||||
if strings.HasSuffix(u.GetKind(), "List") {
|
if strings.HasSuffix(u.GetKind(), "List") {
|
||||||
items := u.Map()["items"]
|
m, err := u.Map()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items := m["items"]
|
||||||
itemsSlice, ok := items.([]interface{})
|
itemsSlice, ok := items.([]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
if items == nil {
|
if items == nil {
|
||||||
|
|||||||
@@ -98,11 +98,12 @@ func (r *Resource) GetString(p string) (string, error) {
|
|||||||
return r.kunStr.GetString(p)
|
return r.kunStr.GetString(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resource) IsEmpty() bool {
|
func (r *Resource) IsEmpty() (bool, error) {
|
||||||
return len(r.kunStr.Map()) == 0
|
m, err := r.kunStr.Map()
|
||||||
|
return len(m) == 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resource) Map() map[string]interface{} {
|
func (r *Resource) Map() (map[string]interface{}, error) {
|
||||||
return r.kunStr.Map()
|
return r.kunStr.Map()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -488,7 +489,11 @@ func (r *Resource) ApplySmPatch(patch *Resource) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !r.IsEmpty() {
|
empty, err := r.IsEmpty()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !empty {
|
||||||
r.SetName(n)
|
r.SetName(n)
|
||||||
r.SetNamespace(ns)
|
r.SetNamespace(ns)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,11 @@ func (w Writer) WriteIndividualFiles(dirPath string, m resmap.ResMap) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w Writer) write(path, fName string, res *resource.Resource) error {
|
func (w Writer) write(path, fName string, res *resource.Resource) error {
|
||||||
yml, err := yaml.Marshal(res.Map())
|
m, err := res.Map()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
yml, err := yaml.Marshal(m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -816,17 +816,18 @@ func FromMap(m map[string]interface{}) (*RNode, error) {
|
|||||||
return Parse(string(c))
|
return Parse(string(c))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rn *RNode) Map() map[string]interface{} {
|
func (rn *RNode) Map() (map[string]interface{}, error) {
|
||||||
if rn == nil || rn.value == nil {
|
if rn == nil || rn.value == nil {
|
||||||
return make(map[string]interface{})
|
return make(map[string]interface{}), nil
|
||||||
}
|
}
|
||||||
var result map[string]interface{}
|
var result map[string]interface{}
|
||||||
if err := rn.value.Decode(&result); err != nil {
|
if err := rn.value.Decode(&result); err != nil {
|
||||||
// Should not be able to create an RNode that cannot be decoded;
|
// Should not be able to create an RNode that cannot be decoded;
|
||||||
// this is an unrecoverable error.
|
// this is an unrecoverable error.
|
||||||
log.Fatalf("failed to decode ynode: %v", err)
|
str, _ := rn.String()
|
||||||
|
return nil, fmt.Errorf("received error %w for the following resource:\n%s", err, str)
|
||||||
}
|
}
|
||||||
return result
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConvertJSONToYamlNode parses input json string and returns equivalent yaml node
|
// ConvertJSONToYamlNode parses input json string and returns equivalent yaml node
|
||||||
|
|||||||
@@ -386,7 +386,9 @@ func TestRNodeGetValidatedMetadata(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRNodeMapEmpty(t *testing.T) {
|
func TestRNodeMapEmpty(t *testing.T) {
|
||||||
assert.Equal(t, 0, len(NewRNode(nil).Map()))
|
newRNodeMap, err := NewRNode(nil).Map()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 0, len(newRNodeMap))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRNodeMap(t *testing.T) {
|
func TestRNodeMap(t *testing.T) {
|
||||||
@@ -411,7 +413,8 @@ func TestRNodeMap(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
actual := wn.Map()
|
actual, err := wn.Map()
|
||||||
|
assert.NoError(t, err)
|
||||||
if diff := cmp.Diff(expected, actual); diff != "" {
|
if diff := cmp.Diff(expected, actual); diff != "" {
|
||||||
t.Fatalf("actual map does not deep equal expected map:\n%v", diff)
|
t.Fatalf("actual map does not deep equal expected map:\n%v", diff)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,12 +34,16 @@ func (p *plugin) Transform(m resmap.ResMap) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
for _, r := range m.Resources() {
|
for _, r := range m.Resources() {
|
||||||
if r.IsEmpty() {
|
empty, err := r.IsEmpty()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if empty {
|
||||||
// Don't mutate empty objects?
|
// Don't mutate empty objects?
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
r.StorePreviousId()
|
r.StorePreviousId()
|
||||||
err := r.ApplyFilter(namespace.Filter{
|
err = r.ApplyFilter(namespace.Filter{
|
||||||
Namespace: p.Namespace,
|
Namespace: p.Namespace,
|
||||||
FsSlice: p.FieldSpecs,
|
FsSlice: p.FieldSpecs,
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user