github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/datadog/resource_datadog_monitor.go (about) 1 package datadog 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "log" 7 "strconv" 8 "strings" 9 10 "github.com/hashicorp/terraform/helper/schema" 11 "gopkg.in/zorkian/go-datadog-api.v2" 12 ) 13 14 func resourceDatadogMonitor() *schema.Resource { 15 return &schema.Resource{ 16 Create: resourceDatadogMonitorCreate, 17 Read: resourceDatadogMonitorRead, 18 Update: resourceDatadogMonitorUpdate, 19 Delete: resourceDatadogMonitorDelete, 20 Exists: resourceDatadogMonitorExists, 21 Importer: &schema.ResourceImporter{ 22 State: resourceDatadogMonitorImport, 23 }, 24 25 Schema: map[string]*schema.Schema{ 26 "name": { 27 Type: schema.TypeString, 28 Required: true, 29 }, 30 "message": { 31 Type: schema.TypeString, 32 Required: true, 33 StateFunc: func(val interface{}) string { 34 return strings.TrimSpace(val.(string)) 35 }, 36 }, 37 "escalation_message": { 38 Type: schema.TypeString, 39 Optional: true, 40 StateFunc: func(val interface{}) string { 41 return strings.TrimSpace(val.(string)) 42 }, 43 }, 44 "query": { 45 Type: schema.TypeString, 46 Required: true, 47 StateFunc: func(val interface{}) string { 48 return strings.TrimSpace(val.(string)) 49 }, 50 }, 51 "type": { 52 Type: schema.TypeString, 53 Required: true, 54 }, 55 56 // Options 57 "thresholds": { 58 Type: schema.TypeMap, 59 Optional: true, 60 Elem: &schema.Resource{ 61 Schema: map[string]*schema.Schema{ 62 "ok": { 63 Type: schema.TypeFloat, 64 Optional: true, 65 }, 66 "warning": { 67 Type: schema.TypeFloat, 68 Optional: true, 69 }, 70 "critical": { 71 Type: schema.TypeFloat, 72 Optional: true, 73 }, 74 }, 75 }, 76 DiffSuppressFunc: suppressDataDogFloatIntDiff, 77 }, 78 "notify_no_data": { 79 Type: schema.TypeBool, 80 Optional: true, 81 Default: false, 82 }, 83 "new_host_delay": { 84 Type: schema.TypeInt, 85 Computed: true, 86 Optional: true, 87 }, 88 "no_data_timeframe": { 89 Type: schema.TypeInt, 90 Optional: true, 91 }, 92 "renotify_interval": { 93 Type: schema.TypeInt, 94 Optional: true, 95 }, 96 "notify_audit": { 97 Type: schema.TypeBool, 98 Optional: true, 99 }, 100 "timeout_h": { 101 Type: schema.TypeInt, 102 Optional: true, 103 }, 104 "require_full_window": { 105 Type: schema.TypeBool, 106 Optional: true, 107 Default: true, 108 }, 109 "locked": { 110 Type: schema.TypeBool, 111 Optional: true, 112 }, 113 "silenced": { 114 Type: schema.TypeMap, 115 Optional: true, 116 Elem: schema.TypeInt, 117 }, 118 "include_tags": { 119 Type: schema.TypeBool, 120 Optional: true, 121 Default: true, 122 }, 123 "tags": { 124 Type: schema.TypeList, 125 Optional: true, 126 Elem: &schema.Schema{Type: schema.TypeString}, 127 }, 128 }, 129 } 130 } 131 132 func buildMonitorStruct(d *schema.ResourceData) *datadog.Monitor { 133 134 var thresholds datadog.ThresholdCount 135 136 if r, ok := d.GetOk("thresholds.ok"); ok { 137 thresholds.SetOk(json.Number(r.(string))) 138 } 139 if r, ok := d.GetOk("thresholds.warning"); ok { 140 thresholds.SetWarning(json.Number(r.(string))) 141 } 142 if r, ok := d.GetOk("thresholds.critical"); ok { 143 thresholds.SetCritical(json.Number(r.(string))) 144 } 145 146 o := datadog.Options{ 147 Thresholds: &thresholds, 148 NotifyNoData: datadog.Bool(d.Get("notify_no_data").(bool)), 149 RequireFullWindow: datadog.Bool(d.Get("require_full_window").(bool)), 150 IncludeTags: datadog.Bool(d.Get("include_tags").(bool)), 151 } 152 if attr, ok := d.GetOk("silenced"); ok { 153 s := make(map[string]int) 154 // TODO: this is not very defensive, test if we can fail on non int input 155 for k, v := range attr.(map[string]interface{}) { 156 s[k] = v.(int) 157 } 158 o.Silenced = s 159 } 160 if attr, ok := d.GetOk("notify_no_data"); ok { 161 o.SetNotifyNoData(attr.(bool)) 162 } 163 if attr, ok := d.GetOk("new_host_delay"); ok { 164 o.SetNewHostDelay(attr.(int)) 165 } 166 if attr, ok := d.GetOk("no_data_timeframe"); ok { 167 o.NoDataTimeframe = datadog.NoDataTimeframe(attr.(int)) 168 } 169 if attr, ok := d.GetOk("renotify_interval"); ok { 170 o.SetRenotifyInterval(attr.(int)) 171 } 172 if attr, ok := d.GetOk("notify_audit"); ok { 173 o.SetNotifyAudit(attr.(bool)) 174 } 175 if attr, ok := d.GetOk("timeout_h"); ok { 176 o.SetTimeoutH(attr.(int)) 177 } 178 if attr, ok := d.GetOk("escalation_message"); ok { 179 o.SetEscalationMessage(attr.(string)) 180 } 181 if attr, ok := d.GetOk("locked"); ok { 182 o.SetLocked(attr.(bool)) 183 } 184 185 m := datadog.Monitor{ 186 Type: datadog.String(d.Get("type").(string)), 187 Query: datadog.String(d.Get("query").(string)), 188 Name: datadog.String(d.Get("name").(string)), 189 Message: datadog.String(d.Get("message").(string)), 190 Options: &o, 191 } 192 193 if attr, ok := d.GetOk("tags"); ok { 194 tags := []string{} 195 for _, s := range attr.([]interface{}) { 196 tags = append(tags, s.(string)) 197 } 198 m.Tags = tags 199 } 200 201 return &m 202 } 203 204 func resourceDatadogMonitorExists(d *schema.ResourceData, meta interface{}) (b bool, e error) { 205 // Exists - This is called to verify a resource still exists. It is called prior to Read, 206 // and lowers the burden of Read to be able to assume the resource exists. 207 client := meta.(*datadog.Client) 208 209 i, err := strconv.Atoi(d.Id()) 210 if err != nil { 211 return false, err 212 } 213 214 if _, err = client.GetMonitor(i); err != nil { 215 if strings.Contains(err.Error(), "404 Not Found") { 216 return false, nil 217 } 218 return false, err 219 } 220 221 return true, nil 222 } 223 224 func resourceDatadogMonitorCreate(d *schema.ResourceData, meta interface{}) error { 225 226 client := meta.(*datadog.Client) 227 228 m := buildMonitorStruct(d) 229 m, err := client.CreateMonitor(m) 230 if err != nil { 231 return fmt.Errorf("error updating monitor: %s", err.Error()) 232 } 233 234 d.SetId(strconv.Itoa(m.GetId())) 235 236 return nil 237 } 238 239 func resourceDatadogMonitorRead(d *schema.ResourceData, meta interface{}) error { 240 client := meta.(*datadog.Client) 241 242 i, err := strconv.Atoi(d.Id()) 243 if err != nil { 244 return err 245 } 246 247 m, err := client.GetMonitor(i) 248 if err != nil { 249 return err 250 } 251 252 thresholds := make(map[string]string) 253 for k, v := range map[string]json.Number{ 254 "ok": m.Options.Thresholds.GetOk(), 255 "warning": m.Options.Thresholds.GetWarning(), 256 "critical": m.Options.Thresholds.GetCritical(), 257 } { 258 s := v.String() 259 if s != "" { 260 thresholds[k] = s 261 } 262 } 263 264 tags := []string{} 265 for _, s := range m.Tags { 266 tags = append(tags, s) 267 } 268 269 log.Printf("[DEBUG] monitor: %v", m) 270 d.Set("name", m.GetName()) 271 d.Set("message", m.GetMessage()) 272 d.Set("query", m.GetQuery()) 273 d.Set("type", m.GetType()) 274 d.Set("thresholds", thresholds) 275 276 d.Set("new_host_delay", m.Options.GetNewHostDelay()) 277 d.Set("notify_no_data", m.Options.GetNotifyNoData()) 278 d.Set("no_data_timeframe", m.Options.NoDataTimeframe) 279 d.Set("renotify_interval", m.Options.GetRenotifyInterval()) 280 d.Set("notify_audit", m.Options.GetNotifyAudit()) 281 d.Set("timeout_h", m.Options.GetTimeoutH()) 282 d.Set("escalation_message", m.Options.GetEscalationMessage()) 283 d.Set("silenced", m.Options.Silenced) 284 d.Set("include_tags", m.Options.GetIncludeTags()) 285 d.Set("tags", tags) 286 d.Set("require_full_window", m.Options.GetRequireFullWindow()) // TODO Is this one of those options that we neeed to check? 287 d.Set("locked", m.Options.GetLocked()) 288 289 return nil 290 } 291 292 func resourceDatadogMonitorUpdate(d *schema.ResourceData, meta interface{}) error { 293 client := meta.(*datadog.Client) 294 295 m := &datadog.Monitor{} 296 297 i, err := strconv.Atoi(d.Id()) 298 if err != nil { 299 return err 300 } 301 302 m.Id = datadog.Int(i) 303 if attr, ok := d.GetOk("name"); ok { 304 m.SetName(attr.(string)) 305 } 306 if attr, ok := d.GetOk("message"); ok { 307 m.SetMessage(attr.(string)) 308 } 309 if attr, ok := d.GetOk("query"); ok { 310 m.SetQuery(attr.(string)) 311 } 312 313 if attr, ok := d.GetOk("tags"); ok { 314 s := make([]string, 0) 315 for _, v := range attr.([]interface{}) { 316 s = append(s, v.(string)) 317 } 318 m.Tags = s 319 } 320 321 o := datadog.Options{ 322 NotifyNoData: datadog.Bool(d.Get("notify_no_data").(bool)), 323 RequireFullWindow: datadog.Bool(d.Get("require_full_window").(bool)), 324 IncludeTags: datadog.Bool(d.Get("include_tags").(bool)), 325 } 326 if attr, ok := d.GetOk("thresholds"); ok { 327 thresholds := attr.(map[string]interface{}) 328 o.Thresholds = &datadog.ThresholdCount{} // TODO: This is a little annoying.. 329 if thresholds["ok"] != nil { 330 o.Thresholds.SetOk(json.Number(thresholds["ok"].(string))) 331 } 332 if thresholds["warning"] != nil { 333 o.Thresholds.SetWarning(json.Number(thresholds["warning"].(string))) 334 } 335 if thresholds["critical"] != nil { 336 o.Thresholds.SetCritical(json.Number(thresholds["critical"].(string))) 337 } 338 } 339 340 if attr, ok := d.GetOk("new_host_delay"); ok { 341 o.SetNewHostDelay(attr.(int)) 342 } 343 if attr, ok := d.GetOk("no_data_timeframe"); ok { 344 o.NoDataTimeframe = datadog.NoDataTimeframe(attr.(int)) 345 } 346 if attr, ok := d.GetOk("renotify_interval"); ok { 347 o.SetRenotifyInterval(attr.(int)) 348 } 349 if attr, ok := d.GetOk("notify_audit"); ok { 350 o.SetNotifyAudit(attr.(bool)) 351 } 352 if attr, ok := d.GetOk("timeout_h"); ok { 353 o.SetTimeoutH(attr.(int)) 354 } 355 if attr, ok := d.GetOk("escalation_message"); ok { 356 o.SetEscalationMessage(attr.(string)) 357 } 358 if attr, ok := d.GetOk("silenced"); ok { 359 // TODO: this is not very defensive, test if we can fail non int input 360 s := make(map[string]int) 361 for k, v := range attr.(map[string]interface{}) { 362 s[k] = v.(int) 363 } 364 o.Silenced = s 365 } 366 if attr, ok := d.GetOk("locked"); ok { 367 o.SetLocked(attr.(bool)) 368 } 369 370 m.Options = &o 371 372 if err = client.UpdateMonitor(m); err != nil { 373 return fmt.Errorf("error updating monitor: %s", err.Error()) 374 } 375 376 return resourceDatadogMonitorRead(d, meta) 377 } 378 379 func resourceDatadogMonitorDelete(d *schema.ResourceData, meta interface{}) error { 380 client := meta.(*datadog.Client) 381 382 i, err := strconv.Atoi(d.Id()) 383 if err != nil { 384 return err 385 } 386 387 if err = client.DeleteMonitor(i); err != nil { 388 return err 389 } 390 391 return nil 392 } 393 394 func resourceDatadogMonitorImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { 395 if err := resourceDatadogMonitorRead(d, meta); err != nil { 396 return nil, err 397 } 398 return []*schema.ResourceData{d}, nil 399 } 400 401 // Ignore any diff that results from the mix of ints or floats returned from the 402 // DataDog API. 403 func suppressDataDogFloatIntDiff(k, old, new string, d *schema.ResourceData) bool { 404 oF, err := strconv.ParseFloat(old, 64) 405 if err != nil { 406 log.Printf("Error parsing float of old value (%s): %s", old, err) 407 return false 408 } 409 410 nF, err := strconv.ParseFloat(new, 64) 411 if err != nil { 412 log.Printf("Error parsing float of new value (%s): %s", new, err) 413 return false 414 } 415 416 // if the float values of these attributes are equivalent, ignore this 417 // diff 418 if oF == nF { 419 return true 420 } 421 return false 422 }