github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/librato/resource_librato_space_chart.go (about)

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