Skip to content

Commit 531fd4b

Browse files
authored
Merge pull request #655 from SumoLogic/zzhou-dashboard-panels-tf
DASH-1770: Add terraform support for dashboard trace and service map panels
2 parents 0c35346 + 4caf4c3 commit 531fd4b

File tree

3 files changed

+258
-6
lines changed

3 files changed

+258
-6
lines changed

sumologic/resource_sumologic_dashboard.go

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,22 @@ func getPanelSchema() map[string]*schema.Schema {
147147
Schema: getTextPanelSchema(),
148148
},
149149
},
150+
"traces_list_panel": {
151+
Type: schema.TypeList,
152+
Optional: true,
153+
MaxItems: 1,
154+
Elem: &schema.Resource{
155+
Schema: getTracesListPanelSchema(),
156+
},
157+
},
158+
"service_map_panel": {
159+
Type: schema.TypeList,
160+
Optional: true,
161+
MaxItems: 1,
162+
Elem: &schema.Resource{
163+
Schema: getServiceMapPanelSchema(),
164+
},
165+
},
150166
}
151167
}
152168

@@ -259,6 +275,64 @@ func getSumoSearchPanelSchema() map[string]*schema.Schema {
259275
return panelSchema
260276
}
261277

278+
func getTracesListPanelSchema() map[string]*schema.Schema {
279+
panelSchema := getPanelBaseSchema()
280+
281+
traceListPanelSchema := map[string]*schema.Schema{
282+
"queries": {
283+
Type: schema.TypeList,
284+
Optional: true,
285+
Elem: &schema.Resource{
286+
Schema: getSumoSearchPanelQuerySchema(),
287+
},
288+
MaxItems: 6,
289+
},
290+
"time_range": {
291+
Type: schema.TypeList,
292+
Optional: true,
293+
MaxItems: 1,
294+
Elem: &schema.Resource{
295+
Schema: GetTimeRangeSchema(),
296+
},
297+
},
298+
}
299+
300+
for k, v := range traceListPanelSchema {
301+
panelSchema[k] = v
302+
}
303+
304+
return panelSchema
305+
}
306+
307+
func getServiceMapPanelSchema() map[string]*schema.Schema {
308+
panelSchema := getPanelBaseSchema()
309+
310+
serviceMapPanelSchema := map[string]*schema.Schema{
311+
"application": {
312+
Type: schema.TypeString,
313+
Optional: true,
314+
},
315+
"service": {
316+
Type: schema.TypeString,
317+
Optional: true,
318+
},
319+
"show_remote_services": {
320+
Type: schema.TypeBool,
321+
Optional: true,
322+
},
323+
"environment": {
324+
Type: schema.TypeString,
325+
Optional: true,
326+
},
327+
}
328+
329+
for k, v := range serviceMapPanelSchema {
330+
panelSchema[k] = v
331+
}
332+
333+
return panelSchema
334+
}
335+
262336
func getSumoSearchPanelQuerySchema() map[string]*schema.Schema {
263337
return map[string]*schema.Schema{
264338
"query_string": {
@@ -599,6 +673,14 @@ func getPanel(tfPanel map[string]interface{}) interface{} {
599673
if tfSearchPanel, ok := val[0].(map[string]interface{}); ok {
600674
return getSumoSearchPanel(tfSearchPanel)
601675
}
676+
} else if val := tfPanel["traces_list_panel"].([]interface{}); len(val) == 1 {
677+
if tfTracesListPanel, ok := val[0].(map[string]interface{}); ok {
678+
return getTracesListPanel(tfTracesListPanel)
679+
}
680+
} else if val := tfPanel["service_map_panel"].([]interface{}); len(val) == 1 {
681+
if tfServiceMapPanel, ok := val[0].(map[string]interface{}); ok {
682+
return getServiceMapPanel(tfServiceMapPanel)
683+
}
602684
}
603685
return nil
604686
}
@@ -674,6 +756,64 @@ func getSumoSearchPanel(tfSearchPanel map[string]interface{}) interface{} {
674756
return searchPanel
675757
}
676758

759+
func getTracesListPanel(tfPanel map[string]interface{}) interface{} {
760+
var panel TracesListPanel
761+
panel.PanelType = "TracesListPanel"
762+
763+
panel.Key = tfPanel["key"].(string)
764+
if title, ok := tfPanel["title"].(string); ok {
765+
panel.Title = title
766+
}
767+
if visualSettings, ok := tfPanel["visual_settings"].(string); ok {
768+
panel.VisualSettings = visualSettings
769+
}
770+
if consistentVisualSettings, ok := tfPanel["keep_visual_settings_consistent_with_parent"].(bool); ok {
771+
panel.KeepVisualSettingsConsistentWithParent = consistentVisualSettings
772+
}
773+
tfQueries := tfPanel["query"].([]interface{})
774+
var queries []SearchPanelQuery
775+
for _, tfQuery := range tfQueries {
776+
query := getSearchPanelQuery(tfQuery.(map[string]interface{}))
777+
queries = append(queries, query)
778+
}
779+
panel.Queries = queries
780+
if timeRangeData, ok := tfPanel["time_range"].([]interface{}); ok && len(timeRangeData) > 0 {
781+
panel.TimeRange = GetTimeRange(timeRangeData[0].(map[string]interface{}))
782+
}
783+
784+
return panel
785+
}
786+
787+
func getServiceMapPanel(tfPanel map[string]interface{}) interface{} {
788+
var panel ServiceMapPanel
789+
panel.PanelType = "ServiceMapPanel"
790+
791+
panel.Key = tfPanel["key"].(string)
792+
if title, ok := tfPanel["title"].(string); ok {
793+
panel.Title = title
794+
}
795+
if visualSettings, ok := tfPanel["visual_settings"].(string); ok {
796+
panel.VisualSettings = visualSettings
797+
}
798+
if consistentVisualSettings, ok := tfPanel["keep_visual_settings_consistent_with_parent"].(bool); ok {
799+
panel.KeepVisualSettingsConsistentWithParent = consistentVisualSettings
800+
}
801+
if application, ok := tfPanel["application"].(string); ok {
802+
panel.Application = application
803+
}
804+
if service, ok := tfPanel["service"].(string); ok {
805+
panel.Service = service
806+
}
807+
if showRemoteServices, ok := tfPanel["show_remote_services"].(bool); ok {
808+
panel.ShowRemoteServices = showRemoteServices
809+
}
810+
if environment, ok := tfPanel["environment"].(string); ok {
811+
panel.Environment = environment
812+
}
813+
814+
return panel
815+
}
816+
677817
func getSearchPanelQuery(tfQuery map[string]interface{}) SearchPanelQuery {
678818
var query SearchPanelQuery
679819

@@ -1004,6 +1144,10 @@ func getTerraformPanels(panels []interface{}) []map[string]interface{} {
10041144
tfPanel["text_panel"] = getTerraformTextPanel(panel)
10051145
} else if panel["panelType"] == "SumoSearchPanel" {
10061146
tfPanel["sumo_search_panel"] = getTerraformSearchPanel(panel)
1147+
} else if panel["panelType"] == "TracesListPanel" {
1148+
tfPanel["traces_list_panel"] = getTerraformTracesListPanel(panel)
1149+
} else if panel["panelType"] == "ServiceMapPanel" {
1150+
tfPanel["service_map_panel"] = getTerraformServiceMapPanel(panel)
10071151
}
10081152

10091153
tfPanels[i] = tfPanel
@@ -1060,6 +1204,60 @@ func getTerraformSearchPanel(searchPanel map[string]interface{}) TerraformObject
10601204
return tfSearchPanel
10611205
}
10621206

1207+
func getTerraformTracesListPanel(panel map[string]interface{}) TerraformObject {
1208+
tfTracesListPanel := MakeTerraformObject()
1209+
1210+
tfTracesListPanel[0]["key"] = panel["key"]
1211+
1212+
if title, ok := panel["title"].(string); ok {
1213+
tfTracesListPanel[0]["title"] = title
1214+
}
1215+
if visualSettings, ok := panel["visual_settings"].(string); ok {
1216+
tfTracesListPanel[0]["visual_settings"] = visualSettings
1217+
}
1218+
if keepVisualSettingsConsistentWithParent, ok := panel["keepVisualSettingsConsistentWithParent"]; ok {
1219+
tfTracesListPanel[0]["keep_visual_settings_consistent_with_parent"] = keepVisualSettingsConsistentWithParent
1220+
}
1221+
1222+
tfTracesListPanel[0]["queries"] = getTerraformSearchPanelQuery(panel["queries"].([]interface{}))
1223+
1224+
if timeRange := panel["timeRange"]; timeRange != nil {
1225+
tfTracesListPanel[0]["time_range"] = GetTerraformTimeRange(timeRange.(map[string]interface{}))
1226+
}
1227+
1228+
return tfTracesListPanel
1229+
}
1230+
1231+
func getTerraformServiceMapPanel(panel map[string]interface{}) TerraformObject {
1232+
tfServiceMapPanel := MakeTerraformObject()
1233+
1234+
tfServiceMapPanel[0]["key"] = panel["key"]
1235+
1236+
if title, ok := panel["title"].(string); ok {
1237+
tfServiceMapPanel[0]["title"] = title
1238+
}
1239+
if visualSettings, ok := panel["visual_settings"].(string); ok {
1240+
tfServiceMapPanel[0]["visual_settings"] = visualSettings
1241+
}
1242+
if keepVisualSettingsConsistentWithParent, ok := panel["keepVisualSettingsConsistentWithParent"]; ok {
1243+
tfServiceMapPanel[0]["keep_visual_settings_consistent_with_parent"] = keepVisualSettingsConsistentWithParent
1244+
}
1245+
if application, ok := panel["application"].(string); ok {
1246+
tfServiceMapPanel[0]["application"] = application
1247+
}
1248+
if service, ok := panel["service"].(string); ok {
1249+
tfServiceMapPanel[0]["service"] = service
1250+
}
1251+
if showRemoteServices, ok := panel["show_remote_services"].(bool); ok {
1252+
tfServiceMapPanel[0]["show_remote_services"] = showRemoteServices
1253+
}
1254+
if environment, ok := panel["environment"].(string); ok {
1255+
tfServiceMapPanel[0]["environment"] = environment
1256+
}
1257+
1258+
return tfServiceMapPanel
1259+
}
1260+
10631261
func getTerraformSearchPanelQuery(queries []interface{}) []map[string]interface{} {
10641262
tfPanelQueries := make([]map[string]interface{}, len(queries))
10651263

sumologic/resource_sumologic_dashboard_test.go

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,14 @@ func TestAccSumologicDashboard_create(t *testing.T) {
5858
Title: "Text Panel Title",
5959
Text: "This is a text panel",
6060
}
61+
serviceMapPanel := ServiceMapPanel{
62+
Key: "service-map-panel-001",
63+
Title: "Service Map Panel Title",
64+
Application: "example-app",
65+
Service: "example-service",
66+
ShowRemoteServices: false,
67+
Environment: "example-env",
68+
}
6169
layout := GridLayout{
6270
LayoutStructures: []LayoutStructure{
6371
{
@@ -86,7 +94,7 @@ func TestAccSumologicDashboard_create(t *testing.T) {
8694
Steps: []resource.TestStep{
8795
{
8896
Config: dashboardCreateConfig(title, description, theme, refreshInterval,
89-
topologyLabel, domain, literalRangeName, relativeTime, textPanel, layout, variable),
97+
topologyLabel, domain, literalRangeName, relativeTime, textPanel, serviceMapPanel, layout, variable),
9098
Check: resource.ComposeTestCheckFunc(
9199
testAccCheckDashboardExists("sumologic_dashboard.tf_crud_test", &dashboard, t),
92100
resource.TestCheckResourceAttr("sumologic_dashboard.tf_crud_test",
@@ -110,7 +118,7 @@ func TestAccSumologicDashboard_create(t *testing.T) {
110118
"time_range.0.begin_bounded_time_range.0.to.0.relative_time_range.0.relative_time",
111119
canonicalRelativeTime),
112120
resource.TestCheckResourceAttr("sumologic_dashboard.tf_crud_test",
113-
"panel.#", "1"),
121+
"panel.#", "2"),
114122
resource.TestCheckResourceAttr("sumologic_dashboard.tf_crud_test",
115123
"panel.0.text_panel.0.key", textPanel.Key),
116124
resource.TestCheckResourceAttr("sumologic_dashboard.tf_crud_test",
@@ -145,6 +153,14 @@ func TestAccSumologicDashboard_update(t *testing.T) {
145153
Title: "Text Panel Title",
146154
Text: "This is a text panel",
147155
}
156+
serviceMapPanel := ServiceMapPanel{
157+
Key: "service-map-panel-001",
158+
Title: "Service Map Panel Title",
159+
Application: "example-app",
160+
Service: "example-service",
161+
ShowRemoteServices: false,
162+
Environment: "example-env",
163+
}
148164
layout := GridLayout{
149165
LayoutStructures: []LayoutStructure{
150166
{
@@ -223,7 +239,7 @@ func TestAccSumologicDashboard_update(t *testing.T) {
223239
Steps: []resource.TestStep{
224240
{
225241
Config: dashboardCreateConfig(title, description, theme, refreshInterval,
226-
topologyLabel, domain, literalRangeName, relativeTime, textPanel, layout, csvVariable),
242+
topologyLabel, domain, literalRangeName, relativeTime, textPanel, serviceMapPanel, layout, csvVariable),
227243
Check: resource.ComposeTestCheckFunc(
228244
testAccCheckDashboardExists("sumologic_dashboard.tf_crud_test", &dashboard, t),
229245
resource.TestCheckResourceAttr("sumologic_dashboard.tf_crud_test",
@@ -243,7 +259,7 @@ func TestAccSumologicDashboard_update(t *testing.T) {
243259
"time_range.0.begin_bounded_time_range.0.to.0.relative_time_range.0.relative_time",
244260
canonicalRelativeTime),
245261
resource.TestCheckResourceAttr("sumologic_dashboard.tf_crud_test",
246-
"panel.#", "1"),
262+
"panel.#", "2"),
247263
resource.TestCheckResourceAttr("sumologic_dashboard.tf_crud_test",
248264
"panel.0.text_panel.0.key", textPanel.Key),
249265
resource.TestCheckResourceAttr("sumologic_dashboard.tf_crud_test",
@@ -427,7 +443,7 @@ func dashboardImportConfig(title string) string {
427443
}
428444

429445
func dashboardCreateConfig(title string, description string, theme string, refreshInterval int,
430-
topologyLabel TopologyLabel, domain string, rangeName string, relativeTime string, textPanel TextPanel,
446+
topologyLabel TopologyLabel, domain string, rangeName string, relativeTime string, textPanel TextPanel, serviceMapPanel ServiceMapPanel,
431447
layout GridLayout, variable Variable) string {
432448

433449
return fmt.Sprintf(`
@@ -473,6 +489,17 @@ func dashboardCreateConfig(title string, description string, theme string, refre
473489
text = "%s"
474490
}
475491
}
492+
panel {
493+
service_map_panel {
494+
key = "%s"
495+
title = "%s"
496+
visual_settings = ""
497+
application = "%s"
498+
service = "%s"
499+
show_remote_services = %t
500+
environment = "%s"
501+
}
502+
}
476503
layout {
477504
grid {
478505
layout_structure {
@@ -497,7 +524,8 @@ func dashboardCreateConfig(title string, description string, theme string, refre
497524
}`,
498525
title, description, refreshInterval, theme, firstLabelKey, topologyLabel.Data[firstLabelKey][0],
499526
secondLabelKey, topologyLabel.Data[secondLabelKey][0], topologyLabel.Data[secondLabelKey][1],
500-
domain, rangeName, relativeTime, textPanel.Key, textPanel.Title, textPanel.Text,
527+
domain, rangeName, relativeTime, textPanel.Key, textPanel.Title, textPanel.Text, serviceMapPanel.Key,
528+
serviceMapPanel.Title, serviceMapPanel.Application, serviceMapPanel.Service, serviceMapPanel.ShowRemoteServices, serviceMapPanel.Environment,
501529
layout.LayoutStructures[0].Key, variable.Name, variable.DisplayName, variable.DefaultValue,
502530
variable.SourceDefinition.(CsvVariableSourceDefinition).Values,
503531
)

sumologic/sumologic_dashboard.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,32 @@ type SumoSearchPanel struct {
9999
LinkedDashboards []LinkedDashboard `json:"linkedDashboards"`
100100
}
101101

102+
type TracesListPanel struct {
103+
Id string `json:"id,omitempty"`
104+
Key string `json:"key"`
105+
Title string `json:"title"`
106+
PanelType string `json:"panelType"`
107+
VisualSettings string `json:"visualSettings"`
108+
KeepVisualSettingsConsistentWithParent bool `json:"keepVisualSettingsConsistentWithParent"`
109+
// Trace panel related properties
110+
Queries []SearchPanelQuery `json:"queries"`
111+
TimeRange interface{} `json:"timeRange"`
112+
}
113+
114+
type ServiceMapPanel struct {
115+
Id string `json:"id,omitempty"`
116+
Key string `json:"key"`
117+
Title string `json:"title"`
118+
VisualSettings string `json:"visualSettings"`
119+
PanelType string `json:"panelType"`
120+
KeepVisualSettingsConsistentWithParent bool `json:"keepVisualSettingsConsistentWithParent"`
121+
// Service map panel related properties
122+
Application string `json:"application,omitempty"`
123+
Service string `json:"service,omitempty"`
124+
ShowRemoteServices bool `json:"showRemoteServices,omitempty"`
125+
Environment string `json:"environment,omitempty"`
126+
}
127+
102128
type SearchPanelQuery struct {
103129
QueryString string `json:"queryString"`
104130
QueryType string `json:"queryType"`

0 commit comments

Comments
 (0)