Skip to content

Commit

Permalink
[monitor] Add support for formula and function variables.
Browse files Browse the repository at this point in the history
  • Loading branch information
phillip-dd committed Feb 11, 2022
1 parent 93a6c9a commit 20a04ef
Show file tree
Hide file tree
Showing 2 changed files with 355 additions and 0 deletions.
285 changes: 285 additions & 0 deletions datadog/resource_datadog_monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)

// Minimal interface between ResourceData and ResourceDiff so that we can use them interchangeably in buildMonitorStruct
Expand Down Expand Up @@ -305,6 +306,134 @@ func resourceDatadogMonitor() *schema.Resource {
return true
},
},
"variables": getMonitorFormulaQuerySchema(),
},
}
}

// Monitor specific schema for formula and functions. Should be a strict
// subset of getFormulaQuerySchema with the appropriate types.
func getMonitorFormulaQuerySchema() *schema.Schema {
return &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"event_query": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Description: "A timeseries formula and functions events query.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"data_source": {
Type: schema.TypeString,
Required: true,
ValidateDiagFunc: validators.ValidateEnumValue(datadogV1.NewMonitorFormulaAndFunctionEventsDataSourceFromValue),
Description: "The data source for event platform-based queries.",
},
"search": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Description: "The search options.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"query": {
Type: schema.TypeString,
ValidateFunc: validation.StringIsNotEmpty,
Required: true,
Description: "The events search string.",
},
},
},
},
"indexes": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
Description: "An array of index names to query in the stream.",
},
"compute": {
Type: schema.TypeList,
Required: true,
Description: "The compute options.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"aggregation": {
Type: schema.TypeString,
Required: true,
ValidateDiagFunc: validators.ValidateEnumValue(datadogV1.NewMonitorFormulaAndFunctionEventAggregationFromValue),
Description: "The aggregation methods for event platform queries.",
},
"interval": {
Type: schema.TypeInt,
Optional: true,
Description: "A time interval in milliseconds.",
},
"metric": {
Type: schema.TypeString,
Optional: true,
Description: "The measurable attribute to compute.",
},
},
},
},
"group_by": {
Type: schema.TypeList,
Optional: true,
Description: "Group by options.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"facet": {
Type: schema.TypeString,
Required: true,
Description: "The event facet.",
},
"limit": {
Type: schema.TypeInt,
Optional: true,
Description: "The number of groups to return.",
},
"sort": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Description: "The options for sorting group by results.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"aggregation": {
Type: schema.TypeString,
Required: true,
ValidateDiagFunc: validators.ValidateEnumValue(datadogV1.NewMonitorFormulaAndFunctionEventAggregationFromValue),
Description: "The aggregation methods for the event platform queries.",
},
"metric": {
Type: schema.TypeString,
Optional: true,
Description: "The metric used for sorting group by results.",
},
"order": {
Type: schema.TypeString,
Optional: true,
ValidateDiagFunc: validators.ValidateEnumValue(datadogV1.NewQuerySortOrderFromValue),
Description: "Direction of sort.",
},
},
},
},
},
},
},
"name": {
Type: schema.TypeString,
Required: true,
Description: "The name of query for use in formulas.",
},
},
},
},
},
},
}
}
Expand Down Expand Up @@ -400,6 +529,16 @@ func buildMonitorStruct(d builtResource) (*datadogV1.Monitor, *datadogV1.Monitor
if attr, ok := d.GetOk("locked"); ok {
o.SetLocked(attr.(bool))
}
if v, ok := d.GetOk("variables"); ok {
variables := v.([]interface{})
if len(variables) > 0 {
monitorVariables := make([]datadogV1.MonitorFormulaAndFunctionQueryDefinition, len(variables))
for i, variable := range variables {
monitorVariables[i] = buildMonitorFormulaAndFunctionEventQuery(variable.(map[string]interface{}))
}
o.SetVariables(monitorVariables)
}
}

monitorType := datadogV1.MonitorType(d.Get("type").(string))
if monitorType == datadogV1.MONITORTYPE_LOG_ALERT {
Expand Down Expand Up @@ -454,6 +593,72 @@ func buildMonitorStruct(d builtResource) (*datadogV1.Monitor, *datadogV1.Monitor
return m, u
}

func buildMonitorFormulaAndFunctionEventQuery(data map[string]interface{}) datadogV1.MonitorFormulaAndFunctionQueryDefinition {
dataSource := datadogV1.MonitorFormulaAndFunctionEventsDataSource(data["data_source"].(string))
computeList := data["compute"].([]interface{})
computeMap := computeList[0].(map[string]interface{})
aggregation := datadogV1.MonitorFormulaAndFunctionEventAggregation(computeMap["aggregation"].(string))
compute := datadogV1.NewMonitorFormulaAndFunctionEventQueryDefinitionCompute(aggregation)
if interval, ok := computeMap["interval"].(int); ok && interval != 0 {
compute.SetInterval(int64(interval))
}
if metric, ok := computeMap["metric"].(string); ok && len(metric) > 0 {
compute.SetMetric(metric)
}
eventQuery := datadogV1.NewMonitorFormulaAndFunctionEventQueryDefinition(*compute, dataSource, data["name"].(string))
eventQueryIndexes := data["indexes"].([]interface{})
indexes := make([]string, len(eventQueryIndexes))
for i, index := range eventQueryIndexes {
indexes[i] = index.(string)
}
eventQuery.SetIndexes(indexes)

if terraformSearches, ok := data["search"].([]interface{}); ok && len(terraformSearches) > 0 {
terraformSearch := terraformSearches[0].(map[string]interface{})
eventQuery.Search = datadogV1.NewMonitorFormulaAndFunctionEventQueryDefinitionSearch(terraformSearch["query"].(string))
}

// GroupBy
if terraformGroupBys, ok := data["group_by"].([]interface{}); ok && len(terraformGroupBys) > 0 {
datadogGroupBys := make([]datadogV1.MonitorFormulaAndFunctionEventQueryGroupBy, len(terraformGroupBys))
for i, g := range terraformGroupBys {
groupBy := g.(map[string]interface{})

// Facet
datadogGroupBy := datadogV1.NewMonitorFormulaAndFunctionEventQueryGroupBy(groupBy["facet"].(string))

// Limit
if v, ok := groupBy["limit"].(int); ok && v != 0 {
datadogGroupBy.SetLimit(int64(v))
}

// Sort
if v, ok := groupBy["sort"].([]interface{}); ok && len(v) > 0 {
if v, ok := v[0].(map[string]interface{}); ok && len(v) > 0 {
sortMap := &datadogV1.MonitorFormulaAndFunctionEventQueryGroupBySort{}
if aggr, ok := v["aggregation"].(string); ok && len(aggr) > 0 {
aggregation := datadogV1.MonitorFormulaAndFunctionEventAggregation(v["aggregation"].(string))
sortMap.SetAggregation(aggregation)
}
if order, ok := v["order"].(string); ok && len(order) > 0 {
eventSort := datadogV1.QuerySortOrder(order)
sortMap.SetOrder(eventSort)
}
if metric, ok := v["metric"].(string); ok && len(metric) > 0 {
sortMap.SetMetric(metric)
}
datadogGroupBy.SetSort(*sortMap)
}
}

datadogGroupBys[i] = *datadogGroupBy
}
eventQuery.SetGroupBy(datadogGroupBys)
}

return datadogV1.MonitorFormulaAndFunctionEventQueryDefinitionAsMonitorFormulaAndFunctionQueryDefinition(eventQuery)
}

// Use CustomizeDiff to do monitor validation
func resourceDatadogMonitorCustomizeDiff(ctx context.Context, diff *schema.ResourceDiff, meta interface{}) error {
if _, ok := diff.GetOk("query"); !ok {
Expand Down Expand Up @@ -618,6 +823,11 @@ func updateMonitorState(d *schema.ResourceData, meta interface{}, m *datadogV1.M
if err := d.Set("locked", m.Options.GetLocked()); err != nil {
return diag.FromErr(err)
}

variables := buildTerraformMonitorVariables(m.Options.GetVariables())
if err := d.Set("variables", variables); err != nil {
return diag.FromErr(err)
}
// This helper function is defined in `resource_datadog_dashboard`
restrictedRoles := buildTerraformRestrictedRoles(&m.RestrictedRoles)
if err := d.Set("restricted_roles", restrictedRoles); err != nil {
Expand All @@ -636,6 +846,81 @@ func updateMonitorState(d *schema.ResourceData, meta interface{}, m *datadogV1.M
return nil
}

func buildTerraformMonitorVariables(datadogVariables []datadogV1.MonitorFormulaAndFunctionQueryDefinition) []map[string]interface{} {
queries := make([]map[string]interface{}, len(datadogVariables))
for i, query := range datadogVariables {
terraformQuery := map[string]interface{}{}
terraformEventQueryDefinition := query.MonitorFormulaAndFunctionEventQueryDefinition
if terraformEventQueryDefinition != nil {
if dataSource, ok := terraformEventQueryDefinition.GetDataSourceOk(); ok {
terraformQuery["data_source"] = dataSource
}
if name, ok := terraformEventQueryDefinition.GetNameOk(); ok {
terraformQuery["name"] = name
}
if indexes, ok := terraformEventQueryDefinition.GetIndexesOk(); ok {
terraformQuery["indexes"] = indexes
}
if search, ok := terraformEventQueryDefinition.GetSearchOk(); ok {
if len(search.GetQuery()) > 0 {
terraformSearch := map[string]interface{}{}
terraformSearch["query"] = search.GetQuery()
terraformSearchList := []map[string]interface{}{terraformSearch}
terraformQuery["search"] = terraformSearchList
}
}
if compute, ok := terraformEventQueryDefinition.GetComputeOk(); ok {
terraformCompute := map[string]interface{}{}
if aggregation, ok := compute.GetAggregationOk(); ok {
terraformCompute["aggregation"] = aggregation
}
if interval, ok := compute.GetIntervalOk(); ok {
terraformCompute["interval"] = interval
}
if metric, ok := compute.GetMetricOk(); ok {
terraformCompute["metric"] = metric
}
terraformComputeList := []map[string]interface{}{terraformCompute}
terraformQuery["compute"] = terraformComputeList
}
if terraformEventQuery, ok := terraformEventQueryDefinition.GetGroupByOk(); ok {
terraformGroupBys := make([]map[string]interface{}, len(*terraformEventQuery))
for i, groupBy := range *terraformEventQuery {
// Facet
terraformGroupBy := map[string]interface{}{
"facet": groupBy.GetFacet(),
}
// Limit
if v, ok := groupBy.GetLimitOk(); ok {
terraformGroupBy["limit"] = *v
}
// Sort
if v, ok := groupBy.GetSortOk(); ok {
terraformSort := map[string]interface{}{}
if metric, ok := v.GetMetricOk(); ok {
terraformSort["metric"] = metric
}
if order, ok := v.GetOrderOk(); ok {
terraformSort["order"] = order
}
if aggregation, ok := v.GetAggregationOk(); ok {
terraformSort["aggregation"] = aggregation
}
terraformGroupBy["sort"] = []map[string]interface{}{terraformSort}
}
terraformGroupBys[i] = terraformGroupBy
}
terraformQuery["group_by"] = &terraformGroupBys
}
terraformQueries := []map[string]interface{}{terraformQuery}
terraformEventQuery := map[string]interface{}{}
terraformEventQuery["event_query"] = terraformQueries
queries[i] = terraformEventQuery
}
}
return queries
}

func resourceDatadogMonitorRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
providerConf := meta.(*ProviderConfiguration)
datadogClientV1 := providerConf.DatadogClientV1
Expand Down
Loading

0 comments on commit 20a04ef

Please sign in to comment.