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