Skip to content

Commit

Permalink
Add Datadog dashboard SLO widget support (#355)
Browse files Browse the repository at this point in the history
  • Loading branch information
mbarrien authored and Slavek Kabrda committed Nov 27, 2019
1 parent a7e5d7c commit 52d30ec
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 1 deletion.
131 changes: 131 additions & 0 deletions datadog/resource_datadog_dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,15 @@ func getNonGroupWidgetSchema() map[string]*schema.Schema {
Schema: getScatterplotDefinitionSchema(),
},
},
"service_level_objective_definition": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Description: "The definition for a Service Level Objective widget",
Elem: &schema.Resource{
Schema: getServiceLevelObjectiveDefinitionSchema(),
},
},
"timeseries_definition": {
Type: schema.TypeList,
Optional: true,
Expand Down Expand Up @@ -594,6 +603,10 @@ func buildDatadogWidget(terraformWidget map[string]interface{}) (*datadog.BoardW
if scatterplotDefinition, ok := _def[0].(map[string]interface{}); ok {
datadogWidget.Definition = buildDatadogScatterplotDefinition(scatterplotDefinition)
}
} else if _def, ok := terraformWidget["service_level_objective_definition"].([]interface{}); ok && len(_def) > 0 {
if serviceLevelObjectiveDefinition, ok := _def[0].(map[string]interface{}); ok {
datadogWidget.Definition = buildDatadogServiceLevelObjectiveDefinition(serviceLevelObjectiveDefinition)
}
} else if _def, ok := terraformWidget["timeseries_definition"].([]interface{}); ok && len(_def) > 0 {
if timeseriesDefinition, ok := _def[0].(map[string]interface{}); ok {
datadogWidget.Definition = buildDatadogTimeseriesDefinition(timeseriesDefinition)
Expand Down Expand Up @@ -713,6 +726,10 @@ func buildTerraformWidget(datadogWidget datadog.BoardWidget) (map[string]interfa
datadogDefinition := datadogWidget.Definition.(datadog.ScatterplotDefinition)
terraformDefinition := buildTerraformScatterplotDefinition(datadogDefinition)
terraformWidget["scatterplot_definition"] = []map[string]interface{}{terraformDefinition}
case datadog.SERVICE_LEVEL_OBJECTIVE_WIDGET:
datadogDefinition := datadogWidget.Definition.(datadog.ServiceLevelObjectiveDefinition)
terraformDefinition := buildTerraformServiceLevelObjectiveDefinition(datadogDefinition)
terraformWidget["service_level_objective_definition"] = []map[string]interface{}{terraformDefinition}
case datadog.TIMESERIES_WIDGET:
datadogDefinition := datadogWidget.Definition.(datadog.TimeseriesDefinition)
terraformDefinition := buildTerraformTimeseriesDefinition(datadogDefinition)
Expand Down Expand Up @@ -2940,6 +2957,120 @@ func buildTerraformScatterplotRequest(datadogScatterplotRequest *datadog.Scatter
return &terraformRequest
}

//
// Service Level Objective Widget Definition helpers
//

func getServiceLevelObjectiveDefinitionSchema() map[string]*schema.Schema {
return map[string]*schema.Schema{
"title": {
Type: schema.TypeString,
Optional: true,
},
"title_size": {
Type: schema.TypeString,
Optional: true,
},
"title_align": {
Type: schema.TypeString,
Optional: true,
},
"view_type": {
Type: schema.TypeString,
Required: true,
},
"slo_id": {
Type: schema.TypeString,
Required: true,
},
"show_error_budget": {
Type: schema.TypeBool,
Optional: true,
},
"view_mode": {
Type: schema.TypeString,
Required: true,
},
"time_windows": {
Type: schema.TypeList,
Required: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
}
}

func buildDatadogServiceLevelObjectiveDefinition(terraformDefinition map[string]interface{}) *datadog.ServiceLevelObjectiveDefinition {
datadogDefinition := &datadog.ServiceLevelObjectiveDefinition{}
// Required params
datadogDefinition.SetType(datadog.SERVICE_LEVEL_OBJECTIVE_WIDGET)

// Optional params
if v, ok := terraformDefinition["title"].(string); ok && len(v) != 0 {
datadogDefinition.SetTitle(v)
}
if v, ok := terraformDefinition["title_size"].(string); ok && len(v) != 0 {
datadogDefinition.SetTitleSize(v)
}
if v, ok := terraformDefinition["title_align"].(string); ok && len(v) != 0 {
datadogDefinition.SetTitleAlign(v)
}
if v, ok := terraformDefinition["view_type"].(string); ok && len(v) != 0 {
datadogDefinition.SetViewType(v)
}
if v, ok := terraformDefinition["slo_id"].(string); ok && len(v) != 0 {
datadogDefinition.SetServiceLevelObjectiveID(v)
}
if v, ok := terraformDefinition["show_error_budget"].(bool); ok {
datadogDefinition.SetShowErrorBudget(v)
}
if v, ok := terraformDefinition["view_mode"].(string); ok && len(v) != 0 {
datadogDefinition.SetViewMode(v)
}
if terraformTimeWindows, ok := terraformDefinition["time_windows"].([]interface{}); ok && len(terraformTimeWindows) > 0 {
datadogTimeWindows := make([]string, len(terraformTimeWindows))
for i, timeWindows := range terraformTimeWindows {
datadogTimeWindows[i] = timeWindows.(string)
}
datadogDefinition.TimeWindows = datadogTimeWindows
}
return datadogDefinition
}

func buildTerraformServiceLevelObjectiveDefinition(datadogDefinition datadog.ServiceLevelObjectiveDefinition) map[string]interface{} {
terraformDefinition := map[string]interface{}{}
// Required params
// Optional params
if title, ok := datadogDefinition.GetTitleOk(); ok {
terraformDefinition["title"] = title
}
if titleSize, ok := datadogDefinition.GetTitleSizeOk(); ok {
terraformDefinition["title_size"] = titleSize
}
if titleAlign, ok := datadogDefinition.GetTitleAlignOk(); ok {
terraformDefinition["title_align"] = titleAlign
}
if viewType, ok := datadogDefinition.GetViewTypeOk(); ok {
terraformDefinition["view_type"] = viewType
}
if sloID, ok := datadogDefinition.GetServiceLevelObjectiveIDOk(); ok {
terraformDefinition["slo_id"] = sloID
}
if showErrorBudget, ok := datadogDefinition.GetShowErrorBudgetOk(); ok {
terraformDefinition["show_error_budget"] = showErrorBudget
}
if viewMode, ok := datadogDefinition.GetViewModeOk(); ok {
terraformDefinition["view_mode"] = viewMode
}
if datadogDefinition.TimeWindows != nil {
terraformTimeWindows := make([]string, len(datadogDefinition.TimeWindows))
for i, datadogTimeWindow := range datadogDefinition.TimeWindows {
terraformTimeWindows[i] = datadogTimeWindow
}
terraformDefinition["time_windows"] = terraformTimeWindows
}
return terraformDefinition
}

//
// Timeseries Widget Definition helpers
//
Expand Down
21 changes: 20 additions & 1 deletion datadog/resource_datadog_dashboard_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,16 @@ resource "datadog_dashboard" "ordered_dashboard" {
}
}
}
widget {
service_level_objective_definition {
title = "Widget Title"
view_type = "detail"
slo_id = "56789"
show_error_budget = true
view_mode = "overall"
time_windows = ["7d", "previous_week"]
}
}
template_variable {
name = "var_1"
prefix = "host"
Expand Down Expand Up @@ -517,7 +527,7 @@ func TestAccDatadogDashboard_update(t *testing.T) {
resource.TestCheckResourceAttr("datadog_dashboard.ordered_dashboard", "description", "Created using the Datadog provider in Terraform"),
resource.TestCheckResourceAttr("datadog_dashboard.ordered_dashboard", "layout_type", "ordered"),
resource.TestCheckResourceAttr("datadog_dashboard.ordered_dashboard", "is_read_only", "true"),
resource.TestCheckResourceAttr("datadog_dashboard.ordered_dashboard", "widget.#", "13"),
resource.TestCheckResourceAttr("datadog_dashboard.ordered_dashboard", "widget.#", "14"),
// Alert Graph widget
resource.TestCheckResourceAttr("datadog_dashboard.ordered_dashboard", "widget.0.alert_graph_definition.0.alert_id", "895605"),
resource.TestCheckResourceAttr("datadog_dashboard.ordered_dashboard", "widget.0.alert_graph_definition.0.viz_type", "timeseries"),
Expand Down Expand Up @@ -702,6 +712,15 @@ func TestAccDatadogDashboard_update(t *testing.T) {
resource.TestCheckResourceAttr("datadog_dashboard.ordered_dashboard", "widget.12.group_definition.0.widget.1.alert_graph_definition.0.viz_type", "toplist"),
resource.TestCheckResourceAttr("datadog_dashboard.ordered_dashboard", "widget.12.group_definition.0.widget.1.alert_graph_definition.0.title", "Alert Graph"),
resource.TestCheckResourceAttr("datadog_dashboard.ordered_dashboard", "widget.12.group_definition.0.widget.1.alert_graph_definition.0.time.live_span", "1h"),
// Service Level Objective widget
resource.TestCheckResourceAttr("datadog_dashboard.ordered_dashboard", "widget.13.service_level_objective_definition.0.title", "Widget Title"),
resource.TestCheckResourceAttr("datadog_dashboard.ordered_dashboard", "widget.13.service_level_objective_definition.0.view_type", "detail"),
resource.TestCheckResourceAttr("datadog_dashboard.ordered_dashboard", "widget.13.service_level_objective_definition.0.slo_id", "56789"),
resource.TestCheckResourceAttr("datadog_dashboard.ordered_dashboard", "widget.13.service_level_objective_definition.0.show_error_budget", "true"),
resource.TestCheckResourceAttr("datadog_dashboard.ordered_dashboard", "widget.13.service_level_objective_definition.0.view_mode", "overall"),
resource.TestCheckResourceAttr("datadog_dashboard.ordered_dashboard", "widget.13.service_level_objective_definition.0.time_windows.#", "2"),
resource.TestCheckResourceAttr("datadog_dashboard.ordered_dashboard", "widget.13.service_level_objective_definition.0.time_windows.0", "7d"),
resource.TestCheckResourceAttr("datadog_dashboard.ordered_dashboard", "widget.13.service_level_objective_definition.0.time_windows.1", "previous_week"),
// Template Variables
resource.TestCheckResourceAttr("datadog_dashboard.ordered_dashboard", "template_variable.#", "2"),
resource.TestCheckResourceAttr("datadog_dashboard.ordered_dashboard", "template_variable.0.name", "var_1"),
Expand Down
20 changes: 20 additions & 0 deletions website/docs/r/dashboard.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,17 @@ resource "datadog_dashboard" "ordered_dashboard" {
}
}
widget {
service_level_objective_definition {
title = "Widget Title"
view_type = "detail"
slo_id = "56789"
show_error_budget = true
view_mode = "overall"
time_windows = ["7d", "previous_week"]
}
}
template_variable {
name = "var_1"
prefix = "host"
Expand Down Expand Up @@ -719,6 +730,15 @@ Nested `widget` blocks have the following structure:
- `title_size`: (Optional) The size of the widget's title. Default is 16.
- `title_align`: (Optional) The alignment of the widget's title. One of "left", "center", or "right".
- `time`: (Optional) Nested block describing the timeframe to use when displaying the widget. The structure of this block is described [below](dashboard.html#nested-widget-time-blocks).
- `service_level_objective_definition`: The definition for a Service Level Objective widget. Exactly one nested block is allowed with the following structure:
- `view_type`: (Required) Type of view to use when displaying the widget. Only "detail" is currently supported.
- `slo_id`: (Required) The ID of the service level objective used by the widget.
- `show_error_budget`: (Optional) Whether to show the error budget or not.
- `view_mode`: (Required) View mode for the widget. One of "overall", "component", or "both".
- `time_windows`: (Required) List of time windows to display in the widget. Each value in the list must be one of "7d", "30d", "90d", "week_to_date", "previous_week", "month_to_date", or "previous_month".
- `title`: (Optional) The title of the widget.
- `title_size`: (Optional) The size of the widget's title. Default is 16.
- `title_align`: (Optional) The alignment of the widget's title. One of "left", "center", or "right".
- `timeseries_definition`: The definition for a Timeseries widget. Exactly one nested block is allowed with the following structure:
- `request`: (Required) Nested block describing the request to use when displaying the widget. Multiple request blocks are allowed with the following structure (exactly only one of `q`, `apm_query`, `log_query` or `process_query` is required within the request block):
- `q`: (Optional) The metric query to use in the widget
Expand Down

0 comments on commit 52d30ec

Please sign in to comment.