github.com/koding/terraform@v0.6.4-0.20170608090606-5d7e0339779d/builtin/providers/librato/resource_librato_metric.go (about) 1 package librato 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "log" 7 "time" 8 9 "github.com/hashicorp/terraform/helper/resource" 10 "github.com/hashicorp/terraform/helper/schema" 11 "github.com/henrikhodne/go-librato/librato" 12 ) 13 14 func resourceLibratoMetric() *schema.Resource { 15 return &schema.Resource{ 16 Create: resourceLibratoMetricCreate, 17 Read: resourceLibratoMetricRead, 18 Update: resourceLibratoMetricUpdate, 19 Delete: resourceLibratoMetricDelete, 20 21 Schema: map[string]*schema.Schema{ 22 "name": { 23 Type: schema.TypeString, 24 Required: true, 25 ForceNew: false, 26 }, 27 "type": { 28 Type: schema.TypeString, 29 Required: true, 30 }, 31 "display_name": { 32 Type: schema.TypeString, 33 Optional: true, 34 }, 35 "description": { 36 Type: schema.TypeString, 37 Optional: true, 38 }, 39 "period": { 40 Type: schema.TypeInt, 41 Optional: true, 42 }, 43 "composite": { 44 Type: schema.TypeString, 45 Optional: true, 46 }, 47 "attributes": { 48 Type: schema.TypeList, 49 Optional: true, 50 MaxItems: 1, 51 Elem: &schema.Resource{ 52 Schema: map[string]*schema.Schema{ 53 "color": { 54 Type: schema.TypeString, 55 Optional: true, 56 }, 57 "display_max": { 58 Type: schema.TypeString, 59 Optional: true, 60 }, 61 "display_min": { 62 Type: schema.TypeString, 63 Optional: true, 64 }, 65 "display_units_long": { 66 Type: schema.TypeString, 67 Optional: true, 68 }, 69 "display_units_short": { 70 Type: schema.TypeString, 71 Optional: true, 72 }, 73 "display_stacked": { 74 Type: schema.TypeBool, 75 Optional: true, 76 Default: false, 77 }, 78 "created_by_ua": { 79 Type: schema.TypeString, 80 Computed: true, 81 }, 82 "gap_detection": { 83 Type: schema.TypeBool, 84 Optional: true, 85 }, 86 "aggregate": { 87 Type: schema.TypeBool, 88 Optional: true, 89 }, 90 }, 91 }, 92 }, 93 }, 94 } 95 } 96 97 func resourceLibratoMetricCreate(d *schema.ResourceData, meta interface{}) error { 98 client := meta.(*librato.Client) 99 100 metric := librato.Metric{ 101 Name: librato.String(d.Get("name").(string)), 102 Type: librato.String(d.Get("type").(string)), 103 } 104 if a, ok := d.GetOk("display_name"); ok { 105 metric.DisplayName = librato.String(a.(string)) 106 } 107 if a, ok := d.GetOk("description"); ok { 108 metric.Description = librato.String(a.(string)) 109 } 110 if a, ok := d.GetOk("period"); ok { 111 metric.Period = librato.Uint(uint(a.(int))) 112 } 113 if a, ok := d.GetOk("composite"); ok { 114 metric.Composite = librato.String(a.(string)) 115 } 116 117 if a, ok := d.GetOk("attributes"); ok { 118 119 attributeData := a.([]interface{}) 120 attributeDataMap := attributeData[0].(map[string]interface{}) 121 attributes := new(librato.MetricAttributes) 122 123 if v, ok := attributeDataMap["color"].(string); ok && v != "" { 124 attributes.Color = librato.String(v) 125 } 126 if v, ok := attributeDataMap["display_max"].(string); ok && v != "" { 127 attributes.DisplayMax = librato.String(v) 128 } 129 if v, ok := attributeDataMap["display_min"].(string); ok && v != "" { 130 attributes.DisplayMin = librato.String(v) 131 } 132 if v, ok := attributeDataMap["display_units_long"].(string); ok && v != "" { 133 attributes.DisplayUnitsLong = *librato.String(v) 134 } 135 if v, ok := attributeDataMap["display_units_short"].(string); ok && v != "" { 136 attributes.DisplayUnitsShort = *librato.String(v) 137 } 138 if v, ok := attributeDataMap["created_by_ua"].(string); ok && v != "" { 139 attributes.CreatedByUA = *librato.String(v) 140 } 141 if v, ok := attributeDataMap["display_stacked"].(bool); ok { 142 attributes.DisplayStacked = *librato.Bool(v) 143 } 144 if v, ok := attributeDataMap["gap_detection"].(bool); ok { 145 attributes.GapDetection = *librato.Bool(v) 146 } 147 if v, ok := attributeDataMap["aggregate"].(bool); ok { 148 attributes.Aggregate = *librato.Bool(v) 149 } 150 151 metric.Attributes = attributes 152 } 153 154 _, err := client.Metrics.Update(&metric) 155 if err != nil { 156 log.Printf("[INFO] ERROR creating Metric: %s", err) 157 return fmt.Errorf("Error creating Librato metric: %s", err) 158 } 159 160 retryErr := resource.Retry(1*time.Minute, func() *resource.RetryError { 161 _, _, err := client.Metrics.Get(*metric.Name) 162 if err != nil { 163 if errResp, ok := err.(*librato.ErrorResponse); ok && errResp.Response.StatusCode == 404 { 164 return resource.RetryableError(err) 165 } 166 return resource.NonRetryableError(err) 167 } 168 return nil 169 }) 170 if retryErr != nil { 171 return fmt.Errorf("Error creating Librato metric: %s", retryErr) 172 } 173 174 d.SetId(*metric.Name) 175 return resourceLibratoMetricRead(d, meta) 176 } 177 178 func resourceLibratoMetricRead(d *schema.ResourceData, meta interface{}) error { 179 client := meta.(*librato.Client) 180 181 id := d.Id() 182 183 log.Printf("[INFO] Reading Librato Metric: %s", id) 184 metric, _, err := client.Metrics.Get(id) 185 if err != nil { 186 if errResp, ok := err.(*librato.ErrorResponse); ok && errResp.Response.StatusCode == 404 { 187 d.SetId("") 188 return nil 189 } 190 return fmt.Errorf("Error reading Librato Metric %s: %s", id, err) 191 } 192 193 d.Set("name", metric.Name) 194 d.Set("type", metric.Type) 195 196 if metric.Description != nil { 197 d.Set("description", metric.Description) 198 } 199 200 if metric.DisplayName != nil { 201 d.Set("display_name", metric.DisplayName) 202 } 203 204 if metric.Period != nil { 205 d.Set("period", metric.Period) 206 } 207 208 if metric.Composite != nil { 209 d.Set("composite", metric.Composite) 210 } 211 212 attributes := metricAttributesGather(d, metric.Attributes) 213 214 // Since attributes isn't a simple terraform type (TypeList), it's best to 215 // catch the error returned from the d.Set() function, and handle accordingly. 216 if err := d.Set("attributes", attributes); err != nil { 217 return err 218 } 219 220 return nil 221 } 222 223 func resourceLibratoMetricUpdate(d *schema.ResourceData, meta interface{}) error { 224 client := meta.(*librato.Client) 225 226 id := d.Id() 227 228 metric := new(librato.Metric) 229 metric.Name = librato.String(id) 230 231 if d.HasChange("type") { 232 metric.Type = librato.String(d.Get("type").(string)) 233 } 234 if d.HasChange("description") { 235 metric.Description = librato.String(d.Get("description").(string)) 236 } 237 if d.HasChange("display_name") { 238 metric.DisplayName = librato.String(d.Get("display_name").(string)) 239 } 240 if d.HasChange("period") { 241 metric.Period = librato.Uint(uint(d.Get("period").(int))) 242 } 243 if d.HasChange("composite") { 244 metric.Composite = librato.String(d.Get("composite").(string)) 245 } 246 if d.HasChange("attributes") { 247 attributeData := d.Get("attributes").([]interface{}) 248 attributeDataMap := attributeData[0].(map[string]interface{}) 249 attributes := new(librato.MetricAttributes) 250 251 if v, ok := attributeDataMap["color"].(string); ok && v != "" { 252 attributes.Color = librato.String(v) 253 } 254 if v, ok := attributeDataMap["display_max"].(string); ok && v != "" { 255 attributes.DisplayMax = librato.String(v) 256 } 257 if v, ok := attributeDataMap["display_min"].(string); ok && v != "" { 258 attributes.DisplayMin = librato.String(v) 259 } 260 if v, ok := attributeDataMap["display_units_long"].(string); ok && v != "" { 261 attributes.DisplayUnitsLong = *librato.String(v) 262 } 263 if v, ok := attributeDataMap["display_units_short"].(string); ok && v != "" { 264 attributes.DisplayUnitsShort = *librato.String(v) 265 } 266 if v, ok := attributeDataMap["created_by_ua"].(string); ok && v != "" { 267 attributes.CreatedByUA = *librato.String(v) 268 } 269 if v, ok := attributeDataMap["display_stacked"].(bool); ok { 270 attributes.DisplayStacked = *librato.Bool(v) 271 } 272 if v, ok := attributeDataMap["gap_detection"].(bool); ok { 273 attributes.GapDetection = *librato.Bool(v) 274 } 275 if v, ok := attributeDataMap["aggregate"].(bool); ok { 276 attributes.Aggregate = *librato.Bool(v) 277 } 278 metric.Attributes = attributes 279 } 280 281 log.Printf("[INFO] Updating Librato metric: %v", structToString(metric)) 282 283 _, err := client.Metrics.Update(metric) 284 if err != nil { 285 return fmt.Errorf("Error updating Librato metric: %s", err) 286 } 287 288 log.Printf("[INFO] Updated Librato metric %s", id) 289 290 // Wait for propagation since Librato updates are eventually consistent 291 wait := resource.StateChangeConf{ 292 Pending: []string{fmt.Sprintf("%t", false)}, 293 Target: []string{fmt.Sprintf("%t", true)}, 294 Timeout: 5 * time.Minute, 295 MinTimeout: 2 * time.Second, 296 ContinuousTargetOccurence: 5, 297 Refresh: func() (interface{}, string, error) { 298 log.Printf("[INFO] Checking if Librato Metric %s was updated yet", id) 299 changedMetric, _, getErr := client.Metrics.Get(id) 300 if getErr != nil { 301 return changedMetric, "", getErr 302 } 303 return changedMetric, "true", nil 304 }, 305 } 306 307 _, err = wait.WaitForState() 308 if err != nil { 309 log.Printf("[INFO] ERROR - Failed updating Librato Metric %s: %s", id, err) 310 return fmt.Errorf("Failed updating Librato Metric %s: %s", id, err) 311 } 312 313 return resourceLibratoMetricRead(d, meta) 314 } 315 316 func resourceLibratoMetricDelete(d *schema.ResourceData, meta interface{}) error { 317 client := meta.(*librato.Client) 318 319 id := d.Id() 320 321 log.Printf("[INFO] Deleting Metric: %s", id) 322 _, err := client.Metrics.Delete(id) 323 if err != nil { 324 return fmt.Errorf("Error deleting Metric: %s", err) 325 } 326 327 log.Printf("[INFO] Verifying Metric %s deleted", id) 328 retryErr := resource.Retry(1*time.Minute, func() *resource.RetryError { 329 330 log.Printf("[INFO] Getting Metric %s", id) 331 _, _, err := client.Metrics.Get(id) 332 if err != nil { 333 if errResp, ok := err.(*librato.ErrorResponse); ok && errResp.Response.StatusCode == 404 { 334 log.Printf("[INFO] Metric %s not found, removing from state", id) 335 return nil 336 } 337 log.Printf("[INFO] non-retryable error attempting to Get metric: %s", err) 338 return resource.NonRetryableError(err) 339 } 340 341 log.Printf("[INFO] retryable error attempting to Get metric: %s", id) 342 return resource.RetryableError(fmt.Errorf("metric still exists")) 343 }) 344 if retryErr != nil { 345 return fmt.Errorf("Error deleting librato metric: %s", retryErr) 346 } 347 348 return nil 349 } 350 351 // Flattens an attributes hash into something that flatmap.Flatten() can handle 352 func metricAttributesGather(d *schema.ResourceData, attributes *librato.MetricAttributes) []map[string]interface{} { 353 result := make([]map[string]interface{}, 0, 1) 354 355 if attributes != nil { 356 retAttributes := make(map[string]interface{}) 357 if attributes.Color != nil { 358 retAttributes["color"] = *attributes.Color 359 } 360 if attributes.DisplayMax != nil { 361 retAttributes["display_max"] = attributes.DisplayMax 362 } 363 if attributes.DisplayMin != nil { 364 retAttributes["display_min"] = attributes.DisplayMin 365 } 366 if attributes.DisplayUnitsLong != "" { 367 retAttributes["display_units_long"] = attributes.DisplayUnitsLong 368 } 369 if attributes.DisplayUnitsShort != "" { 370 retAttributes["display_units_short"] = attributes.DisplayUnitsShort 371 } 372 if attributes.CreatedByUA != "" { 373 retAttributes["created_by_ua"] = attributes.CreatedByUA 374 } 375 retAttributes["display_stacked"] = attributes.DisplayStacked || false 376 retAttributes["gap_detection"] = attributes.GapDetection || false 377 retAttributes["aggregate"] = attributes.Aggregate || false 378 379 result = append(result, retAttributes) 380 } 381 382 return result 383 } 384 385 func structToString(i interface{}) string { 386 s, _ := json.Marshal(i) 387 return string(s) 388 }