Skip to content

Commit 1b342a9

Browse files
authored
Merge pull request #498 from SumoLogic/fix/SUMO-211620/sumologic-content-idempotency
SUMO-211620 Reduce meaningless diff for sumologic_content.
2 parents d5f2129 + 44a91a0 commit 1b342a9

File tree

3 files changed

+169
-0
lines changed

3 files changed

+169
-0
lines changed

sumologic/resource_sumologic_content.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package sumologic
33
import (
44
"encoding/json"
55
"log"
6+
"strings"
67
"time"
78

89
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
@@ -28,6 +29,7 @@ func resourceSumologicContent() *schema.Resource {
2829
ValidateFunc: validation.StringIsJSON,
2930
Required: true,
3031
DiffSuppressFunc: structure.SuppressJsonDiff,
32+
StateFunc: configStateFunc,
3133
},
3234
},
3335
Timeouts: &schema.ResourceTimeout{
@@ -39,6 +41,73 @@ func resourceSumologicContent() *schema.Resource {
3941
}
4042
}
4143

44+
func configStateFunc(value interface{}) string {
45+
return normalizeConfig(value.(string))
46+
}
47+
48+
// modify json config to remove logically equivalent changes in terrafrom diff output
49+
// e.g. absent map entry vs map entry with null value
50+
// or absent map entry vs map entry with default value
51+
func normalizeConfig(originalConfig string) string {
52+
config, err := structure.ExpandJsonFromString(originalConfig)
53+
54+
if err != nil {
55+
log.Println("Couldn't expand config json from string")
56+
return originalConfig
57+
}
58+
59+
removeEmptyValues(config)
60+
fillPanelQueriesDefaultValues(config)
61+
62+
if config["theme"] != nil {
63+
config["theme"] = strings.ToLower(config["theme"].(string))
64+
}
65+
66+
children, ok := config["children"].([]interface{})
67+
if ok {
68+
for _, childItemObject := range children {
69+
childItemMap, ok := childItemObject.(map[string]interface{})
70+
if ok {
71+
fillPanelQueriesDefaultValues(childItemMap)
72+
}
73+
}
74+
}
75+
configString, err := structure.FlattenJsonToString(config)
76+
if err != nil {
77+
log.Println("Couldn't flatten config json to string")
78+
return originalConfig
79+
}
80+
81+
return configString
82+
}
83+
84+
func fillPanelQueriesDefaultValues(config map[string]interface{}) {
85+
if config["panels"] != nil {
86+
panels := config["panels"].([]interface{})
87+
88+
for _, panelInterface := range panels {
89+
panelItem := panelInterface.(map[string]interface{})
90+
91+
for range panelItem {
92+
if panelItem["queries"] != nil {
93+
queries := panelItem["queries"].([]interface{})
94+
for _, queryInterface := range queries {
95+
queryItem := queryInterface.(map[string]interface{})
96+
97+
if queryItem["outputCardinalityLimit"] == nil {
98+
queryItem["outputCardinalityLimit"] = 1000
99+
}
100+
101+
if queryItem["transient"] == nil {
102+
queryItem["transient"] = false
103+
}
104+
}
105+
}
106+
}
107+
}
108+
}
109+
}
110+
42111
func resourceSumologicContentRead(d *schema.ResourceData, meta interface{}) error {
43112
c := meta.(*Client)
44113
// Retrieve the content Id from the state
@@ -61,6 +130,9 @@ func resourceSumologicContentRead(d *schema.ResourceData, meta interface{}) erro
61130

62131
// Write the newly read content object into the schema
63132
d.Set("config", content.Config)
133+
134+
normalizedConfig := normalizeConfig(content.Config)
135+
d.Set("config", normalizedConfig)
64136
return nil
65137
}
66138

sumologic/util.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,3 +411,26 @@ func abs(value int64) int64 {
411411

412412
return value
413413
}
414+
415+
// traverse map and remove fields with null value and empty lists
416+
func removeEmptyValues(object interface{}) {
417+
mapObject, isMap := object.(map[string]interface{})
418+
if isMap {
419+
for key, value := range mapObject {
420+
if value == nil {
421+
delete(mapObject, key)
422+
}
423+
listObject, isList := value.([]interface{})
424+
if isList {
425+
if len(listObject) == 0 {
426+
delete(mapObject, key)
427+
} else {
428+
for _, listItem := range listObject {
429+
removeEmptyValues(listItem)
430+
}
431+
}
432+
}
433+
removeEmptyValues(value)
434+
}
435+
}
436+
}

sumologic/util_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package sumologic
22

33
import (
4+
"github.com/hashicorp/terraform-plugin-sdk/helper/structure"
5+
"reflect"
46
"testing"
57
)
68

@@ -200,3 +202,75 @@ func TestSuppressTimeDiff(t *testing.T) {
200202
}
201203
}
202204
}
205+
206+
func TestRemoveEmptyValues(t *testing.T) {
207+
inputJsonStr := `{
208+
"leaf": "b",
209+
"leafNull": null,
210+
"emptyList": [],
211+
"list": [
212+
1,
213+
2
214+
],
215+
"listOfObjects": [
216+
{
217+
"a": 1
218+
},
219+
{
220+
"b": null,
221+
"c": 3
222+
}
223+
],
224+
"map": {
225+
"e": 5,
226+
"a": null,
227+
"nestedMap": {
228+
"t": 6,
229+
"g": null,
230+
"nestedListEmpty": [],
231+
"nestedList": [
232+
1,
233+
2
234+
]
235+
}
236+
}
237+
}`
238+
239+
cleanedJsonStr := `
240+
{
241+
"leaf": "b",
242+
"list": [
243+
1,
244+
2
245+
],
246+
"listOfObjects": [
247+
{
248+
"a": 1
249+
},
250+
{
251+
"c": 3
252+
}
253+
],
254+
"map": {
255+
"e": 5,
256+
"nestedMap": {
257+
"nestedList": [
258+
1,
259+
2
260+
],
261+
"t": 6
262+
}
263+
}
264+
}
265+
`
266+
267+
inputMapObject, _ := structure.ExpandJsonFromString(inputJsonStr)
268+
removeEmptyValues(inputMapObject)
269+
cleanedMapObject, _ := structure.ExpandJsonFromString(cleanedJsonStr)
270+
isEqual := reflect.DeepEqual(inputMapObject, cleanedMapObject)
271+
272+
if !isEqual {
273+
processedJsonStr, _ := structure.FlattenJsonToString(inputMapObject)
274+
t.Fatal("Expected json after removing empty values:", cleanedJsonStr, "but was:", processedJsonStr)
275+
}
276+
}

0 commit comments

Comments
 (0)