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