github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/librato/resource_librato_space_chart.go (about) 1 package librato 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 "math" 8 "reflect" 9 "strconv" 10 "time" 11 12 "github.com/hashicorp/terraform/helper/hashcode" 13 "github.com/hashicorp/terraform/helper/resource" 14 "github.com/hashicorp/terraform/helper/schema" 15 "github.com/henrikhodne/go-librato/librato" 16 ) 17 18 func resourceLibratoSpaceChart() *schema.Resource { 19 return &schema.Resource{ 20 Create: resourceLibratoSpaceChartCreate, 21 Read: resourceLibratoSpaceChartRead, 22 Update: resourceLibratoSpaceChartUpdate, 23 Delete: resourceLibratoSpaceChartDelete, 24 25 Schema: map[string]*schema.Schema{ 26 "space_id": &schema.Schema{ 27 Type: schema.TypeInt, 28 Required: true, 29 ForceNew: true, 30 }, 31 "name": &schema.Schema{ 32 Type: schema.TypeString, 33 Required: true, 34 }, 35 "type": &schema.Schema{ 36 Type: schema.TypeString, 37 Required: true, 38 ForceNew: true, 39 }, 40 "min": &schema.Schema{ 41 Type: schema.TypeFloat, 42 Default: math.NaN(), 43 Optional: true, 44 }, 45 "max": &schema.Schema{ 46 Type: schema.TypeFloat, 47 Default: math.NaN(), 48 Optional: true, 49 }, 50 "label": &schema.Schema{ 51 Type: schema.TypeString, 52 Optional: true, 53 }, 54 "related_space": &schema.Schema{ 55 Type: schema.TypeInt, 56 Optional: true, 57 }, 58 "stream": &schema.Schema{ 59 Type: schema.TypeSet, 60 Optional: true, 61 Elem: &schema.Resource{ 62 Schema: map[string]*schema.Schema{ 63 "metric": &schema.Schema{ 64 Type: schema.TypeString, 65 Optional: true, 66 ConflictsWith: []string{"stream.composite"}, 67 }, 68 "source": &schema.Schema{ 69 Type: schema.TypeString, 70 Optional: true, 71 ConflictsWith: []string{"stream.composite"}, 72 }, 73 "group_function": &schema.Schema{ 74 Type: schema.TypeString, 75 Optional: true, 76 ConflictsWith: []string{"stream.composite"}, 77 }, 78 "composite": &schema.Schema{ 79 Type: schema.TypeString, 80 Optional: true, 81 ConflictsWith: []string{"stream.metric", "stream.source", "stream.group_function"}, 82 }, 83 "summary_function": &schema.Schema{ 84 Type: schema.TypeString, 85 Optional: true, 86 }, 87 "name": &schema.Schema{ 88 Type: schema.TypeString, 89 Optional: true, 90 }, 91 "color": &schema.Schema{ 92 Type: schema.TypeString, 93 Optional: true, 94 }, 95 "units_short": &schema.Schema{ 96 Type: schema.TypeString, 97 Optional: true, 98 }, 99 "units_long": &schema.Schema{ 100 Type: schema.TypeString, 101 Optional: true, 102 }, 103 "min": &schema.Schema{ 104 Type: schema.TypeFloat, 105 Default: math.NaN(), 106 Optional: true, 107 }, 108 "max": &schema.Schema{ 109 Type: schema.TypeFloat, 110 Default: math.NaN(), 111 Optional: true, 112 }, 113 "transform_function": &schema.Schema{ 114 Type: schema.TypeString, 115 Optional: true, 116 }, 117 "period": &schema.Schema{ 118 Type: schema.TypeInt, 119 Optional: true, 120 }, 121 }, 122 }, 123 Set: resourceLibratoSpaceChartHash, 124 }, 125 }, 126 } 127 } 128 129 func resourceLibratoSpaceChartHash(v interface{}) int { 130 var buf bytes.Buffer 131 m := v.(map[string]interface{}) 132 buf.WriteString(fmt.Sprintf("%s-", m["metric"].(string))) 133 buf.WriteString(fmt.Sprintf("%s-", m["source"].(string))) 134 buf.WriteString(fmt.Sprintf("%s-", m["composite"].(string))) 135 136 return hashcode.String(buf.String()) 137 } 138 139 func resourceLibratoSpaceChartCreate(d *schema.ResourceData, meta interface{}) error { 140 client := meta.(*librato.Client) 141 142 spaceID := uint(d.Get("space_id").(int)) 143 144 spaceChart := new(librato.SpaceChart) 145 if v, ok := d.GetOk("name"); ok { 146 spaceChart.Name = librato.String(v.(string)) 147 } 148 if v, ok := d.GetOk("type"); ok { 149 spaceChart.Type = librato.String(v.(string)) 150 } 151 if v, ok := d.GetOk("min"); ok { 152 if math.IsNaN(v.(float64)) { 153 spaceChart.Min = nil 154 } else { 155 spaceChart.Min = librato.Float(v.(float64)) 156 } 157 } 158 if v, ok := d.GetOk("max"); ok { 159 if math.IsNaN(v.(float64)) { 160 spaceChart.Max = nil 161 } else { 162 spaceChart.Max = librato.Float(v.(float64)) 163 } 164 } 165 if v, ok := d.GetOk("label"); ok { 166 spaceChart.Label = librato.String(v.(string)) 167 } 168 if v, ok := d.GetOk("related_space"); ok { 169 spaceChart.RelatedSpace = librato.Uint(uint(v.(int))) 170 } 171 if v, ok := d.GetOk("stream"); ok { 172 vs := v.(*schema.Set) 173 streams := make([]librato.SpaceChartStream, vs.Len()) 174 for i, streamDataM := range vs.List() { 175 streamData := streamDataM.(map[string]interface{}) 176 var stream librato.SpaceChartStream 177 if v, ok := streamData["metric"].(string); ok && v != "" { 178 stream.Metric = librato.String(v) 179 } 180 if v, ok := streamData["source"].(string); ok && v != "" { 181 stream.Source = librato.String(v) 182 } 183 if v, ok := streamData["composite"].(string); ok && v != "" { 184 stream.Composite = librato.String(v) 185 } 186 if v, ok := streamData["group_function"].(string); ok && v != "" { 187 stream.GroupFunction = librato.String(v) 188 } 189 if v, ok := streamData["summary_function"].(string); ok && v != "" { 190 stream.SummaryFunction = librato.String(v) 191 } 192 if v, ok := streamData["transform_function"].(string); ok && v != "" { 193 stream.TransformFunction = librato.String(v) 194 } 195 if v, ok := streamData["color"].(string); ok && v != "" { 196 stream.Color = librato.String(v) 197 } 198 if v, ok := streamData["units_short"].(string); ok && v != "" { 199 stream.UnitsShort = librato.String(v) 200 } 201 if v, ok := streamData["units_longs"].(string); ok && v != "" { 202 stream.UnitsLong = librato.String(v) 203 } 204 if v, ok := streamData["min"].(float64); ok && !math.IsNaN(v) { 205 stream.Min = librato.Float(v) 206 } 207 if v, ok := streamData["max"].(float64); ok && !math.IsNaN(v) { 208 stream.Max = librato.Float(v) 209 } 210 streams[i] = stream 211 } 212 spaceChart.Streams = streams 213 } 214 215 spaceChartResult, _, err := client.Spaces.CreateChart(spaceID, spaceChart) 216 if err != nil { 217 return fmt.Errorf("Error creating Librato space chart %s: %s", *spaceChart.Name, err) 218 } 219 220 resource.Retry(1*time.Minute, func() *resource.RetryError { 221 _, _, err := client.Spaces.GetChart(spaceID, *spaceChartResult.ID) 222 if err != nil { 223 if errResp, ok := err.(*librato.ErrorResponse); ok && errResp.Response.StatusCode == 404 { 224 return resource.RetryableError(err) 225 } 226 return resource.NonRetryableError(err) 227 } 228 return nil 229 }) 230 231 return resourceLibratoSpaceChartReadResult(d, spaceChartResult) 232 } 233 234 func resourceLibratoSpaceChartRead(d *schema.ResourceData, meta interface{}) error { 235 client := meta.(*librato.Client) 236 237 spaceID := uint(d.Get("space_id").(int)) 238 239 id, err := strconv.ParseUint(d.Id(), 10, 0) 240 if err != nil { 241 return err 242 } 243 244 chart, _, err := client.Spaces.GetChart(spaceID, uint(id)) 245 if err != nil { 246 if errResp, ok := err.(*librato.ErrorResponse); ok && errResp.Response.StatusCode == 404 { 247 d.SetId("") 248 return nil 249 } 250 return fmt.Errorf("Error reading Librato Space chart %s: %s", d.Id(), err) 251 } 252 253 return resourceLibratoSpaceChartReadResult(d, chart) 254 } 255 256 func resourceLibratoSpaceChartReadResult(d *schema.ResourceData, chart *librato.SpaceChart) error { 257 d.SetId(strconv.FormatUint(uint64(*chart.ID), 10)) 258 if chart.Name != nil { 259 if err := d.Set("name", *chart.Name); err != nil { 260 return err 261 } 262 } 263 if chart.Type != nil { 264 if err := d.Set("type", *chart.Type); err != nil { 265 return err 266 } 267 } 268 if chart.Min != nil { 269 if err := d.Set("min", *chart.Min); err != nil { 270 return err 271 } 272 } 273 if chart.Max != nil { 274 if err := d.Set("max", *chart.Max); err != nil { 275 return err 276 } 277 } 278 if chart.Label != nil { 279 if err := d.Set("label", *chart.Label); err != nil { 280 return err 281 } 282 } 283 if chart.RelatedSpace != nil { 284 if err := d.Set("related_space", *chart.RelatedSpace); err != nil { 285 return err 286 } 287 } 288 289 streams := resourceLibratoSpaceChartStreamsGather(d, chart.Streams) 290 if err := d.Set("stream", streams); err != nil { 291 return err 292 } 293 294 return nil 295 } 296 297 func resourceLibratoSpaceChartStreamsGather(d *schema.ResourceData, streams []librato.SpaceChartStream) []map[string]interface{} { 298 retStreams := make([]map[string]interface{}, 0, len(streams)) 299 for _, s := range streams { 300 stream := make(map[string]interface{}) 301 if s.Metric != nil { 302 stream["metric"] = *s.Metric 303 } 304 if s.Source != nil { 305 stream["source"] = *s.Source 306 } 307 if s.Composite != nil { 308 stream["composite"] = *s.Composite 309 } 310 if s.GroupFunction != nil { 311 stream["group_function"] = *s.GroupFunction 312 } 313 if s.SummaryFunction != nil { 314 stream["summary_function"] = *s.SummaryFunction 315 } 316 if s.TransformFunction != nil { 317 stream["transform_function"] = *s.TransformFunction 318 } 319 if s.Color != nil { 320 stream["color"] = *s.Color 321 } 322 if s.UnitsShort != nil { 323 stream["units_short"] = *s.UnitsShort 324 } 325 if s.UnitsLong != nil { 326 stream["units_long"] = *s.UnitsLong 327 } 328 retStreams = append(retStreams, stream) 329 } 330 331 return retStreams 332 } 333 334 func resourceLibratoSpaceChartUpdate(d *schema.ResourceData, meta interface{}) error { 335 client := meta.(*librato.Client) 336 337 spaceID := uint(d.Get("space_id").(int)) 338 chartID, err := strconv.ParseUint(d.Id(), 10, 0) 339 if err != nil { 340 return err 341 } 342 343 // Just to have whole object for comparison before/after update 344 fullChart, _, err := client.Spaces.GetChart(spaceID, uint(chartID)) 345 if err != nil { 346 return err 347 } 348 349 spaceChart := new(librato.SpaceChart) 350 if d.HasChange("name") { 351 spaceChart.Name = librato.String(d.Get("name").(string)) 352 fullChart.Name = spaceChart.Name 353 } 354 if d.HasChange("min") { 355 if math.IsNaN(d.Get("min").(float64)) { 356 spaceChart.Min = nil 357 } else { 358 spaceChart.Min = librato.Float(d.Get("min").(float64)) 359 } 360 fullChart.Min = spaceChart.Min 361 } 362 if d.HasChange("max") { 363 if math.IsNaN(d.Get("max").(float64)) { 364 spaceChart.Max = nil 365 } else { 366 spaceChart.Max = librato.Float(d.Get("max").(float64)) 367 } 368 fullChart.Max = spaceChart.Max 369 } 370 if d.HasChange("label") { 371 spaceChart.Label = librato.String(d.Get("label").(string)) 372 fullChart.Label = spaceChart.Label 373 } 374 if d.HasChange("related_space") { 375 spaceChart.RelatedSpace = librato.Uint(d.Get("related_space").(uint)) 376 fullChart.RelatedSpace = spaceChart.RelatedSpace 377 } 378 if d.HasChange("stream") { 379 vs := d.Get("stream").(*schema.Set) 380 streams := make([]librato.SpaceChartStream, vs.Len()) 381 for i, streamDataM := range vs.List() { 382 streamData := streamDataM.(map[string]interface{}) 383 var stream librato.SpaceChartStream 384 if v, ok := streamData["metric"].(string); ok && v != "" { 385 stream.Metric = librato.String(v) 386 } 387 if v, ok := streamData["source"].(string); ok && v != "" { 388 stream.Source = librato.String(v) 389 } 390 if v, ok := streamData["composite"].(string); ok && v != "" { 391 stream.Composite = librato.String(v) 392 } 393 if v, ok := streamData["group_function"].(string); ok && v != "" { 394 stream.GroupFunction = librato.String(v) 395 } 396 if v, ok := streamData["summary_function"].(string); ok && v != "" { 397 stream.SummaryFunction = librato.String(v) 398 } 399 if v, ok := streamData["transform_function"].(string); ok && v != "" { 400 stream.TransformFunction = librato.String(v) 401 } 402 if v, ok := streamData["color"].(string); ok && v != "" { 403 stream.Color = librato.String(v) 404 } 405 if v, ok := streamData["units_short"].(string); ok && v != "" { 406 stream.UnitsShort = librato.String(v) 407 } 408 if v, ok := streamData["units_longs"].(string); ok && v != "" { 409 stream.UnitsLong = librato.String(v) 410 } 411 if v, ok := streamData["min"].(float64); ok && !math.IsNaN(v) { 412 stream.Min = librato.Float(v) 413 } 414 if v, ok := streamData["max"].(float64); ok && !math.IsNaN(v) { 415 stream.Max = librato.Float(v) 416 } 417 streams[i] = stream 418 } 419 spaceChart.Streams = streams 420 fullChart.Streams = streams 421 } 422 423 _, err = client.Spaces.EditChart(spaceID, uint(chartID), spaceChart) 424 if err != nil { 425 return fmt.Errorf("Error updating Librato space chart %s: %s", *spaceChart.Name, err) 426 } 427 428 // Wait for propagation since Librato updates are eventually consistent 429 wait := resource.StateChangeConf{ 430 Pending: []string{fmt.Sprintf("%t", false)}, 431 Target: []string{fmt.Sprintf("%t", true)}, 432 Timeout: 5 * time.Minute, 433 MinTimeout: 2 * time.Second, 434 ContinuousTargetOccurence: 5, 435 Refresh: func() (interface{}, string, error) { 436 log.Printf("[DEBUG] Checking if Librato Space Chart %d was updated yet", chartID) 437 changedChart, _, err := client.Spaces.GetChart(spaceID, uint(chartID)) 438 if err != nil { 439 return changedChart, "", err 440 } 441 isEqual := reflect.DeepEqual(*fullChart, *changedChart) 442 log.Printf("[DEBUG] Updated Librato Space Chart %d match: %t", chartID, isEqual) 443 return changedChart, fmt.Sprintf("%t", isEqual), nil 444 }, 445 } 446 447 _, err = wait.WaitForState() 448 if err != nil { 449 return fmt.Errorf("Failed updating Librato Space Chart %d: %s", chartID, err) 450 } 451 452 return resourceLibratoSpaceChartRead(d, meta) 453 } 454 455 func resourceLibratoSpaceChartDelete(d *schema.ResourceData, meta interface{}) error { 456 client := meta.(*librato.Client) 457 458 spaceID := uint(d.Get("space_id").(int)) 459 460 id, err := strconv.ParseUint(d.Id(), 10, 0) 461 if err != nil { 462 return err 463 } 464 465 log.Printf("[INFO] Deleting Chart: %d/%d", spaceID, uint(id)) 466 _, err = client.Spaces.DeleteChart(spaceID, uint(id)) 467 if err != nil { 468 return fmt.Errorf("Error deleting space: %s", err) 469 } 470 471 resource.Retry(1*time.Minute, func() *resource.RetryError { 472 _, _, err := client.Spaces.GetChart(spaceID, uint(id)) 473 if err != nil { 474 if errResp, ok := err.(*librato.ErrorResponse); ok && errResp.Response.StatusCode == 404 { 475 return nil 476 } 477 return resource.NonRetryableError(err) 478 } 479 return resource.RetryableError(fmt.Errorf("space chart still exists")) 480 }) 481 482 d.SetId("") 483 return nil 484 }