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