diff --git a/pkg/transformer/kubernetes/k8sutils.go b/pkg/transformer/kubernetes/k8sutils.go index 499f1ee5..69309b40 100644 --- a/pkg/transformer/kubernetes/k8sutils.go +++ b/pkg/transformer/kubernetes/k8sutils.go @@ -280,6 +280,34 @@ func marshal(obj runtime.Object, jsonFormat bool, indent int) (data []byte, err return } +// remove empty map[string]interface{} strings from the object +// +// Note: this function uses recursion, use it only objects created by the unmarshalled json. +// Passing cyclic structures to removeEmptyInterfaces will result in a stack overflow. +func removeEmptyInterfaces(obj interface{}) interface{} { + switch v := obj.(type) { + case []interface{}: + for i, val := range v { + if valMap, ok := val.(map[string]interface{}); (ok && len(valMap) == 0) || val == nil { + v = append(v[:i], v[i+1:]...) + } else { + v[i] = removeEmptyInterfaces(val) + } + } + case map[string]interface{}: + for k, val := range v { + if valMap, ok := val.(map[string]interface{}); (ok && len(valMap) == 0) || val == nil { + delete(v, k) + } else { + v[k] = removeEmptyInterfaces(val) + } + } + default: + return v + } + return obj +} + // Convert JSON to YAML. func jsonToYaml(j []byte, spaces int) ([]byte, error) { // Convert the JSON to an object. @@ -293,7 +321,7 @@ func jsonToYaml(j []byte, spaces int) ([]byte, error) { if err != nil { return nil, err } - + jsonObj = removeEmptyInterfaces(jsonObj) var b bytes.Buffer encoder := yaml.NewEncoder(&b) encoder.SetIndent(spaces) diff --git a/pkg/transformer/kubernetes/kubernetes_test.go b/pkg/transformer/kubernetes/kubernetes_test.go index ef1a0053..70f05652 100644 --- a/pkg/transformer/kubernetes/kubernetes_test.go +++ b/pkg/transformer/kubernetes/kubernetes_test.go @@ -1145,3 +1145,25 @@ func TestNamespaceGenerationBlank(t *testing.T) { } } } + +// Test empty interfaces removal +func TestRemoveEmptyInterfaces(t *testing.T) { + type Obj = map[string]interface{} + var testCases = []struct { + input interface{} + output interface{} + }{ + {Obj{"useless": Obj{}}, Obj{}}, + {Obj{"usefull": Obj{"usefull": "usefull"}}, Obj{"usefull": Obj{"usefull": "usefull"}}}, + {Obj{"usefull": Obj{"usefull": "usefull", "uselessdeep": Obj{}, "uselessnil": nil}}, Obj{"usefull": Obj{"usefull": "usefull"}}}, + {"test", "test"}, + } + for _, tc := range testCases { + t.Run(fmt.Sprintf("Test removeEmptyInterfaces(%s)", tc.input), func(t *testing.T) { + result := removeEmptyInterfaces(tc.input) + if !reflect.DeepEqual(result, tc.output) { + t.Errorf("Expected %v, got %v", tc.output, result) + } + }) + } +}