931 lines
27 KiB
Go
931 lines
27 KiB
Go
![]() |
package circonus
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"regexp"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/circonus-labs/circonus-gometrics/api"
|
||
|
"github.com/circonus-labs/circonus-gometrics/api/config"
|
||
|
"github.com/hashicorp/errwrap"
|
||
|
"github.com/hashicorp/terraform/helper/schema"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
// circonus_graph.* resource attribute names
|
||
|
graphDescriptionAttr = "description"
|
||
|
graphLeftAttr = "left"
|
||
|
graphLineStyleAttr = "line_style"
|
||
|
graphMetricClusterAttr = "metric_cluster"
|
||
|
graphNameAttr = "name"
|
||
|
graphNotesAttr = "notes"
|
||
|
graphRightAttr = "right"
|
||
|
graphMetricAttr = "metric"
|
||
|
graphStyleAttr = "graph_style"
|
||
|
graphTagsAttr = "tags"
|
||
|
|
||
|
// circonus_graph.metric.* resource attribute names
|
||
|
graphMetricActiveAttr = "active"
|
||
|
graphMetricAlphaAttr = "alpha"
|
||
|
graphMetricAxisAttr = "axis"
|
||
|
graphMetricCAQLAttr = "caql"
|
||
|
graphMetricCheckAttr = "check"
|
||
|
graphMetricColorAttr = "color"
|
||
|
graphMetricFormulaAttr = "formula"
|
||
|
graphMetricFormulaLegendAttr = "legend_formula"
|
||
|
graphMetricFunctionAttr = "function"
|
||
|
graphMetricHumanNameAttr = "name"
|
||
|
graphMetricMetricTypeAttr = "metric_type"
|
||
|
graphMetricNameAttr = "metric_name"
|
||
|
graphMetricStackAttr = "stack"
|
||
|
|
||
|
// circonus_graph.metric_cluster.* resource attribute names
|
||
|
graphMetricClusterActiveAttr = "active"
|
||
|
graphMetricClusterAggregateAttr = "aggregate"
|
||
|
graphMetricClusterAxisAttr = "axis"
|
||
|
graphMetricClusterColorAttr = "color"
|
||
|
graphMetricClusterQueryAttr = "query"
|
||
|
graphMetricClusterHumanNameAttr = "name"
|
||
|
|
||
|
// circonus_graph.{left,right}.* resource attribute names
|
||
|
graphAxisLogarithmicAttr = "logarithmic"
|
||
|
graphAxisMaxAttr = "max"
|
||
|
graphAxisMinAttr = "min"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
apiGraphStyleLine = "line"
|
||
|
)
|
||
|
|
||
|
var graphDescriptions = attrDescrs{
|
||
|
// circonus_graph.* resource attribute names
|
||
|
graphDescriptionAttr: "",
|
||
|
graphLeftAttr: "",
|
||
|
graphLineStyleAttr: "How the line should change between point. A string containing either 'stepped', 'interpolated' or null.",
|
||
|
graphNameAttr: "",
|
||
|
graphNotesAttr: "",
|
||
|
graphRightAttr: "",
|
||
|
graphMetricAttr: "",
|
||
|
graphMetricClusterAttr: "",
|
||
|
graphStyleAttr: "",
|
||
|
graphTagsAttr: "",
|
||
|
}
|
||
|
|
||
|
var graphMetricDescriptions = attrDescrs{
|
||
|
// circonus_graph.metric.* resource attribute names
|
||
|
graphMetricActiveAttr: "",
|
||
|
graphMetricAlphaAttr: "",
|
||
|
graphMetricAxisAttr: "",
|
||
|
graphMetricCAQLAttr: "",
|
||
|
graphMetricCheckAttr: "",
|
||
|
graphMetricColorAttr: "",
|
||
|
graphMetricFormulaAttr: "",
|
||
|
graphMetricFormulaLegendAttr: "",
|
||
|
graphMetricFunctionAttr: "",
|
||
|
graphMetricMetricTypeAttr: "",
|
||
|
graphMetricHumanNameAttr: "",
|
||
|
graphMetricNameAttr: "",
|
||
|
graphMetricStackAttr: "",
|
||
|
}
|
||
|
|
||
|
var graphMetricClusterDescriptions = attrDescrs{
|
||
|
// circonus_graph.metric_cluster.* resource attribute names
|
||
|
graphMetricClusterActiveAttr: "",
|
||
|
graphMetricClusterAggregateAttr: "",
|
||
|
graphMetricClusterAxisAttr: "",
|
||
|
graphMetricClusterColorAttr: "",
|
||
|
graphMetricClusterQueryAttr: "",
|
||
|
graphMetricClusterHumanNameAttr: "",
|
||
|
}
|
||
|
|
||
|
// NOTE(sean@): There is no way to set a description on map inputs, but if that
|
||
|
// does happen:
|
||
|
//
|
||
|
// var graphMetricAxisOptionDescriptions = attrDescrs{
|
||
|
// // circonus_graph.if.value.over.* resource attribute names
|
||
|
// graphAxisLogarithmicAttr: "",
|
||
|
// graphAxisMaxAttr: "",
|
||
|
// graphAxisMinAttr: "",
|
||
|
// }
|
||
|
|
||
|
func resourceGraph() *schema.Resource {
|
||
|
makeConflictsWith := func(in ...schemaAttr) []string {
|
||
|
out := make([]string, 0, len(in))
|
||
|
for _, attr := range in {
|
||
|
out = append(out, string(graphMetricAttr)+"."+string(attr))
|
||
|
}
|
||
|
return out
|
||
|
}
|
||
|
|
||
|
return &schema.Resource{
|
||
|
Create: graphCreate,
|
||
|
Read: graphRead,
|
||
|
Update: graphUpdate,
|
||
|
Delete: graphDelete,
|
||
|
Exists: graphExists,
|
||
|
Importer: &schema.ResourceImporter{
|
||
|
State: schema.ImportStatePassthrough,
|
||
|
},
|
||
|
|
||
|
Schema: convertToHelperSchema(graphDescriptions, map[schemaAttr]*schema.Schema{
|
||
|
graphDescriptionAttr: &schema.Schema{
|
||
|
Type: schema.TypeString,
|
||
|
Optional: true,
|
||
|
StateFunc: suppressWhitespace,
|
||
|
},
|
||
|
graphLeftAttr: &schema.Schema{
|
||
|
Type: schema.TypeMap,
|
||
|
Elem: schema.TypeString,
|
||
|
Optional: true,
|
||
|
ValidateFunc: validateGraphAxisOptions,
|
||
|
},
|
||
|
graphLineStyleAttr: &schema.Schema{
|
||
|
Type: schema.TypeString,
|
||
|
Optional: true,
|
||
|
Default: defaultGraphLineStyle,
|
||
|
ValidateFunc: validateStringIn(graphLineStyleAttr, validGraphLineStyles),
|
||
|
},
|
||
|
graphNameAttr: &schema.Schema{
|
||
|
Type: schema.TypeString,
|
||
|
Required: true,
|
||
|
ValidateFunc: validateRegexp(graphNameAttr, `.+`),
|
||
|
},
|
||
|
graphNotesAttr: &schema.Schema{
|
||
|
Type: schema.TypeString,
|
||
|
Optional: true,
|
||
|
},
|
||
|
graphRightAttr: &schema.Schema{
|
||
|
Type: schema.TypeMap,
|
||
|
Elem: schema.TypeString,
|
||
|
Optional: true,
|
||
|
ValidateFunc: validateGraphAxisOptions,
|
||
|
},
|
||
|
graphMetricAttr: &schema.Schema{
|
||
|
Type: schema.TypeList,
|
||
|
Optional: true,
|
||
|
MinItems: 1,
|
||
|
Elem: &schema.Resource{
|
||
|
Schema: convertToHelperSchema(graphMetricDescriptions, map[schemaAttr]*schema.Schema{
|
||
|
graphMetricActiveAttr: &schema.Schema{
|
||
|
Type: schema.TypeBool,
|
||
|
Optional: true,
|
||
|
Default: true,
|
||
|
},
|
||
|
graphMetricAlphaAttr: &schema.Schema{
|
||
|
Type: schema.TypeFloat,
|
||
|
Optional: true,
|
||
|
ValidateFunc: validateFuncs(
|
||
|
validateFloatMin(graphMetricAlphaAttr, 0.0),
|
||
|
validateFloatMax(graphMetricAlphaAttr, 1.0),
|
||
|
),
|
||
|
},
|
||
|
graphMetricAxisAttr: &schema.Schema{
|
||
|
Type: schema.TypeString,
|
||
|
Optional: true,
|
||
|
Default: "left",
|
||
|
ValidateFunc: validateStringIn(graphMetricAxisAttr, validAxisAttrs),
|
||
|
},
|
||
|
graphMetricCAQLAttr: &schema.Schema{
|
||
|
Type: schema.TypeString,
|
||
|
Optional: true,
|
||
|
ValidateFunc: validateRegexp(graphMetricCAQLAttr, `.+`),
|
||
|
ConflictsWith: makeConflictsWith(graphMetricCheckAttr, graphMetricNameAttr),
|
||
|
},
|
||
|
graphMetricCheckAttr: &schema.Schema{
|
||
|
Type: schema.TypeString,
|
||
|
Optional: true,
|
||
|
ValidateFunc: validateRegexp(graphMetricCheckAttr, config.CheckCIDRegex),
|
||
|
ConflictsWith: makeConflictsWith(graphMetricCAQLAttr),
|
||
|
},
|
||
|
graphMetricColorAttr: &schema.Schema{
|
||
|
Type: schema.TypeString,
|
||
|
Optional: true,
|
||
|
ValidateFunc: validateRegexp(graphMetricColorAttr, `^#[0-9a-fA-F]{6}$`),
|
||
|
},
|
||
|
graphMetricFormulaAttr: &schema.Schema{
|
||
|
Type: schema.TypeString,
|
||
|
Optional: true,
|
||
|
ValidateFunc: validateRegexp(graphMetricFormulaAttr, `^.+$`),
|
||
|
},
|
||
|
graphMetricFormulaLegendAttr: &schema.Schema{
|
||
|
Type: schema.TypeString,
|
||
|
Optional: true,
|
||
|
ValidateFunc: validateRegexp(graphMetricFormulaLegendAttr, `^.+$`),
|
||
|
},
|
||
|
graphMetricFunctionAttr: &schema.Schema{
|
||
|
Type: schema.TypeString,
|
||
|
Optional: true,
|
||
|
Default: defaultGraphFunction,
|
||
|
ValidateFunc: validateStringIn(graphMetricFunctionAttr, validGraphFunctionValues),
|
||
|
},
|
||
|
graphMetricMetricTypeAttr: &schema.Schema{
|
||
|
Type: schema.TypeString,
|
||
|
Required: true,
|
||
|
ValidateFunc: validateStringIn(graphMetricMetricTypeAttr, validMetricTypes),
|
||
|
},
|
||
|
graphMetricHumanNameAttr: &schema.Schema{
|
||
|
Type: schema.TypeString,
|
||
|
Optional: true,
|
||
|
ValidateFunc: validateRegexp(graphMetricHumanNameAttr, `.+`),
|
||
|
},
|
||
|
graphMetricNameAttr: &schema.Schema{
|
||
|
Type: schema.TypeString,
|
||
|
Optional: true,
|
||
|
ValidateFunc: validateRegexp(graphMetricNameAttr, `^[\S]+$`),
|
||
|
},
|
||
|
graphMetricStackAttr: &schema.Schema{
|
||
|
Type: schema.TypeString,
|
||
|
Optional: true,
|
||
|
ValidateFunc: validateRegexp(graphMetricStackAttr, `^[\d]*$`),
|
||
|
},
|
||
|
}),
|
||
|
},
|
||
|
},
|
||
|
graphMetricClusterAttr: &schema.Schema{
|
||
|
Type: schema.TypeList,
|
||
|
Optional: true,
|
||
|
MinItems: 1,
|
||
|
Elem: &schema.Resource{
|
||
|
Schema: convertToHelperSchema(graphMetricClusterDescriptions, map[schemaAttr]*schema.Schema{
|
||
|
graphMetricClusterActiveAttr: &schema.Schema{
|
||
|
Type: schema.TypeBool,
|
||
|
Optional: true,
|
||
|
Default: true,
|
||
|
},
|
||
|
graphMetricClusterAggregateAttr: &schema.Schema{
|
||
|
Type: schema.TypeString,
|
||
|
Optional: true,
|
||
|
Default: "none",
|
||
|
ValidateFunc: validateStringIn(graphMetricClusterAggregateAttr, validAggregateFuncs),
|
||
|
},
|
||
|
graphMetricClusterAxisAttr: &schema.Schema{
|
||
|
Type: schema.TypeString,
|
||
|
Optional: true,
|
||
|
Default: "left",
|
||
|
ValidateFunc: validateStringIn(graphMetricClusterAttr, validAxisAttrs),
|
||
|
},
|
||
|
graphMetricClusterColorAttr: &schema.Schema{
|
||
|
Type: schema.TypeString,
|
||
|
Optional: true,
|
||
|
ValidateFunc: validateRegexp(graphMetricClusterColorAttr, `^#[0-9a-fA-F]{6}$`),
|
||
|
},
|
||
|
graphMetricClusterQueryAttr: &schema.Schema{
|
||
|
Type: schema.TypeString,
|
||
|
Optional: true,
|
||
|
ValidateFunc: validateRegexp(graphMetricClusterQueryAttr, config.MetricClusterCIDRegex),
|
||
|
},
|
||
|
graphMetricClusterHumanNameAttr: &schema.Schema{
|
||
|
Type: schema.TypeString,
|
||
|
Required: true,
|
||
|
ValidateFunc: validateRegexp(graphMetricHumanNameAttr, `.+`),
|
||
|
},
|
||
|
}),
|
||
|
},
|
||
|
},
|
||
|
graphStyleAttr: &schema.Schema{
|
||
|
Type: schema.TypeString,
|
||
|
Optional: true,
|
||
|
Default: defaultGraphStyle,
|
||
|
ValidateFunc: validateStringIn(graphStyleAttr, validGraphStyles),
|
||
|
},
|
||
|
graphTagsAttr: tagMakeConfigSchema(graphTagsAttr),
|
||
|
}),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func graphCreate(d *schema.ResourceData, meta interface{}) error {
|
||
|
ctxt := meta.(*providerContext)
|
||
|
g := newGraph()
|
||
|
if err := g.ParseConfig(d); err != nil {
|
||
|
return errwrap.Wrapf("error parsing graph schema during create: {{err}}", err)
|
||
|
}
|
||
|
|
||
|
if err := g.Create(ctxt); err != nil {
|
||
|
return errwrap.Wrapf("error creating graph: {{err}}", err)
|
||
|
}
|
||
|
|
||
|
d.SetId(g.CID)
|
||
|
|
||
|
return graphRead(d, meta)
|
||
|
}
|
||
|
|
||
|
func graphExists(d *schema.ResourceData, meta interface{}) (bool, error) {
|
||
|
ctxt := meta.(*providerContext)
|
||
|
|
||
|
cid := d.Id()
|
||
|
g, err := ctxt.client.FetchGraph(api.CIDType(&cid))
|
||
|
if err != nil {
|
||
|
if strings.Contains(err.Error(), defaultCirconus404ErrorString) {
|
||
|
return false, nil
|
||
|
}
|
||
|
|
||
|
return false, err
|
||
|
}
|
||
|
|
||
|
if g.CID == "" {
|
||
|
return false, nil
|
||
|
}
|
||
|
|
||
|
return true, nil
|
||
|
}
|
||
|
|
||
|
// graphRead pulls data out of the Graph object and stores it into the
|
||
|
// appropriate place in the statefile.
|
||
|
func graphRead(d *schema.ResourceData, meta interface{}) error {
|
||
|
ctxt := meta.(*providerContext)
|
||
|
|
||
|
cid := d.Id()
|
||
|
g, err := loadGraph(ctxt, api.CIDType(&cid))
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
d.SetId(g.CID)
|
||
|
|
||
|
metrics := make([]interface{}, 0, len(g.Datapoints))
|
||
|
for _, datapoint := range g.Datapoints {
|
||
|
dataPointAttrs := make(map[string]interface{}, 13) // 13 == len(members in api.GraphDatapoint)
|
||
|
|
||
|
dataPointAttrs[string(graphMetricActiveAttr)] = !datapoint.Hidden
|
||
|
|
||
|
if datapoint.Alpha != nil && *datapoint.Alpha != 0 {
|
||
|
dataPointAttrs[string(graphMetricAlphaAttr)] = *datapoint.Alpha
|
||
|
}
|
||
|
|
||
|
switch datapoint.Axis {
|
||
|
case "l", "":
|
||
|
dataPointAttrs[string(graphMetricAxisAttr)] = "left"
|
||
|
case "r":
|
||
|
dataPointAttrs[string(graphMetricAxisAttr)] = "right"
|
||
|
default:
|
||
|
return fmt.Errorf("PROVIDER BUG: Unsupported axis type %q", datapoint.Axis)
|
||
|
}
|
||
|
|
||
|
if datapoint.CAQL != nil {
|
||
|
dataPointAttrs[string(graphMetricCAQLAttr)] = *datapoint.CAQL
|
||
|
}
|
||
|
|
||
|
if datapoint.CheckID != 0 {
|
||
|
dataPointAttrs[string(graphMetricCheckAttr)] = fmt.Sprintf("%s/%d", config.CheckPrefix, datapoint.CheckID)
|
||
|
}
|
||
|
|
||
|
if datapoint.Color != nil {
|
||
|
dataPointAttrs[string(graphMetricColorAttr)] = *datapoint.Color
|
||
|
}
|
||
|
|
||
|
if datapoint.DataFormula != nil {
|
||
|
dataPointAttrs[string(graphMetricFormulaAttr)] = *datapoint.DataFormula
|
||
|
}
|
||
|
|
||
|
switch datapoint.Derive.(type) {
|
||
|
case bool:
|
||
|
case string:
|
||
|
dataPointAttrs[string(graphMetricFunctionAttr)] = datapoint.Derive.(string)
|
||
|
default:
|
||
|
return fmt.Errorf("PROVIDER BUG: Unsupported type for derive: %T", datapoint.Derive)
|
||
|
}
|
||
|
|
||
|
if datapoint.LegendFormula != nil {
|
||
|
dataPointAttrs[string(graphMetricFormulaLegendAttr)] = *datapoint.LegendFormula
|
||
|
}
|
||
|
|
||
|
if datapoint.MetricName != "" {
|
||
|
dataPointAttrs[string(graphMetricNameAttr)] = datapoint.MetricName
|
||
|
}
|
||
|
|
||
|
if datapoint.MetricType != "" {
|
||
|
dataPointAttrs[string(graphMetricMetricTypeAttr)] = datapoint.MetricType
|
||
|
}
|
||
|
|
||
|
if datapoint.Name != "" {
|
||
|
dataPointAttrs[string(graphMetricHumanNameAttr)] = datapoint.Name
|
||
|
}
|
||
|
|
||
|
if datapoint.Stack != nil {
|
||
|
dataPointAttrs[string(graphMetricStackAttr)] = fmt.Sprintf("%d", *datapoint.Stack)
|
||
|
}
|
||
|
|
||
|
metrics = append(metrics, dataPointAttrs)
|
||
|
}
|
||
|
|
||
|
metricClusters := make([]interface{}, 0, len(g.MetricClusters))
|
||
|
for _, metricCluster := range g.MetricClusters {
|
||
|
metricClusterAttrs := make(map[string]interface{}, 8) // 8 == len(num struct attrs in api.GraphMetricCluster)
|
||
|
|
||
|
metricClusterAttrs[string(graphMetricClusterActiveAttr)] = !metricCluster.Hidden
|
||
|
|
||
|
if metricCluster.AggregateFunc != "" {
|
||
|
metricClusterAttrs[string(graphMetricClusterAggregateAttr)] = metricCluster.AggregateFunc
|
||
|
}
|
||
|
|
||
|
switch metricCluster.Axis {
|
||
|
case "l", "":
|
||
|
metricClusterAttrs[string(graphMetricClusterAxisAttr)] = "left"
|
||
|
case "r":
|
||
|
metricClusterAttrs[string(graphMetricClusterAxisAttr)] = "right"
|
||
|
default:
|
||
|
return fmt.Errorf("PROVIDER BUG: Unsupported axis type %q", metricCluster.Axis)
|
||
|
}
|
||
|
|
||
|
if metricCluster.Color != nil {
|
||
|
metricClusterAttrs[string(graphMetricClusterColorAttr)] = *metricCluster.Color
|
||
|
}
|
||
|
|
||
|
if metricCluster.DataFormula != nil {
|
||
|
metricClusterAttrs[string(graphMetricFormulaAttr)] = *metricCluster.DataFormula
|
||
|
}
|
||
|
|
||
|
if metricCluster.LegendFormula != nil {
|
||
|
metricClusterAttrs[string(graphMetricFormulaLegendAttr)] = *metricCluster.LegendFormula
|
||
|
}
|
||
|
|
||
|
if metricCluster.MetricCluster != "" {
|
||
|
metricClusterAttrs[string(graphMetricClusterQueryAttr)] = metricCluster.MetricCluster
|
||
|
}
|
||
|
|
||
|
if metricCluster.Name != "" {
|
||
|
metricClusterAttrs[string(graphMetricHumanNameAttr)] = metricCluster.Name
|
||
|
}
|
||
|
|
||
|
if metricCluster.Stack != nil {
|
||
|
metricClusterAttrs[string(graphMetricStackAttr)] = fmt.Sprintf("%d", *metricCluster.Stack)
|
||
|
}
|
||
|
|
||
|
metricClusters = append(metricClusters, metricClusterAttrs)
|
||
|
}
|
||
|
|
||
|
leftAxisMap := make(map[string]interface{}, 3)
|
||
|
if g.LogLeftY != nil {
|
||
|
leftAxisMap[string(graphAxisLogarithmicAttr)] = fmt.Sprintf("%d", *g.LogLeftY)
|
||
|
}
|
||
|
|
||
|
if g.MaxLeftY != nil {
|
||
|
leftAxisMap[string(graphAxisMaxAttr)] = strconv.FormatFloat(*g.MaxLeftY, 'f', -1, 64)
|
||
|
}
|
||
|
|
||
|
if g.MinLeftY != nil {
|
||
|
leftAxisMap[string(graphAxisMinAttr)] = strconv.FormatFloat(*g.MinLeftY, 'f', -1, 64)
|
||
|
}
|
||
|
|
||
|
rightAxisMap := make(map[string]interface{}, 3)
|
||
|
if g.LogRightY != nil {
|
||
|
rightAxisMap[string(graphAxisLogarithmicAttr)] = fmt.Sprintf("%d", *g.LogRightY)
|
||
|
}
|
||
|
|
||
|
if g.MaxRightY != nil {
|
||
|
rightAxisMap[string(graphAxisMaxAttr)] = strconv.FormatFloat(*g.MaxRightY, 'f', -1, 64)
|
||
|
}
|
||
|
|
||
|
if g.MinRightY != nil {
|
||
|
rightAxisMap[string(graphAxisMinAttr)] = strconv.FormatFloat(*g.MinRightY, 'f', -1, 64)
|
||
|
}
|
||
|
|
||
|
d.Set(graphDescriptionAttr, g.Description)
|
||
|
|
||
|
if err := d.Set(graphLeftAttr, leftAxisMap); err != nil {
|
||
|
return errwrap.Wrapf(fmt.Sprintf("Unable to store graph %q attribute: {{err}}", graphLeftAttr), err)
|
||
|
}
|
||
|
|
||
|
d.Set(graphLineStyleAttr, g.LineStyle)
|
||
|
d.Set(graphNameAttr, g.Title)
|
||
|
d.Set(graphNotesAttr, indirect(g.Notes))
|
||
|
|
||
|
if err := d.Set(graphRightAttr, rightAxisMap); err != nil {
|
||
|
return errwrap.Wrapf(fmt.Sprintf("Unable to store graph %q attribute: {{err}}", graphRightAttr), err)
|
||
|
}
|
||
|
|
||
|
if err := d.Set(graphMetricAttr, metrics); err != nil {
|
||
|
return errwrap.Wrapf(fmt.Sprintf("Unable to store graph %q attribute: {{err}}", graphMetricAttr), err)
|
||
|
}
|
||
|
|
||
|
if err := d.Set(graphMetricClusterAttr, metricClusters); err != nil {
|
||
|
return errwrap.Wrapf(fmt.Sprintf("Unable to store graph %q attribute: {{err}}", graphMetricClusterAttr), err)
|
||
|
}
|
||
|
|
||
|
d.Set(graphStyleAttr, g.Style)
|
||
|
|
||
|
if err := d.Set(graphTagsAttr, tagsToState(apiToTags(g.Tags))); err != nil {
|
||
|
return errwrap.Wrapf(fmt.Sprintf("Unable to store graph %q attribute: {{err}}", graphTagsAttr), err)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func graphUpdate(d *schema.ResourceData, meta interface{}) error {
|
||
|
ctxt := meta.(*providerContext)
|
||
|
g := newGraph()
|
||
|
if err := g.ParseConfig(d); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
g.CID = d.Id()
|
||
|
if err := g.Update(ctxt); err != nil {
|
||
|
return errwrap.Wrapf(fmt.Sprintf("unable to update graph %q: {{err}}", d.Id()), err)
|
||
|
}
|
||
|
|
||
|
return graphRead(d, meta)
|
||
|
}
|
||
|
|
||
|
func graphDelete(d *schema.ResourceData, meta interface{}) error {
|
||
|
ctxt := meta.(*providerContext)
|
||
|
|
||
|
cid := d.Id()
|
||
|
if _, err := ctxt.client.DeleteGraphByCID(api.CIDType(&cid)); err != nil {
|
||
|
return errwrap.Wrapf(fmt.Sprintf("unable to delete graph %q: {{err}}", d.Id()), err)
|
||
|
}
|
||
|
|
||
|
d.SetId("")
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
type circonusGraph struct {
|
||
|
api.Graph
|
||
|
}
|
||
|
|
||
|
func newGraph() circonusGraph {
|
||
|
g := circonusGraph{
|
||
|
Graph: *api.NewGraph(),
|
||
|
}
|
||
|
|
||
|
return g
|
||
|
}
|
||
|
|
||
|
func loadGraph(ctxt *providerContext, cid api.CIDType) (circonusGraph, error) {
|
||
|
var g circonusGraph
|
||
|
ng, err := ctxt.client.FetchGraph(cid)
|
||
|
if err != nil {
|
||
|
return circonusGraph{}, err
|
||
|
}
|
||
|
g.Graph = *ng
|
||
|
|
||
|
return g, nil
|
||
|
}
|
||
|
|
||
|
// ParseConfig reads Terraform config data and stores the information into a
|
||
|
// Circonus Graph object. ParseConfig and graphRead() must be kept in sync.
|
||
|
func (g *circonusGraph) ParseConfig(d *schema.ResourceData) error {
|
||
|
g.Datapoints = make([]api.GraphDatapoint, 0, defaultGraphDatapoints)
|
||
|
|
||
|
if v, found := d.GetOk(graphLeftAttr); found {
|
||
|
listRaw := v.(map[string]interface{})
|
||
|
leftAxisMap := make(map[string]interface{}, len(listRaw))
|
||
|
for k, v := range listRaw {
|
||
|
leftAxisMap[k] = v
|
||
|
}
|
||
|
|
||
|
if v, ok := leftAxisMap[string(graphAxisLogarithmicAttr)]; ok {
|
||
|
i64, _ := strconv.ParseInt(v.(string), 10, 64)
|
||
|
i := int(i64)
|
||
|
g.LogLeftY = &i
|
||
|
}
|
||
|
|
||
|
if v, ok := leftAxisMap[string(graphAxisMaxAttr)]; ok && v.(string) != "" {
|
||
|
f, _ := strconv.ParseFloat(v.(string), 64)
|
||
|
g.MaxLeftY = &f
|
||
|
}
|
||
|
|
||
|
if v, ok := leftAxisMap[string(graphAxisMinAttr)]; ok && v.(string) != "" {
|
||
|
f, _ := strconv.ParseFloat(v.(string), 64)
|
||
|
g.MinLeftY = &f
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if v, found := d.GetOk(graphRightAttr); found {
|
||
|
listRaw := v.(map[string]interface{})
|
||
|
rightAxisMap := make(map[string]interface{}, len(listRaw))
|
||
|
for k, v := range listRaw {
|
||
|
rightAxisMap[k] = v
|
||
|
}
|
||
|
|
||
|
if v, ok := rightAxisMap[string(graphAxisLogarithmicAttr)]; ok {
|
||
|
i64, _ := strconv.ParseInt(v.(string), 10, 64)
|
||
|
i := int(i64)
|
||
|
g.LogRightY = &i
|
||
|
}
|
||
|
|
||
|
if v, ok := rightAxisMap[string(graphAxisMaxAttr)]; ok && v.(string) != "" {
|
||
|
f, _ := strconv.ParseFloat(v.(string), 64)
|
||
|
g.MaxRightY = &f
|
||
|
}
|
||
|
|
||
|
if v, ok := rightAxisMap[string(graphAxisMinAttr)]; ok && v.(string) != "" {
|
||
|
f, _ := strconv.ParseFloat(v.(string), 64)
|
||
|
g.MinRightY = &f
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if v, found := d.GetOk(graphDescriptionAttr); found {
|
||
|
g.Description = v.(string)
|
||
|
}
|
||
|
|
||
|
if v, found := d.GetOk(graphLineStyleAttr); found {
|
||
|
switch v.(type) {
|
||
|
case string:
|
||
|
s := v.(string)
|
||
|
g.LineStyle = &s
|
||
|
case *string:
|
||
|
g.LineStyle = v.(*string)
|
||
|
default:
|
||
|
return fmt.Errorf("PROVIDER BUG: unsupported type for %q: %T", graphLineStyleAttr, v)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if v, found := d.GetOk(graphNameAttr); found {
|
||
|
g.Title = v.(string)
|
||
|
}
|
||
|
|
||
|
if v, found := d.GetOk(graphNotesAttr); found {
|
||
|
s := v.(string)
|
||
|
g.Notes = &s
|
||
|
}
|
||
|
|
||
|
if listRaw, found := d.GetOk(graphMetricAttr); found {
|
||
|
metricList := listRaw.([]interface{})
|
||
|
for _, metricListElem := range metricList {
|
||
|
metricAttrs := newInterfaceMap(metricListElem.(map[string]interface{}))
|
||
|
datapoint := api.GraphDatapoint{}
|
||
|
|
||
|
if v, found := metricAttrs[graphMetricActiveAttr]; found {
|
||
|
datapoint.Hidden = !(v.(bool))
|
||
|
}
|
||
|
|
||
|
if v, found := metricAttrs[graphMetricAlphaAttr]; found {
|
||
|
f := v.(float64)
|
||
|
if f != 0 {
|
||
|
datapoint.Alpha = &f
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if v, found := metricAttrs[graphMetricAxisAttr]; found {
|
||
|
switch v.(string) {
|
||
|
case "left", "":
|
||
|
datapoint.Axis = "l"
|
||
|
case "right":
|
||
|
datapoint.Axis = "r"
|
||
|
default:
|
||
|
return fmt.Errorf("PROVIDER BUG: Unsupported axis attribute %q: %q", graphMetricAxisAttr, v.(string))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if v, found := metricAttrs[graphMetricCheckAttr]; found {
|
||
|
re := regexp.MustCompile(config.CheckCIDRegex)
|
||
|
matches := re.FindStringSubmatch(v.(string))
|
||
|
if len(matches) == 3 {
|
||
|
checkID, _ := strconv.ParseUint(matches[2], 10, 64)
|
||
|
datapoint.CheckID = uint(checkID)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if v, found := metricAttrs[graphMetricColorAttr]; found {
|
||
|
s := v.(string)
|
||
|
datapoint.Color = &s
|
||
|
}
|
||
|
|
||
|
if v, found := metricAttrs[graphMetricFormulaAttr]; found {
|
||
|
switch v.(type) {
|
||
|
case string:
|
||
|
s := v.(string)
|
||
|
datapoint.DataFormula = &s
|
||
|
case *string:
|
||
|
datapoint.DataFormula = v.(*string)
|
||
|
default:
|
||
|
return fmt.Errorf("PROVIDER BUG: unsupported type for %q: %T", graphMetricAttr, v)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if v, found := metricAttrs[graphMetricFunctionAttr]; found {
|
||
|
s := v.(string)
|
||
|
if s != "" {
|
||
|
datapoint.Derive = s
|
||
|
} else {
|
||
|
datapoint.Derive = false
|
||
|
}
|
||
|
} else {
|
||
|
datapoint.Derive = false
|
||
|
}
|
||
|
|
||
|
if v, found := metricAttrs[graphMetricFormulaLegendAttr]; found {
|
||
|
switch u := v.(type) {
|
||
|
case string:
|
||
|
datapoint.LegendFormula = &u
|
||
|
case *string:
|
||
|
datapoint.LegendFormula = u
|
||
|
default:
|
||
|
return fmt.Errorf("PROVIDER BUG: unsupported type for %q: %T", graphMetricAttr, v)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if v, found := metricAttrs[graphMetricNameAttr]; found {
|
||
|
s := v.(string)
|
||
|
if s != "" {
|
||
|
datapoint.MetricName = s
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if v, found := metricAttrs[graphMetricMetricTypeAttr]; found {
|
||
|
s := v.(string)
|
||
|
if s != "" {
|
||
|
datapoint.MetricType = s
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if v, found := metricAttrs[graphMetricHumanNameAttr]; found {
|
||
|
s := v.(string)
|
||
|
if s != "" {
|
||
|
datapoint.Name = s
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if v, found := metricAttrs[graphMetricStackAttr]; found {
|
||
|
var stackStr string
|
||
|
switch u := v.(type) {
|
||
|
case string:
|
||
|
stackStr = u
|
||
|
case *string:
|
||
|
if u != nil {
|
||
|
stackStr = *u
|
||
|
}
|
||
|
default:
|
||
|
return fmt.Errorf("PROVIDER BUG: unsupported type for %q: %T", graphMetricStackAttr, v)
|
||
|
}
|
||
|
|
||
|
if stackStr != "" {
|
||
|
u64, _ := strconv.ParseUint(stackStr, 10, 64)
|
||
|
u := uint(u64)
|
||
|
datapoint.Stack = &u
|
||
|
}
|
||
|
}
|
||
|
|
||
|
g.Datapoints = append(g.Datapoints, datapoint)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if listRaw, found := d.GetOk(graphMetricClusterAttr); found {
|
||
|
metricClusterList := listRaw.([]interface{})
|
||
|
|
||
|
for _, metricClusterListRaw := range metricClusterList {
|
||
|
metricClusterAttrs := newInterfaceMap(metricClusterListRaw.(map[string]interface{}))
|
||
|
|
||
|
metricCluster := api.GraphMetricCluster{}
|
||
|
|
||
|
if v, found := metricClusterAttrs[graphMetricClusterActiveAttr]; found {
|
||
|
metricCluster.Hidden = !(v.(bool))
|
||
|
}
|
||
|
|
||
|
if v, found := metricClusterAttrs[graphMetricClusterAggregateAttr]; found {
|
||
|
metricCluster.AggregateFunc = v.(string)
|
||
|
}
|
||
|
|
||
|
if v, found := metricClusterAttrs[graphMetricClusterAxisAttr]; found {
|
||
|
switch v.(string) {
|
||
|
case "left", "":
|
||
|
metricCluster.Axis = "l"
|
||
|
case "right":
|
||
|
metricCluster.Axis = "r"
|
||
|
default:
|
||
|
return fmt.Errorf("PROVIDER BUG: Unsupported axis attribute %q: %q", graphMetricClusterAxisAttr, v.(string))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if v, found := metricClusterAttrs[graphMetricClusterColorAttr]; found {
|
||
|
s := v.(string)
|
||
|
if s != "" {
|
||
|
metricCluster.Color = &s
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if v, found := metricClusterAttrs[graphMetricFormulaAttr]; found {
|
||
|
switch v.(type) {
|
||
|
case string:
|
||
|
s := v.(string)
|
||
|
metricCluster.DataFormula = &s
|
||
|
case *string:
|
||
|
metricCluster.DataFormula = v.(*string)
|
||
|
default:
|
||
|
return fmt.Errorf("PROVIDER BUG: unsupported type for %q: %T", graphMetricFormulaAttr, v)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if v, found := metricClusterAttrs[graphMetricFormulaLegendAttr]; found {
|
||
|
switch v.(type) {
|
||
|
case string:
|
||
|
s := v.(string)
|
||
|
metricCluster.LegendFormula = &s
|
||
|
case *string:
|
||
|
metricCluster.LegendFormula = v.(*string)
|
||
|
default:
|
||
|
return fmt.Errorf("PROVIDER BUG: unsupported type for %q: %T", graphMetricFormulaLegendAttr, v)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if v, found := metricClusterAttrs[graphMetricClusterQueryAttr]; found {
|
||
|
s := v.(string)
|
||
|
if s != "" {
|
||
|
metricCluster.MetricCluster = s
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if v, found := metricClusterAttrs[graphMetricHumanNameAttr]; found {
|
||
|
s := v.(string)
|
||
|
if s != "" {
|
||
|
metricCluster.Name = s
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if v, found := metricClusterAttrs[graphMetricStackAttr]; found {
|
||
|
var stackStr string
|
||
|
switch u := v.(type) {
|
||
|
case string:
|
||
|
stackStr = u
|
||
|
case *string:
|
||
|
if u != nil {
|
||
|
stackStr = *u
|
||
|
}
|
||
|
default:
|
||
|
return fmt.Errorf("PROVIDER BUG: unsupported type for %q: %T", graphMetricStackAttr, v)
|
||
|
}
|
||
|
|
||
|
if stackStr != "" {
|
||
|
u64, _ := strconv.ParseUint(stackStr, 10, 64)
|
||
|
u := uint(u64)
|
||
|
metricCluster.Stack = &u
|
||
|
}
|
||
|
}
|
||
|
|
||
|
g.MetricClusters = append(g.MetricClusters, metricCluster)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if v, found := d.GetOk(graphStyleAttr); found {
|
||
|
switch v.(type) {
|
||
|
case string:
|
||
|
s := v.(string)
|
||
|
g.Style = &s
|
||
|
case *string:
|
||
|
g.Style = v.(*string)
|
||
|
default:
|
||
|
return fmt.Errorf("PROVIDER BUG: unsupported type for %q: %T", graphStyleAttr, v)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if v, found := d.GetOk(graphTagsAttr); found {
|
||
|
g.Tags = derefStringList(flattenSet(v.(*schema.Set)))
|
||
|
}
|
||
|
|
||
|
if err := g.Validate(); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (g *circonusGraph) Create(ctxt *providerContext) error {
|
||
|
ng, err := ctxt.client.CreateGraph(&g.Graph)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
g.CID = ng.CID
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (g *circonusGraph) Update(ctxt *providerContext) error {
|
||
|
_, err := ctxt.client.UpdateGraph(&g.Graph)
|
||
|
if err != nil {
|
||
|
return errwrap.Wrapf(fmt.Sprintf("Unable to update graph %s: {{err}}", g.CID), err)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (g *circonusGraph) Validate() error {
|
||
|
for i, datapoint := range g.Datapoints {
|
||
|
if *g.Style == apiGraphStyleLine && datapoint.Alpha != nil && *datapoint.Alpha != 0 {
|
||
|
return fmt.Errorf("%s can not be set on graphs with style %s", graphMetricAlphaAttr, apiGraphStyleLine)
|
||
|
}
|
||
|
|
||
|
if datapoint.CheckID != 0 && datapoint.MetricName == "" {
|
||
|
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)
|
||
|
}
|
||
|
|
||
|
if datapoint.CheckID == 0 && datapoint.MetricName != "" {
|
||
|
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)
|
||
|
}
|
||
|
|
||
|
if datapoint.CAQL != nil && (datapoint.CheckID != 0 || datapoint.MetricName != "") {
|
||
|
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)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for i, mc := range g.MetricClusters {
|
||
|
if mc.AggregateFunc != "" && (mc.Color == nil || *mc.Color == "") {
|
||
|
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)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|