github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/circonus/resource_circonus_graph.go (about) 1 package circonus 2 3 import ( 4 "fmt" 5 "regexp" 6 "strconv" 7 "strings" 8 9 "github.com/circonus-labs/circonus-gometrics/api" 10 "github.com/circonus-labs/circonus-gometrics/api/config" 11 "github.com/hashicorp/errwrap" 12 "github.com/hashicorp/terraform/helper/schema" 13 ) 14 15 const ( 16 // circonus_graph.* resource attribute names 17 graphDescriptionAttr = "description" 18 graphLeftAttr = "left" 19 graphLineStyleAttr = "line_style" 20 graphMetricClusterAttr = "metric_cluster" 21 graphNameAttr = "name" 22 graphNotesAttr = "notes" 23 graphRightAttr = "right" 24 graphMetricAttr = "metric" 25 graphStyleAttr = "graph_style" 26 graphTagsAttr = "tags" 27 28 // circonus_graph.metric.* resource attribute names 29 graphMetricActiveAttr = "active" 30 graphMetricAlphaAttr = "alpha" 31 graphMetricAxisAttr = "axis" 32 graphMetricCAQLAttr = "caql" 33 graphMetricCheckAttr = "check" 34 graphMetricColorAttr = "color" 35 graphMetricFormulaAttr = "formula" 36 graphMetricFormulaLegendAttr = "legend_formula" 37 graphMetricFunctionAttr = "function" 38 graphMetricHumanNameAttr = "name" 39 graphMetricMetricTypeAttr = "metric_type" 40 graphMetricNameAttr = "metric_name" 41 graphMetricStackAttr = "stack" 42 43 // circonus_graph.metric_cluster.* resource attribute names 44 graphMetricClusterActiveAttr = "active" 45 graphMetricClusterAggregateAttr = "aggregate" 46 graphMetricClusterAxisAttr = "axis" 47 graphMetricClusterColorAttr = "color" 48 graphMetricClusterQueryAttr = "query" 49 graphMetricClusterHumanNameAttr = "name" 50 51 // circonus_graph.{left,right}.* resource attribute names 52 graphAxisLogarithmicAttr = "logarithmic" 53 graphAxisMaxAttr = "max" 54 graphAxisMinAttr = "min" 55 ) 56 57 const ( 58 apiGraphStyleLine = "line" 59 ) 60 61 var graphDescriptions = attrDescrs{ 62 // circonus_graph.* resource attribute names 63 graphDescriptionAttr: "", 64 graphLeftAttr: "", 65 graphLineStyleAttr: "How the line should change between point. A string containing either 'stepped', 'interpolated' or null.", 66 graphNameAttr: "", 67 graphNotesAttr: "", 68 graphRightAttr: "", 69 graphMetricAttr: "", 70 graphMetricClusterAttr: "", 71 graphStyleAttr: "", 72 graphTagsAttr: "", 73 } 74 75 var graphMetricDescriptions = attrDescrs{ 76 // circonus_graph.metric.* resource attribute names 77 graphMetricActiveAttr: "", 78 graphMetricAlphaAttr: "", 79 graphMetricAxisAttr: "", 80 graphMetricCAQLAttr: "", 81 graphMetricCheckAttr: "", 82 graphMetricColorAttr: "", 83 graphMetricFormulaAttr: "", 84 graphMetricFormulaLegendAttr: "", 85 graphMetricFunctionAttr: "", 86 graphMetricMetricTypeAttr: "", 87 graphMetricHumanNameAttr: "", 88 graphMetricNameAttr: "", 89 graphMetricStackAttr: "", 90 } 91 92 var graphMetricClusterDescriptions = attrDescrs{ 93 // circonus_graph.metric_cluster.* resource attribute names 94 graphMetricClusterActiveAttr: "", 95 graphMetricClusterAggregateAttr: "", 96 graphMetricClusterAxisAttr: "", 97 graphMetricClusterColorAttr: "", 98 graphMetricClusterQueryAttr: "", 99 graphMetricClusterHumanNameAttr: "", 100 } 101 102 // NOTE(sean@): There is no way to set a description on map inputs, but if that 103 // does happen: 104 // 105 // var graphMetricAxisOptionDescriptions = attrDescrs{ 106 // // circonus_graph.if.value.over.* resource attribute names 107 // graphAxisLogarithmicAttr: "", 108 // graphAxisMaxAttr: "", 109 // graphAxisMinAttr: "", 110 // } 111 112 func resourceGraph() *schema.Resource { 113 makeConflictsWith := func(in ...schemaAttr) []string { 114 out := make([]string, 0, len(in)) 115 for _, attr := range in { 116 out = append(out, string(graphMetricAttr)+"."+string(attr)) 117 } 118 return out 119 } 120 121 return &schema.Resource{ 122 Create: graphCreate, 123 Read: graphRead, 124 Update: graphUpdate, 125 Delete: graphDelete, 126 Exists: graphExists, 127 Importer: &schema.ResourceImporter{ 128 State: schema.ImportStatePassthrough, 129 }, 130 131 Schema: convertToHelperSchema(graphDescriptions, map[schemaAttr]*schema.Schema{ 132 graphDescriptionAttr: &schema.Schema{ 133 Type: schema.TypeString, 134 Optional: true, 135 StateFunc: suppressWhitespace, 136 }, 137 graphLeftAttr: &schema.Schema{ 138 Type: schema.TypeMap, 139 Elem: schema.TypeString, 140 Optional: true, 141 ValidateFunc: validateGraphAxisOptions, 142 }, 143 graphLineStyleAttr: &schema.Schema{ 144 Type: schema.TypeString, 145 Optional: true, 146 Default: defaultGraphLineStyle, 147 ValidateFunc: validateStringIn(graphLineStyleAttr, validGraphLineStyles), 148 }, 149 graphNameAttr: &schema.Schema{ 150 Type: schema.TypeString, 151 Required: true, 152 ValidateFunc: validateRegexp(graphNameAttr, `.+`), 153 }, 154 graphNotesAttr: &schema.Schema{ 155 Type: schema.TypeString, 156 Optional: true, 157 }, 158 graphRightAttr: &schema.Schema{ 159 Type: schema.TypeMap, 160 Elem: schema.TypeString, 161 Optional: true, 162 ValidateFunc: validateGraphAxisOptions, 163 }, 164 graphMetricAttr: &schema.Schema{ 165 Type: schema.TypeList, 166 Optional: true, 167 MinItems: 1, 168 Elem: &schema.Resource{ 169 Schema: convertToHelperSchema(graphMetricDescriptions, map[schemaAttr]*schema.Schema{ 170 graphMetricActiveAttr: &schema.Schema{ 171 Type: schema.TypeBool, 172 Optional: true, 173 Default: true, 174 }, 175 graphMetricAlphaAttr: &schema.Schema{ 176 Type: schema.TypeFloat, 177 Optional: true, 178 ValidateFunc: validateFuncs( 179 validateFloatMin(graphMetricAlphaAttr, 0.0), 180 validateFloatMax(graphMetricAlphaAttr, 1.0), 181 ), 182 }, 183 graphMetricAxisAttr: &schema.Schema{ 184 Type: schema.TypeString, 185 Optional: true, 186 Default: "left", 187 ValidateFunc: validateStringIn(graphMetricAxisAttr, validAxisAttrs), 188 }, 189 graphMetricCAQLAttr: &schema.Schema{ 190 Type: schema.TypeString, 191 Optional: true, 192 ValidateFunc: validateRegexp(graphMetricCAQLAttr, `.+`), 193 ConflictsWith: makeConflictsWith(graphMetricCheckAttr, graphMetricNameAttr), 194 }, 195 graphMetricCheckAttr: &schema.Schema{ 196 Type: schema.TypeString, 197 Optional: true, 198 ValidateFunc: validateRegexp(graphMetricCheckAttr, config.CheckCIDRegex), 199 ConflictsWith: makeConflictsWith(graphMetricCAQLAttr), 200 }, 201 graphMetricColorAttr: &schema.Schema{ 202 Type: schema.TypeString, 203 Optional: true, 204 ValidateFunc: validateRegexp(graphMetricColorAttr, `^#[0-9a-fA-F]{6}$`), 205 }, 206 graphMetricFormulaAttr: &schema.Schema{ 207 Type: schema.TypeString, 208 Optional: true, 209 ValidateFunc: validateRegexp(graphMetricFormulaAttr, `^.+$`), 210 }, 211 graphMetricFormulaLegendAttr: &schema.Schema{ 212 Type: schema.TypeString, 213 Optional: true, 214 ValidateFunc: validateRegexp(graphMetricFormulaLegendAttr, `^.+$`), 215 }, 216 graphMetricFunctionAttr: &schema.Schema{ 217 Type: schema.TypeString, 218 Optional: true, 219 Default: defaultGraphFunction, 220 ValidateFunc: validateStringIn(graphMetricFunctionAttr, validGraphFunctionValues), 221 }, 222 graphMetricMetricTypeAttr: &schema.Schema{ 223 Type: schema.TypeString, 224 Required: true, 225 ValidateFunc: validateStringIn(graphMetricMetricTypeAttr, validMetricTypes), 226 }, 227 graphMetricHumanNameAttr: &schema.Schema{ 228 Type: schema.TypeString, 229 Optional: true, 230 ValidateFunc: validateRegexp(graphMetricHumanNameAttr, `.+`), 231 }, 232 graphMetricNameAttr: &schema.Schema{ 233 Type: schema.TypeString, 234 Optional: true, 235 ValidateFunc: validateRegexp(graphMetricNameAttr, `^[\S]+$`), 236 }, 237 graphMetricStackAttr: &schema.Schema{ 238 Type: schema.TypeString, 239 Optional: true, 240 ValidateFunc: validateRegexp(graphMetricStackAttr, `^[\d]*$`), 241 }, 242 }), 243 }, 244 }, 245 graphMetricClusterAttr: &schema.Schema{ 246 Type: schema.TypeList, 247 Optional: true, 248 MinItems: 1, 249 Elem: &schema.Resource{ 250 Schema: convertToHelperSchema(graphMetricClusterDescriptions, map[schemaAttr]*schema.Schema{ 251 graphMetricClusterActiveAttr: &schema.Schema{ 252 Type: schema.TypeBool, 253 Optional: true, 254 Default: true, 255 }, 256 graphMetricClusterAggregateAttr: &schema.Schema{ 257 Type: schema.TypeString, 258 Optional: true, 259 Default: "none", 260 ValidateFunc: validateStringIn(graphMetricClusterAggregateAttr, validAggregateFuncs), 261 }, 262 graphMetricClusterAxisAttr: &schema.Schema{ 263 Type: schema.TypeString, 264 Optional: true, 265 Default: "left", 266 ValidateFunc: validateStringIn(graphMetricClusterAttr, validAxisAttrs), 267 }, 268 graphMetricClusterColorAttr: &schema.Schema{ 269 Type: schema.TypeString, 270 Optional: true, 271 ValidateFunc: validateRegexp(graphMetricClusterColorAttr, `^#[0-9a-fA-F]{6}$`), 272 }, 273 graphMetricClusterQueryAttr: &schema.Schema{ 274 Type: schema.TypeString, 275 Optional: true, 276 ValidateFunc: validateRegexp(graphMetricClusterQueryAttr, config.MetricClusterCIDRegex), 277 }, 278 graphMetricClusterHumanNameAttr: &schema.Schema{ 279 Type: schema.TypeString, 280 Required: true, 281 ValidateFunc: validateRegexp(graphMetricHumanNameAttr, `.+`), 282 }, 283 }), 284 }, 285 }, 286 graphStyleAttr: &schema.Schema{ 287 Type: schema.TypeString, 288 Optional: true, 289 Default: defaultGraphStyle, 290 ValidateFunc: validateStringIn(graphStyleAttr, validGraphStyles), 291 }, 292 graphTagsAttr: tagMakeConfigSchema(graphTagsAttr), 293 }), 294 } 295 } 296 297 func graphCreate(d *schema.ResourceData, meta interface{}) error { 298 ctxt := meta.(*providerContext) 299 g := newGraph() 300 if err := g.ParseConfig(d); err != nil { 301 return errwrap.Wrapf("error parsing graph schema during create: {{err}}", err) 302 } 303 304 if err := g.Create(ctxt); err != nil { 305 return errwrap.Wrapf("error creating graph: {{err}}", err) 306 } 307 308 d.SetId(g.CID) 309 310 return graphRead(d, meta) 311 } 312 313 func graphExists(d *schema.ResourceData, meta interface{}) (bool, error) { 314 ctxt := meta.(*providerContext) 315 316 cid := d.Id() 317 g, err := ctxt.client.FetchGraph(api.CIDType(&cid)) 318 if err != nil { 319 if strings.Contains(err.Error(), defaultCirconus404ErrorString) { 320 return false, nil 321 } 322 323 return false, err 324 } 325 326 if g.CID == "" { 327 return false, nil 328 } 329 330 return true, nil 331 } 332 333 // graphRead pulls data out of the Graph object and stores it into the 334 // appropriate place in the statefile. 335 func graphRead(d *schema.ResourceData, meta interface{}) error { 336 ctxt := meta.(*providerContext) 337 338 cid := d.Id() 339 g, err := loadGraph(ctxt, api.CIDType(&cid)) 340 if err != nil { 341 return err 342 } 343 344 d.SetId(g.CID) 345 346 metrics := make([]interface{}, 0, len(g.Datapoints)) 347 for _, datapoint := range g.Datapoints { 348 dataPointAttrs := make(map[string]interface{}, 13) // 13 == len(members in api.GraphDatapoint) 349 350 dataPointAttrs[string(graphMetricActiveAttr)] = !datapoint.Hidden 351 352 if datapoint.Alpha != nil && *datapoint.Alpha != 0 { 353 dataPointAttrs[string(graphMetricAlphaAttr)] = *datapoint.Alpha 354 } 355 356 switch datapoint.Axis { 357 case "l", "": 358 dataPointAttrs[string(graphMetricAxisAttr)] = "left" 359 case "r": 360 dataPointAttrs[string(graphMetricAxisAttr)] = "right" 361 default: 362 return fmt.Errorf("PROVIDER BUG: Unsupported axis type %q", datapoint.Axis) 363 } 364 365 if datapoint.CAQL != nil { 366 dataPointAttrs[string(graphMetricCAQLAttr)] = *datapoint.CAQL 367 } 368 369 if datapoint.CheckID != 0 { 370 dataPointAttrs[string(graphMetricCheckAttr)] = fmt.Sprintf("%s/%d", config.CheckPrefix, datapoint.CheckID) 371 } 372 373 if datapoint.Color != nil { 374 dataPointAttrs[string(graphMetricColorAttr)] = *datapoint.Color 375 } 376 377 if datapoint.DataFormula != nil { 378 dataPointAttrs[string(graphMetricFormulaAttr)] = *datapoint.DataFormula 379 } 380 381 switch datapoint.Derive.(type) { 382 case bool: 383 case string: 384 dataPointAttrs[string(graphMetricFunctionAttr)] = datapoint.Derive.(string) 385 default: 386 return fmt.Errorf("PROVIDER BUG: Unsupported type for derive: %T", datapoint.Derive) 387 } 388 389 if datapoint.LegendFormula != nil { 390 dataPointAttrs[string(graphMetricFormulaLegendAttr)] = *datapoint.LegendFormula 391 } 392 393 if datapoint.MetricName != "" { 394 dataPointAttrs[string(graphMetricNameAttr)] = datapoint.MetricName 395 } 396 397 if datapoint.MetricType != "" { 398 dataPointAttrs[string(graphMetricMetricTypeAttr)] = datapoint.MetricType 399 } 400 401 if datapoint.Name != "" { 402 dataPointAttrs[string(graphMetricHumanNameAttr)] = datapoint.Name 403 } 404 405 if datapoint.Stack != nil { 406 dataPointAttrs[string(graphMetricStackAttr)] = fmt.Sprintf("%d", *datapoint.Stack) 407 } 408 409 metrics = append(metrics, dataPointAttrs) 410 } 411 412 metricClusters := make([]interface{}, 0, len(g.MetricClusters)) 413 for _, metricCluster := range g.MetricClusters { 414 metricClusterAttrs := make(map[string]interface{}, 8) // 8 == len(num struct attrs in api.GraphMetricCluster) 415 416 metricClusterAttrs[string(graphMetricClusterActiveAttr)] = !metricCluster.Hidden 417 418 if metricCluster.AggregateFunc != "" { 419 metricClusterAttrs[string(graphMetricClusterAggregateAttr)] = metricCluster.AggregateFunc 420 } 421 422 switch metricCluster.Axis { 423 case "l", "": 424 metricClusterAttrs[string(graphMetricClusterAxisAttr)] = "left" 425 case "r": 426 metricClusterAttrs[string(graphMetricClusterAxisAttr)] = "right" 427 default: 428 return fmt.Errorf("PROVIDER BUG: Unsupported axis type %q", metricCluster.Axis) 429 } 430 431 if metricCluster.Color != nil { 432 metricClusterAttrs[string(graphMetricClusterColorAttr)] = *metricCluster.Color 433 } 434 435 if metricCluster.DataFormula != nil { 436 metricClusterAttrs[string(graphMetricFormulaAttr)] = *metricCluster.DataFormula 437 } 438 439 if metricCluster.LegendFormula != nil { 440 metricClusterAttrs[string(graphMetricFormulaLegendAttr)] = *metricCluster.LegendFormula 441 } 442 443 if metricCluster.MetricCluster != "" { 444 metricClusterAttrs[string(graphMetricClusterQueryAttr)] = metricCluster.MetricCluster 445 } 446 447 if metricCluster.Name != "" { 448 metricClusterAttrs[string(graphMetricHumanNameAttr)] = metricCluster.Name 449 } 450 451 if metricCluster.Stack != nil { 452 metricClusterAttrs[string(graphMetricStackAttr)] = fmt.Sprintf("%d", *metricCluster.Stack) 453 } 454 455 metricClusters = append(metricClusters, metricClusterAttrs) 456 } 457 458 leftAxisMap := make(map[string]interface{}, 3) 459 if g.LogLeftY != nil { 460 leftAxisMap[string(graphAxisLogarithmicAttr)] = fmt.Sprintf("%d", *g.LogLeftY) 461 } 462 463 if g.MaxLeftY != nil { 464 leftAxisMap[string(graphAxisMaxAttr)] = strconv.FormatFloat(*g.MaxLeftY, 'f', -1, 64) 465 } 466 467 if g.MinLeftY != nil { 468 leftAxisMap[string(graphAxisMinAttr)] = strconv.FormatFloat(*g.MinLeftY, 'f', -1, 64) 469 } 470 471 rightAxisMap := make(map[string]interface{}, 3) 472 if g.LogRightY != nil { 473 rightAxisMap[string(graphAxisLogarithmicAttr)] = fmt.Sprintf("%d", *g.LogRightY) 474 } 475 476 if g.MaxRightY != nil { 477 rightAxisMap[string(graphAxisMaxAttr)] = strconv.FormatFloat(*g.MaxRightY, 'f', -1, 64) 478 } 479 480 if g.MinRightY != nil { 481 rightAxisMap[string(graphAxisMinAttr)] = strconv.FormatFloat(*g.MinRightY, 'f', -1, 64) 482 } 483 484 d.Set(graphDescriptionAttr, g.Description) 485 486 if err := d.Set(graphLeftAttr, leftAxisMap); err != nil { 487 return errwrap.Wrapf(fmt.Sprintf("Unable to store graph %q attribute: {{err}}", graphLeftAttr), err) 488 } 489 490 d.Set(graphLineStyleAttr, g.LineStyle) 491 d.Set(graphNameAttr, g.Title) 492 d.Set(graphNotesAttr, indirect(g.Notes)) 493 494 if err := d.Set(graphRightAttr, rightAxisMap); err != nil { 495 return errwrap.Wrapf(fmt.Sprintf("Unable to store graph %q attribute: {{err}}", graphRightAttr), err) 496 } 497 498 if err := d.Set(graphMetricAttr, metrics); err != nil { 499 return errwrap.Wrapf(fmt.Sprintf("Unable to store graph %q attribute: {{err}}", graphMetricAttr), err) 500 } 501 502 if err := d.Set(graphMetricClusterAttr, metricClusters); err != nil { 503 return errwrap.Wrapf(fmt.Sprintf("Unable to store graph %q attribute: {{err}}", graphMetricClusterAttr), err) 504 } 505 506 d.Set(graphStyleAttr, g.Style) 507 508 if err := d.Set(graphTagsAttr, tagsToState(apiToTags(g.Tags))); err != nil { 509 return errwrap.Wrapf(fmt.Sprintf("Unable to store graph %q attribute: {{err}}", graphTagsAttr), err) 510 } 511 512 return nil 513 } 514 515 func graphUpdate(d *schema.ResourceData, meta interface{}) error { 516 ctxt := meta.(*providerContext) 517 g := newGraph() 518 if err := g.ParseConfig(d); err != nil { 519 return err 520 } 521 522 g.CID = d.Id() 523 if err := g.Update(ctxt); err != nil { 524 return errwrap.Wrapf(fmt.Sprintf("unable to update graph %q: {{err}}", d.Id()), err) 525 } 526 527 return graphRead(d, meta) 528 } 529 530 func graphDelete(d *schema.ResourceData, meta interface{}) error { 531 ctxt := meta.(*providerContext) 532 533 cid := d.Id() 534 if _, err := ctxt.client.DeleteGraphByCID(api.CIDType(&cid)); err != nil { 535 return errwrap.Wrapf(fmt.Sprintf("unable to delete graph %q: {{err}}", d.Id()), err) 536 } 537 538 d.SetId("") 539 540 return nil 541 } 542 543 type circonusGraph struct { 544 api.Graph 545 } 546 547 func newGraph() circonusGraph { 548 g := circonusGraph{ 549 Graph: *api.NewGraph(), 550 } 551 552 return g 553 } 554 555 func loadGraph(ctxt *providerContext, cid api.CIDType) (circonusGraph, error) { 556 var g circonusGraph 557 ng, err := ctxt.client.FetchGraph(cid) 558 if err != nil { 559 return circonusGraph{}, err 560 } 561 g.Graph = *ng 562 563 return g, nil 564 } 565 566 // ParseConfig reads Terraform config data and stores the information into a 567 // Circonus Graph object. ParseConfig and graphRead() must be kept in sync. 568 func (g *circonusGraph) ParseConfig(d *schema.ResourceData) error { 569 g.Datapoints = make([]api.GraphDatapoint, 0, defaultGraphDatapoints) 570 571 if v, found := d.GetOk(graphLeftAttr); found { 572 listRaw := v.(map[string]interface{}) 573 leftAxisMap := make(map[string]interface{}, len(listRaw)) 574 for k, v := range listRaw { 575 leftAxisMap[k] = v 576 } 577 578 if v, ok := leftAxisMap[string(graphAxisLogarithmicAttr)]; ok { 579 i64, _ := strconv.ParseInt(v.(string), 10, 64) 580 i := int(i64) 581 g.LogLeftY = &i 582 } 583 584 if v, ok := leftAxisMap[string(graphAxisMaxAttr)]; ok && v.(string) != "" { 585 f, _ := strconv.ParseFloat(v.(string), 64) 586 g.MaxLeftY = &f 587 } 588 589 if v, ok := leftAxisMap[string(graphAxisMinAttr)]; ok && v.(string) != "" { 590 f, _ := strconv.ParseFloat(v.(string), 64) 591 g.MinLeftY = &f 592 } 593 } 594 595 if v, found := d.GetOk(graphRightAttr); found { 596 listRaw := v.(map[string]interface{}) 597 rightAxisMap := make(map[string]interface{}, len(listRaw)) 598 for k, v := range listRaw { 599 rightAxisMap[k] = v 600 } 601 602 if v, ok := rightAxisMap[string(graphAxisLogarithmicAttr)]; ok { 603 i64, _ := strconv.ParseInt(v.(string), 10, 64) 604 i := int(i64) 605 g.LogRightY = &i 606 } 607 608 if v, ok := rightAxisMap[string(graphAxisMaxAttr)]; ok && v.(string) != "" { 609 f, _ := strconv.ParseFloat(v.(string), 64) 610 g.MaxRightY = &f 611 } 612 613 if v, ok := rightAxisMap[string(graphAxisMinAttr)]; ok && v.(string) != "" { 614 f, _ := strconv.ParseFloat(v.(string), 64) 615 g.MinRightY = &f 616 } 617 } 618 619 if v, found := d.GetOk(graphDescriptionAttr); found { 620 g.Description = v.(string) 621 } 622 623 if v, found := d.GetOk(graphLineStyleAttr); found { 624 switch v.(type) { 625 case string: 626 s := v.(string) 627 g.LineStyle = &s 628 case *string: 629 g.LineStyle = v.(*string) 630 default: 631 return fmt.Errorf("PROVIDER BUG: unsupported type for %q: %T", graphLineStyleAttr, v) 632 } 633 } 634 635 if v, found := d.GetOk(graphNameAttr); found { 636 g.Title = v.(string) 637 } 638 639 if v, found := d.GetOk(graphNotesAttr); found { 640 s := v.(string) 641 g.Notes = &s 642 } 643 644 if listRaw, found := d.GetOk(graphMetricAttr); found { 645 metricList := listRaw.([]interface{}) 646 for _, metricListElem := range metricList { 647 metricAttrs := newInterfaceMap(metricListElem.(map[string]interface{})) 648 datapoint := api.GraphDatapoint{} 649 650 if v, found := metricAttrs[graphMetricActiveAttr]; found { 651 datapoint.Hidden = !(v.(bool)) 652 } 653 654 if v, found := metricAttrs[graphMetricAlphaAttr]; found { 655 f := v.(float64) 656 if f != 0 { 657 datapoint.Alpha = &f 658 } 659 } 660 661 if v, found := metricAttrs[graphMetricAxisAttr]; found { 662 switch v.(string) { 663 case "left", "": 664 datapoint.Axis = "l" 665 case "right": 666 datapoint.Axis = "r" 667 default: 668 return fmt.Errorf("PROVIDER BUG: Unsupported axis attribute %q: %q", graphMetricAxisAttr, v.(string)) 669 } 670 } 671 672 if v, found := metricAttrs[graphMetricCheckAttr]; found { 673 re := regexp.MustCompile(config.CheckCIDRegex) 674 matches := re.FindStringSubmatch(v.(string)) 675 if len(matches) == 3 { 676 checkID, _ := strconv.ParseUint(matches[2], 10, 64) 677 datapoint.CheckID = uint(checkID) 678 } 679 } 680 681 if v, found := metricAttrs[graphMetricColorAttr]; found { 682 s := v.(string) 683 datapoint.Color = &s 684 } 685 686 if v, found := metricAttrs[graphMetricFormulaAttr]; found { 687 switch v.(type) { 688 case string: 689 s := v.(string) 690 datapoint.DataFormula = &s 691 case *string: 692 datapoint.DataFormula = v.(*string) 693 default: 694 return fmt.Errorf("PROVIDER BUG: unsupported type for %q: %T", graphMetricAttr, v) 695 } 696 } 697 698 if v, found := metricAttrs[graphMetricFunctionAttr]; found { 699 s := v.(string) 700 if s != "" { 701 datapoint.Derive = s 702 } else { 703 datapoint.Derive = false 704 } 705 } else { 706 datapoint.Derive = false 707 } 708 709 if v, found := metricAttrs[graphMetricFormulaLegendAttr]; found { 710 switch u := v.(type) { 711 case string: 712 datapoint.LegendFormula = &u 713 case *string: 714 datapoint.LegendFormula = u 715 default: 716 return fmt.Errorf("PROVIDER BUG: unsupported type for %q: %T", graphMetricAttr, v) 717 } 718 } 719 720 if v, found := metricAttrs[graphMetricNameAttr]; found { 721 s := v.(string) 722 if s != "" { 723 datapoint.MetricName = s 724 } 725 } 726 727 if v, found := metricAttrs[graphMetricMetricTypeAttr]; found { 728 s := v.(string) 729 if s != "" { 730 datapoint.MetricType = s 731 } 732 } 733 734 if v, found := metricAttrs[graphMetricHumanNameAttr]; found { 735 s := v.(string) 736 if s != "" { 737 datapoint.Name = s 738 } 739 } 740 741 if v, found := metricAttrs[graphMetricStackAttr]; found { 742 var stackStr string 743 switch u := v.(type) { 744 case string: 745 stackStr = u 746 case *string: 747 if u != nil { 748 stackStr = *u 749 } 750 default: 751 return fmt.Errorf("PROVIDER BUG: unsupported type for %q: %T", graphMetricStackAttr, v) 752 } 753 754 if stackStr != "" { 755 u64, _ := strconv.ParseUint(stackStr, 10, 64) 756 u := uint(u64) 757 datapoint.Stack = &u 758 } 759 } 760 761 g.Datapoints = append(g.Datapoints, datapoint) 762 } 763 } 764 765 if listRaw, found := d.GetOk(graphMetricClusterAttr); found { 766 metricClusterList := listRaw.([]interface{}) 767 768 for _, metricClusterListRaw := range metricClusterList { 769 metricClusterAttrs := newInterfaceMap(metricClusterListRaw.(map[string]interface{})) 770 771 metricCluster := api.GraphMetricCluster{} 772 773 if v, found := metricClusterAttrs[graphMetricClusterActiveAttr]; found { 774 metricCluster.Hidden = !(v.(bool)) 775 } 776 777 if v, found := metricClusterAttrs[graphMetricClusterAggregateAttr]; found { 778 metricCluster.AggregateFunc = v.(string) 779 } 780 781 if v, found := metricClusterAttrs[graphMetricClusterAxisAttr]; found { 782 switch v.(string) { 783 case "left", "": 784 metricCluster.Axis = "l" 785 case "right": 786 metricCluster.Axis = "r" 787 default: 788 return fmt.Errorf("PROVIDER BUG: Unsupported axis attribute %q: %q", graphMetricClusterAxisAttr, v.(string)) 789 } 790 } 791 792 if v, found := metricClusterAttrs[graphMetricClusterColorAttr]; found { 793 s := v.(string) 794 if s != "" { 795 metricCluster.Color = &s 796 } 797 } 798 799 if v, found := metricClusterAttrs[graphMetricFormulaAttr]; found { 800 switch v.(type) { 801 case string: 802 s := v.(string) 803 metricCluster.DataFormula = &s 804 case *string: 805 metricCluster.DataFormula = v.(*string) 806 default: 807 return fmt.Errorf("PROVIDER BUG: unsupported type for %q: %T", graphMetricFormulaAttr, v) 808 } 809 } 810 811 if v, found := metricClusterAttrs[graphMetricFormulaLegendAttr]; found { 812 switch v.(type) { 813 case string: 814 s := v.(string) 815 metricCluster.LegendFormula = &s 816 case *string: 817 metricCluster.LegendFormula = v.(*string) 818 default: 819 return fmt.Errorf("PROVIDER BUG: unsupported type for %q: %T", graphMetricFormulaLegendAttr, v) 820 } 821 } 822 823 if v, found := metricClusterAttrs[graphMetricClusterQueryAttr]; found { 824 s := v.(string) 825 if s != "" { 826 metricCluster.MetricCluster = s 827 } 828 } 829 830 if v, found := metricClusterAttrs[graphMetricHumanNameAttr]; found { 831 s := v.(string) 832 if s != "" { 833 metricCluster.Name = s 834 } 835 } 836 837 if v, found := metricClusterAttrs[graphMetricStackAttr]; found { 838 var stackStr string 839 switch u := v.(type) { 840 case string: 841 stackStr = u 842 case *string: 843 if u != nil { 844 stackStr = *u 845 } 846 default: 847 return fmt.Errorf("PROVIDER BUG: unsupported type for %q: %T", graphMetricStackAttr, v) 848 } 849 850 if stackStr != "" { 851 u64, _ := strconv.ParseUint(stackStr, 10, 64) 852 u := uint(u64) 853 metricCluster.Stack = &u 854 } 855 } 856 857 g.MetricClusters = append(g.MetricClusters, metricCluster) 858 } 859 } 860 861 if v, found := d.GetOk(graphStyleAttr); found { 862 switch v.(type) { 863 case string: 864 s := v.(string) 865 g.Style = &s 866 case *string: 867 g.Style = v.(*string) 868 default: 869 return fmt.Errorf("PROVIDER BUG: unsupported type for %q: %T", graphStyleAttr, v) 870 } 871 } 872 873 if v, found := d.GetOk(graphTagsAttr); found { 874 g.Tags = derefStringList(flattenSet(v.(*schema.Set))) 875 } 876 877 if err := g.Validate(); err != nil { 878 return err 879 } 880 881 return nil 882 } 883 884 func (g *circonusGraph) Create(ctxt *providerContext) error { 885 ng, err := ctxt.client.CreateGraph(&g.Graph) 886 if err != nil { 887 return err 888 } 889 890 g.CID = ng.CID 891 892 return nil 893 } 894 895 func (g *circonusGraph) Update(ctxt *providerContext) error { 896 _, err := ctxt.client.UpdateGraph(&g.Graph) 897 if err != nil { 898 return errwrap.Wrapf(fmt.Sprintf("Unable to update graph %s: {{err}}", g.CID), err) 899 } 900 901 return nil 902 } 903 904 func (g *circonusGraph) Validate() error { 905 for i, datapoint := range g.Datapoints { 906 if *g.Style == apiGraphStyleLine && datapoint.Alpha != nil && *datapoint.Alpha != 0 { 907 return fmt.Errorf("%s can not be set on graphs with style %s", graphMetricAlphaAttr, apiGraphStyleLine) 908 } 909 910 if datapoint.CheckID != 0 && datapoint.MetricName == "" { 911 return fmt.Errorf("Error with %s[%d] name=%q: %s is set, missing attribute %s must also be set", graphMetricAttr, i, datapoint.Name, graphMetricCheckAttr, graphMetricNameAttr) 912 } 913 914 if datapoint.CheckID == 0 && datapoint.MetricName != "" { 915 return fmt.Errorf("Error with %s[%d] name=%q: %s is set, missing attribute %s must also be set", graphMetricAttr, i, datapoint.Name, graphMetricNameAttr, graphMetricCheckAttr) 916 } 917 918 if datapoint.CAQL != nil && (datapoint.CheckID != 0 || datapoint.MetricName != "") { 919 return fmt.Errorf("Error with %s[%d] name=%q: %q attribute is mutually exclusive with attributes %s or %s", graphMetricAttr, i, datapoint.Name, graphMetricCAQLAttr, graphMetricNameAttr, graphMetricCheckAttr) 920 } 921 } 922 923 for i, mc := range g.MetricClusters { 924 if mc.AggregateFunc != "" && (mc.Color == nil || *mc.Color == "") { 925 return fmt.Errorf("Error with %s[%d] name=%q: %s is a required attribute for graphs with %s set", graphMetricClusterAttr, i, mc.Name, graphMetricClusterColorAttr, graphMetricClusterAggregateAttr) 926 } 927 } 928 929 return nil 930 }