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  }