provider/datadog: Replace separate monitors with one generic monitor with read functionality.
This commit is contained in:
parent
775a3f8826
commit
eb2407fccf
|
@ -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{
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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 = <<EOF
|
||||
{{#is_alert}}Metric alert foo is critical{{/is_alert}}
|
||||
{{#is_warning}}Metric alert foo is at warning level{{/is_warning}}
|
||||
{{#is_recovery}}Metric alert foo has recovered{{/is_recovery}}
|
||||
Notify: @hipchat-channel
|
||||
EOF
|
||||
|
||||
metric = "aws.ec2.cpu"
|
||||
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
|
||||
operator = ">" // <, <=, >, >=, ==, 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 = <<EOF
|
||||
{{#is_alert}}Metric alert foo is critical{{/is_alert}}
|
||||
{{#is_warning}}Metric alert foo is at warning level{{/is_warning}}
|
||||
{{#is_recovery}}Metric alert foo has recovered{{/is_recovery}}
|
||||
Notify: @hipchat-channel
|
||||
EOF
|
||||
operator = ">" // <, <=, >, >=, ==, 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
|
||||
}
|
||||
`
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
||||
}
|
||||
`
|
|
@ -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
|
||||
}
|
|
@ -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 = <<EOF
|
||||
{{#is_alert}}Service check bar is critical{{/is_alert}}
|
||||
{{#is_warning}}Service check bar is at warning level{{/is_warning}}
|
||||
{{#is_recovery}}Service check bar has recovered{{/is_recovery}}
|
||||
Notify: @hipchat-channel
|
||||
EOF
|
||||
tags = ["environment:foo", "host:bar"]
|
||||
keys = ["foo", "bar"]
|
||||
check = "datadog.agent.up"
|
||||
|
||||
thresholds {
|
||||
ok = 0
|
||||
warning = 1
|
||||
critical = 2
|
||||
}
|
||||
|
||||
notify_no_data = false
|
||||
}
|
||||
`
|
|
@ -1,138 +0,0 @@
|
|||
package datadog
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/zorkian/go-datadog-api"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func thresholdSchema() *schema.Schema {
|
||||
return &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,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getThresholds(d *schema.ResourceData) (string, datadog.ThresholdCount) {
|
||||
t := datadog.ThresholdCount{}
|
||||
|
||||
var threshold string
|
||||
|
||||
if r, ok := d.GetOk("thresholds.ok"); ok {
|
||||
t.Ok = json.Number(r.(string))
|
||||
}
|
||||
|
||||
if r, ok := d.GetOk("thresholds.warning"); ok {
|
||||
t.Warning = json.Number(r.(string))
|
||||
}
|
||||
|
||||
if r, ok := d.GetOk("thresholds.critical"); ok {
|
||||
threshold = r.(string)
|
||||
t.Critical = json.Number(r.(string))
|
||||
}
|
||||
|
||||
return threshold, t
|
||||
}
|
||||
|
||||
func resourceDatadogGenericDelete(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
|
||||
}
|
||||
|
||||
func resourceDatadogGenericExists(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)
|
||||
|
||||
// Workaround to handle upgrades from < 0.0.4
|
||||
if strings.Contains(d.Id(), "__") {
|
||||
return false, fmt.Errorf("Monitor ID contains __, which is pre v0.0.4 old behaviour.\n You have the following options:\n" +
|
||||
" * Run https://github.com/ojongerius/terraform-provider-datadog/blob/master/scripts/migration_helper.py to generate a new statefile and clean up monitors\n" +
|
||||
" * Mannualy fix this by deleting all your metric_check resources and recreate them, " +
|
||||
"or manually remove half of the resources and hack the state file.\n")
|
||||
}
|
||||
|
||||
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 resourceDatadogGenericRead(d *schema.ResourceData, meta interface{}) error {
|
||||
// TODO: Add support for read function.
|
||||
/* Read - This is called to resync the local state with the remote state.
|
||||
Terraform guarantees that an existing ID will be set. This ID should be
|
||||
used to look up the resource. Any remote data should be updated into the
|
||||
local data. No changes to the remote resource are to be made.
|
||||
*/
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func monitorCreator(d *schema.ResourceData, meta interface{}, m *datadog.Monitor) error {
|
||||
client := meta.(*datadog.Client)
|
||||
|
||||
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 monitorUpdater(d *schema.ResourceData, meta interface{}, m *datadog.Monitor) error {
|
||||
client := meta.(*datadog.Client)
|
||||
|
||||
i, err := strconv.Atoi(d.Id())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.Id = i
|
||||
|
||||
if err = client.UpdateMonitor(m); err != nil {
|
||||
return fmt.Errorf("error updating montor: %s", err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package datadog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/zorkian/go-datadog-api"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
Loading…
Reference in New Issue