github.com/minamijoyo/terraform@v0.7.8-0.20161029001309-18b3736ba44b/builtin/providers/librato/resource_librato_alert.go (about) 1 package librato 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 "math" 8 "strconv" 9 "time" 10 11 "github.com/hashicorp/terraform/helper/hashcode" 12 "github.com/hashicorp/terraform/helper/resource" 13 "github.com/hashicorp/terraform/helper/schema" 14 "github.com/henrikhodne/go-librato/librato" 15 ) 16 17 func resourceLibratoAlert() *schema.Resource { 18 return &schema.Resource{ 19 Create: resourceLibratoAlertCreate, 20 Read: resourceLibratoAlertRead, 21 Update: resourceLibratoAlertUpdate, 22 Delete: resourceLibratoAlertDelete, 23 24 Schema: map[string]*schema.Schema{ 25 "name": &schema.Schema{ 26 Type: schema.TypeString, 27 Required: true, 28 ForceNew: false, 29 }, 30 "id": &schema.Schema{ 31 Type: schema.TypeInt, 32 Computed: true, 33 }, 34 "description": &schema.Schema{ 35 Type: schema.TypeString, 36 Optional: true, 37 }, 38 "active": &schema.Schema{ 39 Type: schema.TypeBool, 40 Optional: true, 41 Default: true, 42 }, 43 "rearm_seconds": &schema.Schema{ 44 Type: schema.TypeInt, 45 Optional: true, 46 Default: 600, 47 }, 48 "services": &schema.Schema{ 49 Type: schema.TypeSet, 50 Optional: true, 51 Elem: &schema.Schema{Type: schema.TypeString}, 52 Set: schema.HashString, 53 }, 54 "condition": &schema.Schema{ 55 Type: schema.TypeSet, 56 Optional: true, 57 Elem: &schema.Resource{ 58 Schema: map[string]*schema.Schema{ 59 "type": &schema.Schema{ 60 Type: schema.TypeString, 61 Required: true, 62 }, 63 "metric_name": &schema.Schema{ 64 Type: schema.TypeString, 65 Required: true, 66 }, 67 "source": &schema.Schema{ 68 Type: schema.TypeString, 69 Optional: true, 70 }, 71 "detect_reset": &schema.Schema{ 72 Type: schema.TypeBool, 73 Optional: true, 74 }, 75 "duration": &schema.Schema{ 76 Type: schema.TypeInt, 77 Optional: true, 78 }, 79 "threshold": &schema.Schema{ 80 Type: schema.TypeFloat, 81 Optional: true, 82 }, 83 "summary_function": &schema.Schema{ 84 Type: schema.TypeString, 85 Optional: true, 86 }, 87 }, 88 }, 89 Set: resourceLibratoAlertConditionsHash, 90 }, 91 "attributes": &schema.Schema{ 92 Type: schema.TypeList, 93 Optional: true, 94 Elem: &schema.Resource{ 95 Schema: map[string]*schema.Schema{ 96 "runbook_url": &schema.Schema{ 97 Type: schema.TypeString, 98 Optional: true, 99 }, 100 }, 101 }, 102 }, 103 }, 104 } 105 } 106 107 func resourceLibratoAlertConditionsHash(v interface{}) int { 108 var buf bytes.Buffer 109 m := v.(map[string]interface{}) 110 buf.WriteString(fmt.Sprintf("%s-", m["type"].(string))) 111 buf.WriteString(fmt.Sprintf("%s-", m["metric_name"].(string))) 112 113 source, present := m["source"] 114 if present { 115 buf.WriteString(fmt.Sprintf("%s-", source.(string))) 116 } 117 118 detect_reset, present := m["detect_reset"] 119 if present { 120 buf.WriteString(fmt.Sprintf("%t-", detect_reset.(bool))) 121 } 122 123 duration, present := m["duration"] 124 if present { 125 buf.WriteString(fmt.Sprintf("%d-", duration.(int))) 126 } 127 128 threshold, present := m["threshold"] 129 if present { 130 buf.WriteString(fmt.Sprintf("%f-", threshold.(float64))) 131 } 132 133 summary_function, present := m["summary_function"] 134 if present { 135 buf.WriteString(fmt.Sprintf("%s-", summary_function.(string))) 136 } 137 138 return hashcode.String(buf.String()) 139 } 140 141 func resourceLibratoAlertCreate(d *schema.ResourceData, meta interface{}) error { 142 client := meta.(*librato.Client) 143 144 alert := new(librato.Alert) 145 if v, ok := d.GetOk("name"); ok { 146 alert.Name = librato.String(v.(string)) 147 } 148 if v, ok := d.GetOk("description"); ok { 149 alert.Description = librato.String(v.(string)) 150 } 151 // GetOK returns not OK for false boolean values, use Get 152 alert.Active = librato.Bool(d.Get("active").(bool)) 153 if v, ok := d.GetOk("rearm_seconds"); ok { 154 alert.RearmSeconds = librato.Uint(uint(v.(int))) 155 } 156 if v, ok := d.GetOk("services"); ok { 157 vs := v.(*schema.Set) 158 services := make([]*string, vs.Len()) 159 for i, serviceData := range vs.List() { 160 services[i] = librato.String(serviceData.(string)) 161 } 162 alert.Services = services 163 } 164 if v, ok := d.GetOk("condition"); ok { 165 vs := v.(*schema.Set) 166 conditions := make([]librato.AlertCondition, vs.Len()) 167 for i, conditionDataM := range vs.List() { 168 conditionData := conditionDataM.(map[string]interface{}) 169 var condition librato.AlertCondition 170 if v, ok := conditionData["type"].(string); ok && v != "" { 171 condition.Type = librato.String(v) 172 } 173 if v, ok := conditionData["threshold"].(float64); ok && !math.IsNaN(v) { 174 condition.Threshold = librato.Float(v) 175 } 176 if v, ok := conditionData["metric_name"].(string); ok && v != "" { 177 condition.MetricName = librato.String(v) 178 } 179 if v, ok := conditionData["source"].(string); ok && v != "" { 180 condition.Source = librato.String(v) 181 } 182 if v, ok := conditionData["detect_reset"].(bool); ok { 183 condition.DetectReset = librato.Bool(v) 184 } 185 if v, ok := conditionData["duration"].(int); ok { 186 condition.Duration = librato.Uint(uint(v)) 187 } 188 if v, ok := conditionData["summary_function"].(string); ok && v != "" { 189 condition.SummaryFunction = librato.String(v) 190 } 191 conditions[i] = condition 192 } 193 alert.Conditions = conditions 194 } 195 if v, ok := d.GetOk("attributes"); ok { 196 attributeData := v.([]interface{}) 197 if len(attributeData) > 1 { 198 return fmt.Errorf("Only one set of attributes per alert is supported") 199 } else if len(attributeData) == 1 { 200 if attributeData[0] == nil { 201 return fmt.Errorf("No attributes found in attributes block") 202 } 203 attributeDataMap := attributeData[0].(map[string]interface{}) 204 attributes := new(librato.AlertAttributes) 205 if v, ok := attributeDataMap["runbook_url"].(string); ok && v != "" { 206 attributes.RunbookURL = librato.String(v) 207 } 208 alert.Attributes = attributes 209 } 210 } 211 212 alertResult, _, err := client.Alerts.Create(alert) 213 214 if err != nil { 215 return fmt.Errorf("Error creating Librato alert %s: %s", *alert.Name, err) 216 } 217 218 resource.Retry(1*time.Minute, func() *resource.RetryError { 219 _, _, err := client.Alerts.Get(*alertResult.ID) 220 if err != nil { 221 if errResp, ok := err.(*librato.ErrorResponse); ok && errResp.Response.StatusCode == 404 { 222 return resource.RetryableError(err) 223 } 224 return resource.NonRetryableError(err) 225 } 226 return nil 227 }) 228 229 return resourceLibratoAlertReadResult(d, alertResult) 230 } 231 232 func resourceLibratoAlertRead(d *schema.ResourceData, meta interface{}) error { 233 client := meta.(*librato.Client) 234 id, err := strconv.ParseUint(d.Id(), 10, 0) 235 if err != nil { 236 return err 237 } 238 239 alert, _, err := client.Alerts.Get(uint(id)) 240 if err != nil { 241 if errResp, ok := err.(*librato.ErrorResponse); ok && errResp.Response.StatusCode == 404 { 242 d.SetId("") 243 return nil 244 } 245 return fmt.Errorf("Error reading Librato Alert %s: %s", d.Id(), err) 246 } 247 248 return resourceLibratoAlertReadResult(d, alert) 249 } 250 251 func resourceLibratoAlertReadResult(d *schema.ResourceData, alert *librato.Alert) error { 252 d.SetId(strconv.FormatUint(uint64(*alert.ID), 10)) 253 d.Set("id", *alert.ID) 254 d.Set("name", *alert.Name) 255 d.Set("description", *alert.Description) 256 d.Set("active", *alert.Active) 257 d.Set("rearm_seconds", *alert.RearmSeconds) 258 259 services := resourceLibratoAlertServicesGather(d, alert.Services.([]interface{})) 260 d.Set("services", services) 261 262 conditions := resourceLibratoAlertConditionsGather(d, alert.Conditions) 263 d.Set("condition", conditions) 264 265 attributes := resourceLibratoAlertAttributesGather(d, alert.Attributes) 266 d.Set("attributes", attributes) 267 268 return nil 269 } 270 271 func resourceLibratoAlertServicesGather(d *schema.ResourceData, services []interface{}) []string { 272 retServices := make([]string, 0, len(services)) 273 274 for _, s := range services { 275 serviceData := s.(map[string]interface{}) 276 // ID field is returned as float64, for whatever reason 277 retServices = append(retServices, fmt.Sprintf("%.f", serviceData["id"])) 278 } 279 280 return retServices 281 } 282 283 func resourceLibratoAlertConditionsGather(d *schema.ResourceData, conditions []librato.AlertCondition) []map[string]interface{} { 284 retConditions := make([]map[string]interface{}, 0, len(conditions)) 285 for _, c := range conditions { 286 condition := make(map[string]interface{}) 287 if c.Type != nil { 288 condition["type"] = *c.Type 289 } 290 if c.Threshold != nil { 291 condition["threshold"] = *c.Threshold 292 } 293 if c.MetricName != nil { 294 condition["metric_name"] = *c.MetricName 295 } 296 if c.Source != nil { 297 condition["source"] = *c.Source 298 } 299 if c.DetectReset != nil { 300 condition["detect_reset"] = *c.MetricName 301 } 302 if c.Duration != nil { 303 condition["duration"] = *c.Duration 304 } 305 if c.SummaryFunction != nil { 306 condition["summary_function"] = *c.SummaryFunction 307 } 308 retConditions = append(retConditions, condition) 309 } 310 311 return retConditions 312 } 313 314 // Flattens an attributes hash into something that flatmap.Flatten() can handle 315 func resourceLibratoAlertAttributesGather(d *schema.ResourceData, attributes *librato.AlertAttributes) []map[string]interface{} { 316 result := make([]map[string]interface{}, 0, 1) 317 318 if attributes != nil { 319 retAttributes := make(map[string]interface{}) 320 if attributes.RunbookURL != nil { 321 retAttributes["runbook_url"] = *attributes.RunbookURL 322 } 323 result = append(result, retAttributes) 324 } 325 326 return result 327 } 328 329 func resourceLibratoAlertUpdate(d *schema.ResourceData, meta interface{}) error { 330 client := meta.(*librato.Client) 331 332 alertID, err := strconv.ParseUint(d.Id(), 10, 0) 333 if err != nil { 334 return err 335 } 336 337 alert := new(librato.Alert) 338 alert.Name = librato.String(d.Get("name").(string)) 339 if d.HasChange("description") { 340 alert.Description = librato.String(d.Get("description").(string)) 341 } 342 if d.HasChange("active") { 343 alert.Active = librato.Bool(d.Get("active").(bool)) 344 } 345 if d.HasChange("rearm_seconds") { 346 alert.RearmSeconds = librato.Uint(uint(d.Get("rearm_seconds").(int))) 347 } 348 if d.HasChange("services") { 349 vs := d.Get("services").(*schema.Set) 350 services := make([]*string, vs.Len()) 351 for i, serviceData := range vs.List() { 352 services[i] = librato.String(serviceData.(string)) 353 } 354 alert.Services = services 355 } 356 357 vs := d.Get("condition").(*schema.Set) 358 conditions := make([]librato.AlertCondition, vs.Len()) 359 for i, conditionDataM := range vs.List() { 360 conditionData := conditionDataM.(map[string]interface{}) 361 var condition librato.AlertCondition 362 if v, ok := conditionData["type"].(string); ok && v != "" { 363 condition.Type = librato.String(v) 364 } 365 if v, ok := conditionData["threshold"].(float64); ok && !math.IsNaN(v) { 366 condition.Threshold = librato.Float(v) 367 } 368 if v, ok := conditionData["metric_name"].(string); ok && v != "" { 369 condition.MetricName = librato.String(v) 370 } 371 if v, ok := conditionData["source"].(string); ok && v != "" { 372 condition.Source = librato.String(v) 373 } 374 if v, ok := conditionData["detect_reset"].(bool); ok { 375 condition.DetectReset = librato.Bool(v) 376 } 377 if v, ok := conditionData["duration"].(int); ok { 378 condition.Duration = librato.Uint(uint(v)) 379 } 380 if v, ok := conditionData["summary_function"].(string); ok && v != "" { 381 condition.SummaryFunction = librato.String(v) 382 } 383 conditions[i] = condition 384 alert.Conditions = conditions 385 } 386 if d.HasChange("attributes") { 387 attributeData := d.Get("attributes").([]interface{}) 388 if len(attributeData) > 1 { 389 return fmt.Errorf("Only one set of attributes per alert is supported") 390 } else if len(attributeData) == 1 { 391 if attributeData[0] == nil { 392 return fmt.Errorf("No attributes found in attributes block") 393 } 394 attributeDataMap := attributeData[0].(map[string]interface{}) 395 attributes := new(librato.AlertAttributes) 396 if v, ok := attributeDataMap["runbook_url"].(string); ok && v != "" { 397 attributes.RunbookURL = librato.String(v) 398 } 399 alert.Attributes = attributes 400 } 401 } 402 403 _, err = client.Alerts.Edit(uint(alertID), alert) 404 if err != nil { 405 return fmt.Errorf("Error updating Librato alert: %s", err) 406 } 407 408 return resourceLibratoAlertRead(d, meta) 409 } 410 411 func resourceLibratoAlertDelete(d *schema.ResourceData, meta interface{}) error { 412 client := meta.(*librato.Client) 413 id, err := strconv.ParseUint(d.Id(), 10, 0) 414 if err != nil { 415 return err 416 } 417 418 log.Printf("[INFO] Deleting Alert: %d", id) 419 _, err = client.Alerts.Delete(uint(id)) 420 if err != nil { 421 return fmt.Errorf("Error deleting Alert: %s", err) 422 } 423 424 resource.Retry(1*time.Minute, func() *resource.RetryError { 425 _, _, err := client.Alerts.Get(uint(id)) 426 if err != nil { 427 if errResp, ok := err.(*librato.ErrorResponse); ok && errResp.Response.StatusCode == 404 { 428 return nil 429 } 430 return resource.NonRetryableError(err) 431 } 432 return resource.RetryableError(fmt.Errorf("alert still exists")) 433 }) 434 435 d.SetId("") 436 return nil 437 }