diff --git a/builtin/providers/datadog/provider.go b/builtin/providers/datadog/provider.go index 069d78e886..7221f8481c 100644 --- a/builtin/providers/datadog/provider.go +++ b/builtin/providers/datadog/provider.go @@ -7,7 +7,6 @@ import ( "github.com/hashicorp/terraform/terraform" ) -// Provider returns a terraform.ResourceProvider. func Provider() terraform.ResourceProvider { return &schema.Provider{ Schema: map[string]*schema.Schema{ @@ -24,16 +23,13 @@ func Provider() terraform.ResourceProvider { }, ResourcesMap: map[string]*schema.Resource{ - "datadog_service_check": resourceDatadogServiceCheck(), - "datadog_metric_alert": resourceDatadogMetricAlert(), - "datadog_outlier_alert": resourceDatadogOutlierAlert(), + "datadog_monitor": resourceDatadogMonitor(), }, ConfigureFunc: providerConfigure, } } -// ProviderConfigure returns a configured client. func providerConfigure(d *schema.ResourceData) (interface{}, error) { config := Config{ diff --git a/builtin/providers/datadog/resource_datadog_metric_alert.go b/builtin/providers/datadog/resource_datadog_metric_alert.go deleted file mode 100644 index d92591631d..0000000000 --- a/builtin/providers/datadog/resource_datadog_metric_alert.go +++ /dev/null @@ -1,201 +0,0 @@ -package datadog - -import ( - "bytes" - "fmt" - "log" - - "github.com/hashicorp/terraform/helper/schema" - "github.com/zorkian/go-datadog-api" -) - -// resourceDatadogMetricAlert is a Datadog monitor resource -func resourceDatadogMetricAlert() *schema.Resource { - return &schema.Resource{ - Create: resourceDatadogMetricAlertCreate, - Read: resourceDatadogGenericRead, - Update: resourceDatadogMetricAlertUpdate, - Delete: resourceDatadogGenericDelete, - Exists: resourceDatadogGenericExists, - - Schema: map[string]*schema.Schema{ - "name": &schema.Schema{ - Type: schema.TypeString, - Required: true, - }, - "metric": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - ConflictsWith: []string{"query"}, - }, - "tags": &schema.Schema{ - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - "keys": &schema.Schema{ - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - ConflictsWith: []string{"query"}, - }, - "time_aggr": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - ConflictsWith: []string{"query"}, - }, - "time_window": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - ConflictsWith: []string{"query"}, - }, - "space_aggr": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - ConflictsWith: []string{"query"}, - }, - "operator": &schema.Schema{ - Type: schema.TypeString, - Required: true, - }, - "message": &schema.Schema{ - Type: schema.TypeString, - Required: true, - }, - - // Optional Query for custom monitors - - "query": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - ConflictsWith: []string{"time_aggr", "time_window", "space_aggr", "metric", "keys"}, - }, - - "thresholds": thresholdSchema(), - - // Additional Settings - "notify_no_data": &schema.Schema{ - Type: schema.TypeBool, - Optional: true, - Default: true, - }, - - "no_data_timeframe": &schema.Schema{ - Type: schema.TypeInt, - Optional: true, - }, - - "renotify_interval": &schema.Schema{ - Type: schema.TypeInt, - Optional: true, - Default: 0, - }, - }, - } -} - -// buildMonitorStruct returns a monitor struct -func buildMetricAlertStruct(d *schema.ResourceData) *datadog.Monitor { - name := d.Get("name").(string) - message := d.Get("message").(string) - timeAggr := d.Get("time_aggr").(string) - timeWindow := d.Get("time_window").(string) - spaceAggr := d.Get("space_aggr").(string) - metric := d.Get("metric").(string) - query := d.Get("query").(string) - - // Tags are are no separate resource/gettable, so some trickery is needed - var buffer bytes.Buffer - if raw, ok := d.GetOk("tags"); ok { - list := raw.([]interface{}) - length := (len(list) - 1) - for i, v := range list { - buffer.WriteString(fmt.Sprintf("%s", v)) - if i != length { - buffer.WriteString(",") - } - - } - } - - tagsParsed := buffer.String() - - // Keys are used for multi alerts - var b bytes.Buffer - if raw, ok := d.GetOk("keys"); ok { - list := raw.([]interface{}) - b.WriteString("by {") - length := (len(list) - 1) - for i, v := range list { - b.WriteString(fmt.Sprintf("%s", v)) - if i != length { - b.WriteString(",") - } - - } - b.WriteString("}") - } - - keys := b.String() - - threshold, thresholds := getThresholds(d) - - operator := d.Get("operator").(string) - - var q string - - if query == "" { - q = fmt.Sprintf("%s(%s):%s:%s{%s} %s %s %s", timeAggr, - timeWindow, - spaceAggr, - metric, - tagsParsed, - keys, - operator, - threshold) - } else { - q = fmt.Sprintf("%s %s %s", query, operator, threshold) - } - - log.Print(fmt.Sprintf("[DEBUG] submitting query: %s", q)) - - o := datadog.Options{ - NotifyNoData: d.Get("notify_no_data").(bool), - NoDataTimeframe: d.Get("no_data_timeframe").(int), - RenotifyInterval: d.Get("renotify_interval").(int), - Thresholds: thresholds, - } - - m := datadog.Monitor{ - Type: "metric alert", - Query: q, - Name: name, - Message: message, - Options: o, - } - - return &m -} - -// resourceDatadogMetricAlertCreate creates a monitor. -func resourceDatadogMetricAlertCreate(d *schema.ResourceData, meta interface{}) error { - - m := buildMetricAlertStruct(d) - if err := monitorCreator(d, meta, m); err != nil { - return err - } - - return nil -} - -// resourceDatadogMetricAlertUpdate updates a monitor. -func resourceDatadogMetricAlertUpdate(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] running update.") - - m := buildMetricAlertStruct(d) - if err := monitorUpdater(d, meta, m); err != nil { - return err - } - - return nil -} diff --git a/builtin/providers/datadog/resource_datadog_metric_alert_test.go b/builtin/providers/datadog/resource_datadog_metric_alert_test.go deleted file mode 100644 index 8bf2d21d83..0000000000 --- a/builtin/providers/datadog/resource_datadog_metric_alert_test.go +++ /dev/null @@ -1,170 +0,0 @@ -package datadog - -import ( - "testing" - - "github.com/hashicorp/terraform/helper/resource" - "github.com/hashicorp/terraform/terraform" - "github.com/zorkian/go-datadog-api" -) - -func TestAccDatadogMetricAlert_Basic(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckDatadogMetricAlertDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: testAccCheckDatadogMetricAlertConfigBasic, - Check: resource.ComposeTestCheckFunc( - testAccCheckDatadogMetricAlertExists("datadog_metric_alert.foo"), - resource.TestCheckResourceAttr( - "datadog_metric_alert.foo", "name", "name for metric_alert foo"), - resource.TestCheckResourceAttr( - "datadog_metric_alert.foo", "message", "{{#is_alert}}Metric alert foo is critical"+ - "{{/is_alert}}\n{{#is_warning}}Metric alert foo is at warning "+ - "level{{/is_warning}}\n{{#is_recovery}}Metric alert foo has "+ - "recovered{{/is_recovery}}\nNotify: @hipchat-channel\n"), - resource.TestCheckResourceAttr( - "datadog_metric_alert.foo", "metric", "aws.ec2.cpu"), - resource.TestCheckResourceAttr( - "datadog_metric_alert.foo", "tags.0", "environment:foo"), - resource.TestCheckResourceAttr( - "datadog_metric_alert.foo", "tags.1", "host:foo"), - resource.TestCheckResourceAttr( - "datadog_metric_alert.foo", "tags.#", "2"), - resource.TestCheckResourceAttr( - "datadog_metric_alert.foo", "keys.0", "host"), - resource.TestCheckResourceAttr( - "datadog_metric_alert.foo", "keys.#", "1"), - resource.TestCheckResourceAttr( - "datadog_metric_alert.foo", "time_aggr", "avg"), - resource.TestCheckResourceAttr( - "datadog_metric_alert.foo", "time_window", "last_1h"), - resource.TestCheckResourceAttr( - "datadog_metric_alert.foo", "space_aggr", "avg"), - resource.TestCheckResourceAttr( - "datadog_metric_alert.foo", "operator", ">"), - resource.TestCheckResourceAttr( - "datadog_metric_alert.foo", "notify_no_data", "false"), - resource.TestCheckResourceAttr( - "datadog_metric_alert.foo", "renotify_interval", "60"), - resource.TestCheckResourceAttr( - "datadog_metric_alert.foo", "thresholds.ok", "0"), - resource.TestCheckResourceAttr( - "datadog_metric_alert.foo", "thresholds.warning", "1"), - resource.TestCheckResourceAttr( - "datadog_metric_alert.foo", "thresholds.critical", "2"), - ), - }, - }, - }) -} - -func TestAccDatadogMetricAlert_Query(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckDatadogMetricAlertDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: testAccCheckDatadogMetricAlertConfigQuery, - Check: resource.ComposeTestCheckFunc( - testAccCheckDatadogMetricAlertExists("datadog_metric_alert.foo"), - resource.TestCheckResourceAttr( - "datadog_metric_alert.foo", "name", "name for metric_alert foo"), - resource.TestCheckResourceAttr( - "datadog_metric_alert.foo", "message", "{{#is_alert}}Metric alert foo is critical"+ - "{{/is_alert}}\n{{#is_warning}}Metric alert foo is at warning "+ - "level{{/is_warning}}\n{{#is_recovery}}Metric alert foo has "+ - "recovered{{/is_recovery}}\nNotify: @hipchat-channel\n"), - resource.TestCheckResourceAttr( - "datadog_metric_alert.foo", "query", "avg(last_1h):avg:aws.ec2.cpu{environment:foo,host:foo} by {host}"), - resource.TestCheckResourceAttr( - "datadog_metric_alert.foo", "operator", ">"), - resource.TestCheckResourceAttr( - "datadog_metric_alert.foo", "notify_no_data", "false"), - resource.TestCheckResourceAttr( - "datadog_metric_alert.foo", "renotify_interval", "60"), - resource.TestCheckResourceAttr( - "datadog_metric_alert.foo", "thresholds.ok", "0"), - resource.TestCheckResourceAttr( - "datadog_metric_alert.foo", "thresholds.warning", "1"), - resource.TestCheckResourceAttr( - "datadog_metric_alert.foo", "thresholds.critical", "2"), - ), - }, - }, - }) -} - -func testAccCheckDatadogMetricAlertDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*datadog.Client) - - if err := destroyHelper(s, client); err != nil { - return err - } - return nil -} - -func testAccCheckDatadogMetricAlertExists(n string) resource.TestCheckFunc { - return func(s *terraform.State) error { - client := testAccProvider.Meta().(*datadog.Client) - if err := existsHelper(s, client); err != nil { - return err - } - return nil - } -} - -const testAccCheckDatadogMetricAlertConfigBasic = ` -resource "datadog_metric_alert" "foo" { - name = "name for metric_alert foo" - message = <, >=, ==, or != - - thresholds { - ok = 0 - warning = 1 - critical = 2 - } - - notify_no_data = false - renotify_interval = 60 -} -` -const testAccCheckDatadogMetricAlertConfigQuery = ` -resource "datadog_metric_alert" "foo" { - name = "name for metric_alert foo" - message = <, >=, ==, or != - query = "avg(last_1h):avg:aws.ec2.cpu{environment:foo,host:foo} by {host}" - - thresholds { - ok = 0 - warning = 1 - critical = 2 - } - - notify_no_data = false - renotify_interval = 60 -} -` diff --git a/builtin/providers/datadog/resource_datadog_monitor.go b/builtin/providers/datadog/resource_datadog_monitor.go new file mode 100644 index 0000000000..af2b89f53e --- /dev/null +++ b/builtin/providers/datadog/resource_datadog_monitor.go @@ -0,0 +1,318 @@ +package datadog + +import ( + "fmt" + "log" + "strconv" + "strings" + + "encoding/json" + "github.com/hashicorp/terraform/helper/schema" + "github.com/zorkian/go-datadog-api" +) + +func resourceDatadogMonitor() *schema.Resource { + return &schema.Resource{ + Create: resourceDatadogMonitorCreate, + Read: resourceDatadogMonitorRead, + Update: resourceDatadogMonitorUpdate, + Delete: resourceDatadogMonitorDelete, + Exists: resourceDatadogMonitorExists, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "message": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "escalation_message": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "query": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "type": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + // Options + "thresholds": &schema.Schema{ + Type: schema.TypeMap, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ok": &schema.Schema{ + Type: schema.TypeFloat, + Optional: true, + }, + "warning": &schema.Schema{ + Type: schema.TypeFloat, + Optional: true, + }, + "critical": &schema.Schema{ + Type: schema.TypeFloat, + Required: true, + }, + }, + }, + }, + "notify_no_data": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + "no_data_timeframe": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + }, + "renotify_interval": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + }, + "notify_audit": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + }, + "timeout_h": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + }, + // TODO should actually be map[string]int + "silenced": &schema.Schema{ + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + Elem: &schema.Schema{ + Type: schema.TypeInt}, + }, + }, + "include_tags": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + }, + }, + } +} + +func buildMonitorStruct(d *schema.ResourceData) *datadog.Monitor { + + var thresholds datadog.ThresholdCount + + if r, ok := d.GetOk("thresholds.ok"); ok { + thresholds.Ok = json.Number(r.(string)) + } + if r, ok := d.GetOk("thresholds.warning"); ok { + thresholds.Warning = json.Number(r.(string)) + } + if r, ok := d.GetOk("thresholds.critical"); ok { + thresholds.Critical = json.Number(r.(string)) + } + + o := datadog.Options{ + Thresholds: thresholds, + } + if attr, ok := d.GetOk("silenced"); ok { + s := make(map[string]int) + // TODO: this is not very defensive, test if we can fail on non int input + for k, v := range attr.(map[string]interface{}) { + s[k], _ = strconv.Atoi(v.(string)) + } + o.Silenced = s + } + if attr, ok := d.GetOk("notify_data"); ok { + o.NotifyNoData = attr.(bool) + } + if attr, ok := d.GetOk("no_data_timeframe"); ok { + o.NoDataTimeframe = attr.(int) + } + if attr, ok := d.GetOk("renotify_interval"); ok { + o.RenotifyInterval = attr.(int) + } + if attr, ok := d.GetOk("notify_audit"); ok { + o.NotifyAudit = attr.(bool) + } + if attr, ok := d.GetOk("timeout_h"); ok { + o.TimeoutH = attr.(int) + } + if attr, ok := d.GetOk("escalation_message"); ok { + o.EscalationMessage = attr.(string) + } + if attr, ok := d.GetOk("escalation_message"); ok { + o.EscalationMessage = attr.(string) + } + if attr, ok := d.GetOk("include_tags"); ok { + o.IncludeTags = attr.(bool) + } + + m := datadog.Monitor{ + Type: d.Get("type").(string), + Query: d.Get("query").(string), + Name: d.Get("name").(string), + Message: d.Get("message").(string), + Options: o, + } + + return &m +} + +func resourceDatadogMonitorExists(d *schema.ResourceData, meta interface{}) (b bool, e error) { + // Exists - This is called to verify a resource still exists. It is called prior to Read, + // and lowers the burden of Read to be able to assume the resource exists. + client := meta.(*datadog.Client) + + i, err := strconv.Atoi(d.Id()) + if err != nil { + return false, err + } + + if _, err = client.GetMonitor(i); err != nil { + if strings.Contains(err.Error(), "404 Not Found") { + return false, nil + } + return false, err + } + + return true, nil +} + +func resourceDatadogMonitorCreate(d *schema.ResourceData, meta interface{}) error { + + client := meta.(*datadog.Client) + + m := buildMonitorStruct(d) + m, err := client.CreateMonitor(m) + if err != nil { + return fmt.Errorf("error updating montor: %s", err.Error()) + } + + d.SetId(strconv.Itoa(m.Id)) + + return nil +} + +func resourceDatadogMonitorRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*datadog.Client) + + i, err := strconv.Atoi(d.Id()) + if err != nil { + return err + } + + m, err := client.GetMonitor(i) + if err != nil { + return err + } + + log.Printf("[DEBUG] monitor: %v", m) + d.Set("name", m.Name) + d.Set("message", m.Message) + d.Set("query", m.Query) + d.Set("type", m.Type) + d.Set("thresholds", m.Options.Thresholds) + d.Set("notify_no_data", m.Options.NotifyNoData) + d.Set("notify_no_data_timeframe", m.Options.NoDataTimeframe) + d.Set("renotify_interval", m.Options.RenotifyInterval) + d.Set("notify_audit", m.Options.NotifyAudit) + d.Set("timeout_h", m.Options.TimeoutH) + d.Set("escalation_message", m.Options.EscalationMessage) + d.Set("silenced", m.Options.Silenced) + d.Set("include_tags", m.Options.IncludeTags) + + return nil +} + +func resourceDatadogMonitorUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*datadog.Client) + + m := &datadog.Monitor{} + + i, err := strconv.Atoi(d.Id()) + if err != nil { + return err + } + + m.Id = i + if attr, ok := d.GetOk("name"); ok { + m.Name = attr.(string) + } + if attr, ok := d.GetOk("message"); ok { + m.Message = attr.(string) + } + if attr, ok := d.GetOk("query"); ok { + m.Query = attr.(string) + } + + o := datadog.Options{} + if attr, ok := d.GetOk("thresholds"); ok { + thresholds := attr.(map[string]interface{}) + if thresholds["ok"] != nil { + o.Thresholds.Ok = json.Number(thresholds["ok"].(string)) + } + if thresholds["warning"] != nil { + o.Thresholds.Warning = json.Number(thresholds["warning"].(string)) + } + if thresholds["critical"] != nil { + o.Thresholds.Critical = json.Number(thresholds["critical"].(string)) + } + } + + if attr, ok := d.GetOk("notify_no_data"); ok { + o.NotifyNoData = attr.(bool) + } + if attr, ok := d.GetOk("notify_no_data_timeframe"); ok { + o.NoDataTimeframe = attr.(int) + } + if attr, ok := d.GetOk("renotify_interval"); ok { + o.RenotifyInterval = attr.(int) + } + if attr, ok := d.GetOk("notify_audit"); ok { + o.NotifyAudit = attr.(bool) + } + if attr, ok := d.GetOk("timeout_h"); ok { + o.TimeoutH = attr.(int) + } + if attr, ok := d.GetOk("escalation_message"); ok { + o.EscalationMessage = attr.(string) + } + if attr, ok := d.GetOk("silenced"); ok { + // TODO: this is not very defensive, test if we can fail non int input + s := make(map[string]int) + for k, v := range attr.(map[string]interface{}) { + s[k], _ = strconv.Atoi(v.(string)) + } + o.Silenced = s + } + if attr, ok := d.GetOk("include_tags"); ok { + o.IncludeTags = attr.(bool) + } + + m.Options = o + + if err = client.UpdateMonitor(m); err != nil { + return fmt.Errorf("error updating montor: %s", err.Error()) + } + + return resourceDatadogMonitorRead(d, meta) +} + +func resourceDatadogMonitorDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*datadog.Client) + + i, err := strconv.Atoi(d.Id()) + if err != nil { + return err + } + + if err = client.DeleteMonitor(i); err != nil { + return err + } + + return nil +} diff --git a/builtin/providers/datadog/resource_datadog_monitor_test.go b/builtin/providers/datadog/resource_datadog_monitor_test.go new file mode 100644 index 0000000000..f91019d410 --- /dev/null +++ b/builtin/providers/datadog/resource_datadog_monitor_test.go @@ -0,0 +1,216 @@ +package datadog + +import ( + "fmt" + "strconv" + "strings" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/zorkian/go-datadog-api" +) + +func TestAccDatadogMonitor_Basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDatadogMonitorDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckDatadogMonitorConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckDatadogMonitorExists("datadog_monitor.foo"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "name", "name for monitor foo"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "message", "some message Notify: @hipchat-channel"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "type", "metric alert"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "query", "avg(last_1h):avg:aws.ec2.cpu{environment:foo,host:foo} by {host} > 2"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "notify_no_data", "false"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "renotify_interval", "60"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "thresholds.ok", "0"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "thresholds.warning", "1"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "thresholds.critical", "2"), + ), + }, + }, + }) +} + +func TestAccDatadogMonitor_Updated(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDatadogMonitorDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckDatadogMonitorConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckDatadogMonitorExists("datadog_monitor.foo"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "name", "name for monitor foo"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "message", "some message Notify: @hipchat-channel"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "escalation_message", "the situation has escalated @pagerduty"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "query", "avg(last_1h):avg:aws.ec2.cpu{environment:foo,host:foo} by {host} > 2"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "type", "metric alert"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "notify_no_data", "false"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "renotify_interval", "60"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "thresholds.ok", "0"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "thresholds.warning", "1"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "thresholds.critical", "2"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "notify_audit", "false"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "timeout_h", "60"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "include_tags", "true"), + ), + }, + resource.TestStep{ + Config: testAccCheckDatadogMonitorConfigUpdated, + Check: resource.ComposeTestCheckFunc( + testAccCheckDatadogMonitorExists("datadog_monitor.foo"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "name", "name for monitor bar"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "message", "a different message Notify: @hipchat-channel"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "query", "avg(last_1h):avg:aws.ec2.cpu{environment:bar,host:bar} by {host} > 3"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "escalation_message", "the situation has escalated! @pagerduty"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "type", "metric alert"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "notify_no_data", "true"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "renotify_interval", "40"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "thresholds.ok", "0"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "thresholds.warning", "1"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "thresholds.critical", "3"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "notify_audit", "true"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "timeout_h", "70"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "include_tags", "false"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "silenced.*", "0"), + ), + }, + }, + }) +} + +func testAccCheckDatadogMonitorDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*datadog.Client) + + if err := destroyHelper(s, client); err != nil { + return err + } + return nil +} + +func testAccCheckDatadogMonitorExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + client := testAccProvider.Meta().(*datadog.Client) + if err := existsHelper(s, client); err != nil { + return err + } + return nil + } +} + +const testAccCheckDatadogMonitorConfig = ` +resource "datadog_monitor" "foo" { + name = "name for monitor foo" + type = "metric alert" + message = "some message Notify: @hipchat-channel" + escalation_message = "the situation has escalated @pagerduty" + + query = "avg(last_1h):avg:aws.ec2.cpu{environment:foo,host:foo} by {host} > 2" + + thresholds { + ok = 0 + warning = 1 + critical = 2 + } + + notify_no_data = false + renotify_interval = 60 + + notify_audit = false + timeout_h = 60 + include_tags = true +} +` + +const testAccCheckDatadogMonitorConfigUpdated = ` +resource "datadog_monitor" "foo" { + name = "name for monitor bar" + type = "metric alert" + message = "a different message Notify: @hipchat-channel" + escalation_message = "the situation has escalated @pagerduty" + + query = "avg(last_1h):avg:aws.ec2.cpu{environment:bar,host:bar} by {host} > 3" + + thresholds { + ok = 0 + warning = 1 + critical = 3 + } + + notify_no_data = true + renotify_interval = 40 + escalation_message = "the situation has escalated! @pagerduty" + notify_audit = true + timeout_h = 70 + include_tags = false + silenced { + "*" = 0 + } +} +` + +func destroyHelper(s *terraform.State, client *datadog.Client) error { + for _, r := range s.RootModule().Resources { + i, _ := strconv.Atoi(r.Primary.ID) + if _, err := client.GetMonitor(i); err != nil { + if strings.Contains(err.Error(), "404 Not Found") { + continue + } + return fmt.Errorf("Received an error retrieving monitor %s", err) + } + return fmt.Errorf("Monitor still exists") + } + return nil +} + +func existsHelper(s *terraform.State, client *datadog.Client) error { + for _, r := range s.RootModule().Resources { + i, _ := strconv.Atoi(r.Primary.ID) + if _, err := client.GetMonitor(i); err != nil { + return fmt.Errorf("Received an error retrieving monitor %s", err) + } + } + return nil +} diff --git a/builtin/providers/datadog/resource_datadog_outlier_alert.go b/builtin/providers/datadog/resource_datadog_outlier_alert.go deleted file mode 100644 index e401c6fd65..0000000000 --- a/builtin/providers/datadog/resource_datadog_outlier_alert.go +++ /dev/null @@ -1,184 +0,0 @@ -package datadog - -import ( - "bytes" - "fmt" - "log" - - "github.com/hashicorp/terraform/helper/schema" - "github.com/zorkian/go-datadog-api" -) - -// resourceDatadogOutlierAlert is a Datadog monitor resource -func resourceDatadogOutlierAlert() *schema.Resource { - return &schema.Resource{ - Create: resourceDatadogOutlierAlertCreate, - Read: resourceDatadogGenericRead, - Update: resourceDatadogOutlierAlertUpdate, - Delete: resourceDatadogGenericDelete, - Exists: resourceDatadogGenericExists, - - Schema: map[string]*schema.Schema{ - "name": &schema.Schema{ - Type: schema.TypeString, - Required: true, - }, - "metric": &schema.Schema{ - Type: schema.TypeString, - Required: true, - }, - "tags": &schema.Schema{ - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - "keys": &schema.Schema{ - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - "time_aggr": &schema.Schema{ - Type: schema.TypeString, - Required: true, - }, - "time_window": &schema.Schema{ - Type: schema.TypeString, - Required: true, - }, - "space_aggr": &schema.Schema{ - Type: schema.TypeString, - Required: true, - }, - "message": &schema.Schema{ - Type: schema.TypeString, - Required: true, - }, - "threshold": &schema.Schema{ - Type: schema.TypeString, - Required: true, - }, - // Additional Settings - "notify_no_data": &schema.Schema{ - Type: schema.TypeBool, - Optional: true, - Default: true, - }, - - "no_data_timeframe": &schema.Schema{ - Type: schema.TypeInt, - Optional: true, - }, - - "algorithm": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Default: "dbscan", - }, - - "renotify_interval": &schema.Schema{ - Type: schema.TypeInt, - Optional: true, - Default: 0, - }, - }, - } -} - -// buildMonitorStruct returns a monitor struct -func buildOutlierAlertStruct(d *schema.ResourceData) *datadog.Monitor { - name := d.Get("name").(string) - message := d.Get("message").(string) - timeAggr := d.Get("time_aggr").(string) - timeWindow := d.Get("time_window").(string) - spaceAggr := d.Get("space_aggr").(string) - metric := d.Get("metric").(string) - algorithm := d.Get("algorithm").(string) - - // Tags are are no separate resource/gettable, so some trickery is needed - var buffer bytes.Buffer - if raw, ok := d.GetOk("tags"); ok { - list := raw.([]interface{}) - length := (len(list) - 1) - for i, v := range list { - if length > 1 && v == "*" { - log.Print(fmt.Sprintf("[DEBUG] found wildcard, this is not supported for this type: %s", v)) - continue - } - buffer.WriteString(fmt.Sprintf("%s", v)) - if i != length { - buffer.WriteString(",") - } - - } - } - - tagsParsed := buffer.String() - - // Keys are used for multi alerts - var b bytes.Buffer - if raw, ok := d.GetOk("keys"); ok { - list := raw.([]interface{}) - b.WriteString("by {") - length := (len(list) - 1) - for i, v := range list { - b.WriteString(fmt.Sprintf("%s", v)) - if i != length { - b.WriteString(",") - } - - } - b.WriteString("}") - } - - keys := b.String() - - query := fmt.Sprintf("%s(%s):outliers(%s:%s{%s} %s, '%s',%s) > 0", timeAggr, - timeWindow, - spaceAggr, - metric, - tagsParsed, - keys, - algorithm, - d.Get("threshold")) - - log.Print(fmt.Sprintf("[DEBUG] submitting query: %s", query)) - - o := datadog.Options{ - NotifyNoData: d.Get("notify_no_data").(bool), - NoDataTimeframe: d.Get("no_data_timeframe").(int), - RenotifyInterval: d.Get("renotify_interval").(int), - } - - m := datadog.Monitor{ - Type: "query alert", - Query: query, - Name: name, - Message: message, - Options: o, - } - - return &m -} - -// resourceDatadogOutlierAlertCreate creates a monitor. -func resourceDatadogOutlierAlertCreate(d *schema.ResourceData, meta interface{}) error { - - m := buildOutlierAlertStruct(d) - if err := monitorCreator(d, meta, m); err != nil { - return err - } - - return nil -} - -// resourceDatadogOutlierAlertUpdate updates a monitor. -func resourceDatadogOutlierAlertUpdate(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] running update.") - - m := buildOutlierAlertStruct(d) - if err := monitorUpdater(d, meta, m); err != nil { - return err - } - - return nil -} diff --git a/builtin/providers/datadog/resource_datadog_outlier_alert_test.go b/builtin/providers/datadog/resource_datadog_outlier_alert_test.go deleted file mode 100644 index cbe2e26cc8..0000000000 --- a/builtin/providers/datadog/resource_datadog_outlier_alert_test.go +++ /dev/null @@ -1,97 +0,0 @@ -package datadog - -import ( - "testing" - - "github.com/hashicorp/terraform/helper/resource" - "github.com/hashicorp/terraform/terraform" - "github.com/zorkian/go-datadog-api" -) - -func TestAccDatadogOutlierAlert_Basic(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckDatadogOutlierAlertDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: testAccCheckDatadogOutlierAlertConfigBasic, - Check: resource.ComposeTestCheckFunc( - testAccCheckDatadogOutlierAlertExists("datadog_outlier_alert.foo"), - resource.TestCheckResourceAttr( - "datadog_outlier_alert.foo", "name", "name for outlier_alert foo"), - resource.TestCheckResourceAttr( - "datadog_outlier_alert.foo", "message", "description for outlier_alert foo @hipchat-name"), - resource.TestCheckResourceAttr( - "datadog_outlier_alert.foo", "metric", "system.load.5"), - resource.TestCheckResourceAttr( - "datadog_outlier_alert.foo", "tags.0", "environment:foo"), - resource.TestCheckResourceAttr( - "datadog_outlier_alert.foo", "tags.1", "host:foo"), - resource.TestCheckResourceAttr( - "datadog_outlier_alert.foo", "tags.#", "2"), - resource.TestCheckResourceAttr( - "datadog_outlier_alert.foo", "keys.0", "host"), - resource.TestCheckResourceAttr( - "datadog_outlier_alert.foo", "keys.#", "1"), - resource.TestCheckResourceAttr( - "datadog_outlier_alert.foo", "time_aggr", "avg"), - resource.TestCheckResourceAttr( - "datadog_outlier_alert.foo", "time_window", "last_1h"), - resource.TestCheckResourceAttr( - "datadog_outlier_alert.foo", "space_aggr", "avg"), - resource.TestCheckResourceAttr( - "datadog_outlier_alert.foo", "notify_no_data", "false"), - resource.TestCheckResourceAttr( - "datadog_outlier_alert.foo", "algorithm", "mad"), - resource.TestCheckResourceAttr( - "datadog_outlier_alert.foo", "renotify_interval", "60"), - resource.TestCheckResourceAttr( - "datadog_outlier_alert.foo", "threshold", "2"), - ), - }, - }, - }) -} - -func testAccCheckDatadogOutlierAlertDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*datadog.Client) - - if err := destroyHelper(s, client); err != nil { - return err - } - return nil -} - -func testAccCheckDatadogOutlierAlertExists(n string) resource.TestCheckFunc { - return func(s *terraform.State) error { - client := testAccProvider.Meta().(*datadog.Client) - if err := existsHelper(s, client); err != nil { - return err - } - return nil - } -} - -const testAccCheckDatadogOutlierAlertConfigBasic = ` -resource "datadog_outlier_alert" "foo" { - name = "name for outlier_alert foo" - message = "description for outlier_alert foo @hipchat-name" - - algorithm = "mad" - - metric = "system.load.5" - tags = ["environment:foo", "host:foo"] - keys = ["host"] - - time_aggr = "avg" // avg, sum, max, min, change, or pct_change - time_window = "last_1h" // last_#m (5, 10, 15, 30), last_#h (1, 2, 4), or last_1d - space_aggr = "avg" // avg, sum, min, or max - - threshold = 2.0 - - notify_no_data = false - renotify_interval = 60 - -} -` diff --git a/builtin/providers/datadog/resource_datadog_service_check.go b/builtin/providers/datadog/resource_datadog_service_check.go deleted file mode 100644 index dfbbcbf6b2..0000000000 --- a/builtin/providers/datadog/resource_datadog_service_check.go +++ /dev/null @@ -1,163 +0,0 @@ -package datadog - -import ( - "bytes" - "fmt" - "log" - - "github.com/hashicorp/terraform/helper/schema" - "github.com/zorkian/go-datadog-api" -) - -// resourceDatadogServiceCheck is a Datadog monitor resource -func resourceDatadogServiceCheck() *schema.Resource { - return &schema.Resource{ - Create: resourceDatadogServiceCheckCreate, - Read: resourceDatadogGenericRead, - Update: resourceDatadogServiceCheckUpdate, - Delete: resourceDatadogGenericDelete, - Exists: resourceDatadogGenericExists, - - Schema: map[string]*schema.Schema{ - "name": &schema.Schema{ - Type: schema.TypeString, - Required: true, - }, - "check": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - - "thresholds": thresholdSchema(), - - "tags": &schema.Schema{ - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - "keys": &schema.Schema{ - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - "message": &schema.Schema{ - Type: schema.TypeString, - Required: true, - }, - - // Additional Settings - "notify_no_data": &schema.Schema{ - Type: schema.TypeBool, - Optional: true, - Default: true, - }, - - "no_data_timeframe": &schema.Schema{ - Type: schema.TypeInt, - Optional: true, - }, - "renotify_interval": &schema.Schema{ - Type: schema.TypeInt, - Optional: true, - Default: 0, - }, - }, - } -} - -// buildServiceCheckStruct returns a monitor struct -func buildServiceCheckStruct(d *schema.ResourceData) *datadog.Monitor { - log.Print("[DEBUG] building monitor struct") - name := d.Get("name").(string) - message := d.Get("message").(string) - - // Tags are are no separate resource/gettable, so some trickery is needed - var buffer bytes.Buffer - if raw, ok := d.GetOk("tags"); ok { - list := raw.([]interface{}) - length := (len(list) - 1) - for i, v := range list { - buffer.WriteString(fmt.Sprintf("\"%s\"", v)) - if i != length { - buffer.WriteString(",") - } - - } - } - - tagsParsed := buffer.String() - - // Keys are used for multi alerts - var b bytes.Buffer - if raw, ok := d.GetOk("keys"); ok { - list := raw.([]interface{}) - b.WriteString(".by(") - length := (len(list) - 1) - for i, v := range list { - b.WriteString(fmt.Sprintf("\"%s\"", v)) - if i != length { - b.WriteString(",") - } - - } - b.WriteString(")") - } - - keys := b.String() - - var monitorName string - var query string - - check := d.Get("check").(string) - - // Examples queries - // "http.can_connect".over("instance:buildeng_http","production").last(2).count_by_status() - // "http.can_connect".over("*").by("host","instance","url").last(2).count_by_status() - - checkCount, thresholds := getThresholds(d) - - query = fmt.Sprintf("\"%s\".over(%s)%s.last(%s).count_by_status()", check, tagsParsed, keys, checkCount) - log.Print(fmt.Sprintf("[DEBUG] submitting query: %s", query)) - monitorName = name - - o := datadog.Options{ - NotifyNoData: d.Get("notify_no_data").(bool), - NoDataTimeframe: d.Get("no_data_timeframe").(int), - RenotifyInterval: d.Get("renotify_interval").(int), - Thresholds: thresholds, - } - - m := datadog.Monitor{ - Type: "service check", - Query: query, - Name: monitorName, - Message: message, - Options: o, - } - - return &m -} - -// resourceDatadogServiceCheckCreate creates a monitor. -func resourceDatadogServiceCheckCreate(d *schema.ResourceData, meta interface{}) error { - log.Print("[DEBUG] creating monitor") - - m := buildServiceCheckStruct(d) - if err := monitorCreator(d, meta, m); err != nil { - return err - } - - return nil -} - -// resourceDatadogServiceCheckUpdate updates a monitor. -func resourceDatadogServiceCheckUpdate(d *schema.ResourceData, meta interface{}) error { - log.Printf("[DEBUG] running update.") - - m := buildServiceCheckStruct(d) - if err := monitorUpdater(d, meta, m); err != nil { - return err - } - - return nil -} diff --git a/builtin/providers/datadog/resource_datadog_service_check_test.go b/builtin/providers/datadog/resource_datadog_service_check_test.go deleted file mode 100644 index bfa51e7576..0000000000 --- a/builtin/providers/datadog/resource_datadog_service_check_test.go +++ /dev/null @@ -1,96 +0,0 @@ -package datadog - -import ( - "testing" - - "github.com/hashicorp/terraform/helper/resource" - "github.com/hashicorp/terraform/terraform" - "github.com/zorkian/go-datadog-api" -) - -func TestAccDatadogServiceCheck_Basic(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckDatadogServiceCheckDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: testAccCheckDatadogServiceCheckConfigBasic, - Check: resource.ComposeTestCheckFunc( - testAccCheckDatadogServiceCheckExists("datadog_service_check.bar"), - resource.TestCheckResourceAttr( - "datadog_service_check.bar", "name", "name for service check bar"), - resource.TestCheckResourceAttr( - "datadog_service_check.bar", "message", "{{#is_alert}}Service check bar is critical"+ - "{{/is_alert}}\n{{#is_warning}}Service check bar is at warning "+ - "level{{/is_warning}}\n{{#is_recovery}}Service check bar has "+ - "recovered{{/is_recovery}}\nNotify: @hipchat-channel\n"), - resource.TestCheckResourceAttr( - "datadog_service_check.bar", "check", "datadog.agent.up"), - resource.TestCheckResourceAttr( - "datadog_service_check.bar", "notify_no_data", "false"), - resource.TestCheckResourceAttr( - "datadog_service_check.bar", "tags.0", "environment:foo"), - resource.TestCheckResourceAttr( - "datadog_service_check.bar", "tags.1", "host:bar"), - resource.TestCheckResourceAttr( - "datadog_service_check.bar", "tags.#", "2"), - resource.TestCheckResourceAttr( - "datadog_service_check.bar", "keys.0", "foo"), - resource.TestCheckResourceAttr( - "datadog_service_check.bar", "keys.1", "bar"), - resource.TestCheckResourceAttr( - "datadog_service_check.bar", "keys.#", "2"), - resource.TestCheckResourceAttr( - "datadog_service_check.bar", "thresholds.ok", "0"), - resource.TestCheckResourceAttr( - "datadog_service_check.bar", "thresholds.warning", "1"), - resource.TestCheckResourceAttr( - "datadog_service_check.bar", "thresholds.critical", "2"), - ), - }, - }, - }) -} - -func testAccCheckDatadogServiceCheckDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*datadog.Client) - - if err := destroyHelper(s, client); err != nil { - return err - } - return nil -} - -func testAccCheckDatadogServiceCheckExists(n string) resource.TestCheckFunc { - return func(s *terraform.State) error { - client := testAccProvider.Meta().(*datadog.Client) - if err := existsHelper(s, client); err != nil { - return err - } - return nil - } -} - -const testAccCheckDatadogServiceCheckConfigBasic = ` -resource "datadog_service_check" "bar" { - name = "name for service check bar" - message = <