From 92c1ca00f046d93df1e563685cf1b80b4806caca Mon Sep 17 00:00:00 2001 From: Henrik Hodne Date: Wed, 30 Sep 2015 15:08:51 -0700 Subject: [PATCH] provider/librato: Add Librato provider --- provider.go | 41 +++ provider_test.go | 39 +++ resource_librato_space.go | 134 ++++++++ resource_librato_space_chart.go | 447 +++++++++++++++++++++++++++ resource_librato_space_chart_test.go | 230 ++++++++++++++ resource_librato_space_test.go | 106 +++++++ 6 files changed, 997 insertions(+) create mode 100644 provider.go create mode 100644 provider_test.go create mode 100644 resource_librato_space.go create mode 100644 resource_librato_space_chart.go create mode 100644 resource_librato_space_chart_test.go create mode 100644 resource_librato_space_test.go diff --git a/provider.go b/provider.go new file mode 100644 index 0000000..0b7894f --- /dev/null +++ b/provider.go @@ -0,0 +1,41 @@ +package librato + +import ( + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/terraform" + "github.com/henrikhodne/go-librato/librato" +) + +// Provider returns a schema.Provider for Librato. +func Provider() terraform.ResourceProvider { + return &schema.Provider{ + Schema: map[string]*schema.Schema{ + "email": &schema.Schema{ + Type: schema.TypeString, + Required: true, + DefaultFunc: schema.EnvDefaultFunc("LIBRATO_EMAIL", nil), + Description: "The email address for the Librato account.", + }, + + "token": &schema.Schema{ + Type: schema.TypeString, + Required: true, + DefaultFunc: schema.EnvDefaultFunc("LIBRATO_TOKEN", nil), + Description: "The auth token for the Librato account.", + }, + }, + + ResourcesMap: map[string]*schema.Resource{ + "librato_space": resourceLibratoSpace(), + "librato_space_chart": resourceLibratoSpaceChart(), + }, + + ConfigureFunc: providerConfigure, + } +} + +func providerConfigure(d *schema.ResourceData) (interface{}, error) { + client := librato.NewClient(d.Get("email").(string), d.Get("token").(string)) + + return client, nil +} diff --git a/provider_test.go b/provider_test.go new file mode 100644 index 0000000..f25f17f --- /dev/null +++ b/provider_test.go @@ -0,0 +1,39 @@ +package librato + +import ( + "os" + "testing" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/terraform" +) + +var testAccProviders map[string]terraform.ResourceProvider +var testAccProvider *schema.Provider + +func init() { + testAccProvider = Provider().(*schema.Provider) + testAccProviders = map[string]terraform.ResourceProvider{ + "librato": testAccProvider, + } +} + +func TestProvider(t *testing.T) { + if err := Provider().(*schema.Provider).InternalValidate(); err != nil { + t.Fatalf("err: %s", err) + } +} + +func TestProvider_impl(t *testing.T) { + var _ terraform.ResourceProvider = Provider() +} + +func testAccPreCheck(t *testing.T) { + if v := os.Getenv("LIBRATO_EMAIL"); v == "" { + t.Fatal("LIBRATO_EMAIL must be set for acceptance tests") + } + + if v := os.Getenv("LIBRATO_TOKEN"); v == "" { + t.Fatal("LIBRATO_TOKEN must be set for acceptance tests") + } +} diff --git a/resource_librato_space.go b/resource_librato_space.go new file mode 100644 index 0000000..e0c1242 --- /dev/null +++ b/resource_librato_space.go @@ -0,0 +1,134 @@ +package librato + +import ( + "fmt" + "log" + "strconv" + "time" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "github.com/henrikhodne/go-librato/librato" +) + +func resourceLibratoSpace() *schema.Resource { + return &schema.Resource{ + Create: resourceLibratoSpaceCreate, + Read: resourceLibratoSpaceRead, + Update: resourceLibratoSpaceUpdate, + Delete: resourceLibratoSpaceDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + "id": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + }, + }, + } +} + +func resourceLibratoSpaceCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*librato.Client) + + name := d.Get("name").(string) + + space, _, err := client.Spaces.Create(&librato.Space{Name: librato.String(name)}) + if err != nil { + return fmt.Errorf("Error creating Librato space %s: %s", name, err) + } + + resource.Retry(1*time.Minute, func() *resource.RetryError { + _, _, err := client.Spaces.Get(*space.ID) + if err != nil { + if errResp, ok := err.(*librato.ErrorResponse); ok && errResp.Response.StatusCode == 404 { + return resource.RetryableError(err) + } + return resource.NonRetryableError(err) + } + return nil + }) + + return resourceLibratoSpaceReadResult(d, space) +} + +func resourceLibratoSpaceRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*librato.Client) + + id, err := strconv.ParseUint(d.Id(), 10, 0) + if err != nil { + return err + } + + space, _, err := client.Spaces.Get(uint(id)) + if err != nil { + if errResp, ok := err.(*librato.ErrorResponse); ok && errResp.Response.StatusCode == 404 { + d.SetId("") + return nil + } + return fmt.Errorf("Error reading Librato Space %s: %s", d.Id(), err) + } + + return resourceLibratoSpaceReadResult(d, space) +} + +func resourceLibratoSpaceReadResult(d *schema.ResourceData, space *librato.Space) error { + d.SetId(strconv.FormatUint(uint64(*space.ID), 10)) + if err := d.Set("id", *space.ID); err != nil { + return err + } + if err := d.Set("name", *space.Name); err != nil { + return err + } + return nil +} + +func resourceLibratoSpaceUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*librato.Client) + id, err := strconv.ParseUint(d.Id(), 10, 0) + if err != nil { + return err + } + + if d.HasChange("name") { + newName := d.Get("name").(string) + log.Printf("[INFO] Modifying name space attribute for %d: %#v", id, newName) + if _, err = client.Spaces.Edit(uint(id), &librato.Space{Name: &newName}); err != nil { + return err + } + } + + return resourceLibratoSpaceRead(d, meta) +} + +func resourceLibratoSpaceDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*librato.Client) + id, err := strconv.ParseUint(d.Id(), 10, 0) + if err != nil { + return err + } + + log.Printf("[INFO] Deleting Space: %d", id) + _, err = client.Spaces.Delete(uint(id)) + if err != nil { + return fmt.Errorf("Error deleting space: %s", err) + } + + resource.Retry(1*time.Minute, func() *resource.RetryError { + _, _, err := client.Spaces.Get(uint(id)) + if err != nil { + if errResp, ok := err.(*librato.ErrorResponse); ok && errResp.Response.StatusCode == 404 { + return nil + } + return resource.NonRetryableError(err) + } + return resource.RetryableError(fmt.Errorf("space still exists")) + }) + + d.SetId("") + return nil +} diff --git a/resource_librato_space_chart.go b/resource_librato_space_chart.go new file mode 100644 index 0000000..dea4999 --- /dev/null +++ b/resource_librato_space_chart.go @@ -0,0 +1,447 @@ +package librato + +import ( + "bytes" + "fmt" + "log" + "math" + "strconv" + "time" + + "github.com/hashicorp/terraform/helper/hashcode" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "github.com/henrikhodne/go-librato/librato" +) + +func resourceLibratoSpaceChart() *schema.Resource { + return &schema.Resource{ + Create: resourceLibratoSpaceChartCreate, + Read: resourceLibratoSpaceChartRead, + Update: resourceLibratoSpaceChartUpdate, + Delete: resourceLibratoSpaceChartDelete, + + Schema: map[string]*schema.Schema{ + "space_id": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "type": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "min": &schema.Schema{ + Type: schema.TypeFloat, + Default: math.NaN(), + Optional: true, + }, + "max": &schema.Schema{ + Type: schema.TypeFloat, + Default: math.NaN(), + Optional: true, + }, + "label": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "related_space": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + }, + "stream": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "metric": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"stream.composite"}, + }, + "source": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"stream.composite"}, + }, + "group_function": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"stream.composite"}, + }, + "composite": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"stream.metric", "stream.source", "stream.group_function"}, + }, + "summary_function": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "color": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "units_short": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "units_long": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "min": &schema.Schema{ + Type: schema.TypeFloat, + Default: math.NaN(), + Optional: true, + }, + "max": &schema.Schema{ + Type: schema.TypeFloat, + Default: math.NaN(), + Optional: true, + }, + "transform_function": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "period": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + }, + }, + }, + Set: resourceLibratoSpaceChartHash, + }, + }, + } +} + +func resourceLibratoSpaceChartHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%s-", m["metric"].(string))) + buf.WriteString(fmt.Sprintf("%s-", m["source"].(string))) + buf.WriteString(fmt.Sprintf("%s-", m["composite"].(string))) + + return hashcode.String(buf.String()) +} + +func resourceLibratoSpaceChartCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*librato.Client) + + spaceID := uint(d.Get("space_id").(int)) + + spaceChart := new(librato.SpaceChart) + if v, ok := d.GetOk("name"); ok { + spaceChart.Name = librato.String(v.(string)) + } + if v, ok := d.GetOk("type"); ok { + spaceChart.Type = librato.String(v.(string)) + } + if v, ok := d.GetOk("min"); ok { + if math.IsNaN(v.(float64)) { + spaceChart.Min = nil + } else { + spaceChart.Min = librato.Float(v.(float64)) + } + } + if v, ok := d.GetOk("max"); ok { + if math.IsNaN(v.(float64)) { + spaceChart.Max = nil + } else { + spaceChart.Max = librato.Float(v.(float64)) + } + } + if v, ok := d.GetOk("label"); ok { + spaceChart.Label = librato.String(v.(string)) + } + if v, ok := d.GetOk("related_space"); ok { + spaceChart.RelatedSpace = librato.Uint(uint(v.(int))) + } + if v, ok := d.GetOk("stream"); ok { + vs := v.(*schema.Set) + streams := make([]librato.SpaceChartStream, vs.Len()) + for i, streamDataM := range vs.List() { + streamData := streamDataM.(map[string]interface{}) + var stream librato.SpaceChartStream + if v, ok := streamData["metric"].(string); ok && v != "" { + stream.Metric = librato.String(v) + } + if v, ok := streamData["source"].(string); ok && v != "" { + stream.Source = librato.String(v) + } + if v, ok := streamData["composite"].(string); ok && v != "" { + stream.Composite = librato.String(v) + } + if v, ok := streamData["group_function"].(string); ok && v != "" { + stream.GroupFunction = librato.String(v) + } + if v, ok := streamData["summary_function"].(string); ok && v != "" { + stream.SummaryFunction = librato.String(v) + } + if v, ok := streamData["transform_function"].(string); ok && v != "" { + stream.TransformFunction = librato.String(v) + } + if v, ok := streamData["color"].(string); ok && v != "" { + stream.Color = librato.String(v) + } + if v, ok := streamData["units_short"].(string); ok && v != "" { + stream.UnitsShort = librato.String(v) + } + if v, ok := streamData["units_longs"].(string); ok && v != "" { + stream.UnitsLong = librato.String(v) + } + if v, ok := streamData["min"].(float64); ok && !math.IsNaN(v) { + stream.Min = librato.Float(v) + } + if v, ok := streamData["max"].(float64); ok && !math.IsNaN(v) { + stream.Max = librato.Float(v) + } + streams[i] = stream + } + spaceChart.Streams = streams + } + + spaceChartResult, _, err := client.Spaces.CreateChart(spaceID, spaceChart) + if err != nil { + return fmt.Errorf("Error creating Librato space chart %s: %s", *spaceChart.Name, err) + } + + resource.Retry(1*time.Minute, func() *resource.RetryError { + _, _, err := client.Spaces.GetChart(spaceID, *spaceChartResult.ID) + if err != nil { + if errResp, ok := err.(*librato.ErrorResponse); ok && errResp.Response.StatusCode == 404 { + return resource.RetryableError(err) + } + return resource.NonRetryableError(err) + } + return nil + }) + + return resourceLibratoSpaceChartReadResult(d, spaceChartResult) +} + +func resourceLibratoSpaceChartRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*librato.Client) + + spaceID := uint(d.Get("space_id").(int)) + + id, err := strconv.ParseUint(d.Id(), 10, 0) + if err != nil { + return err + } + + chart, _, err := client.Spaces.GetChart(spaceID, uint(id)) + if err != nil { + if errResp, ok := err.(*librato.ErrorResponse); ok && errResp.Response.StatusCode == 404 { + d.SetId("") + return nil + } + return fmt.Errorf("Error reading Librato Space chart %s: %s", d.Id(), err) + } + + return resourceLibratoSpaceChartReadResult(d, chart) +} + +func resourceLibratoSpaceChartReadResult(d *schema.ResourceData, chart *librato.SpaceChart) error { + d.SetId(strconv.FormatUint(uint64(*chart.ID), 10)) + if chart.Name != nil { + if err := d.Set("name", *chart.Name); err != nil { + return err + } + } + if chart.Type != nil { + if err := d.Set("type", *chart.Type); err != nil { + return err + } + } + if chart.Min != nil { + if err := d.Set("min", *chart.Min); err != nil { + return err + } + } + if chart.Max != nil { + if err := d.Set("max", *chart.Max); err != nil { + return err + } + } + if chart.Label != nil { + if err := d.Set("label", *chart.Label); err != nil { + return err + } + } + if chart.RelatedSpace != nil { + if err := d.Set("related_space", *chart.RelatedSpace); err != nil { + return err + } + } + + streams := resourceLibratoSpaceChartStreamsGather(d, chart.Streams) + if err := d.Set("stream", streams); err != nil { + return err + } + + return nil +} + +func resourceLibratoSpaceChartStreamsGather(d *schema.ResourceData, streams []librato.SpaceChartStream) []map[string]interface{} { + retStreams := make([]map[string]interface{}, 0, len(streams)) + for _, s := range streams { + stream := make(map[string]interface{}) + if s.Metric != nil { + stream["metric"] = *s.Metric + } + if s.Source != nil { + stream["source"] = *s.Source + } + if s.Composite != nil { + stream["composite"] = *s.Composite + } + if s.GroupFunction != nil { + stream["group_function"] = *s.GroupFunction + } + if s.SummaryFunction != nil { + stream["summary_function"] = *s.SummaryFunction + } + if s.TransformFunction != nil { + stream["transform_function"] = *s.TransformFunction + } + if s.Color != nil { + stream["color"] = *s.Color + } + if s.UnitsShort != nil { + stream["units_short"] = *s.UnitsShort + } + if s.UnitsLong != nil { + stream["units_long"] = *s.UnitsLong + } + retStreams = append(retStreams, stream) + } + + return retStreams +} + +func resourceLibratoSpaceChartUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*librato.Client) + + spaceID := uint(d.Get("space_id").(int)) + chartID, err := strconv.ParseUint(d.Id(), 10, 0) + if err != nil { + return err + } + + spaceChart := new(librato.SpaceChart) + if d.HasChange("name") { + spaceChart.Name = librato.String(d.Get("name").(string)) + } + if d.HasChange("min") { + if math.IsNaN(d.Get("min").(float64)) { + spaceChart.Min = nil + } else { + spaceChart.Min = librato.Float(d.Get("min").(float64)) + } + } + if d.HasChange("max") { + if math.IsNaN(d.Get("max").(float64)) { + spaceChart.Max = nil + } else { + spaceChart.Max = librato.Float(d.Get("max").(float64)) + } + } + if d.HasChange("label") { + spaceChart.Label = librato.String(d.Get("label").(string)) + } + if d.HasChange("related_space") { + spaceChart.RelatedSpace = librato.Uint(d.Get("related_space").(uint)) + } + if d.HasChange("stream") { + vs := d.Get("stream").(*schema.Set) + streams := make([]librato.SpaceChartStream, vs.Len()) + for i, streamDataM := range vs.List() { + streamData := streamDataM.(map[string]interface{}) + var stream librato.SpaceChartStream + if v, ok := streamData["metric"].(string); ok && v != "" { + stream.Metric = librato.String(v) + } + if v, ok := streamData["source"].(string); ok && v != "" { + stream.Source = librato.String(v) + } + if v, ok := streamData["composite"].(string); ok && v != "" { + stream.Composite = librato.String(v) + } + if v, ok := streamData["group_function"].(string); ok && v != "" { + stream.GroupFunction = librato.String(v) + } + if v, ok := streamData["summary_function"].(string); ok && v != "" { + stream.SummaryFunction = librato.String(v) + } + if v, ok := streamData["transform_function"].(string); ok && v != "" { + stream.TransformFunction = librato.String(v) + } + if v, ok := streamData["color"].(string); ok && v != "" { + stream.Color = librato.String(v) + } + if v, ok := streamData["units_short"].(string); ok && v != "" { + stream.UnitsShort = librato.String(v) + } + if v, ok := streamData["units_longs"].(string); ok && v != "" { + stream.UnitsLong = librato.String(v) + } + if v, ok := streamData["min"].(float64); ok && !math.IsNaN(v) { + stream.Min = librato.Float(v) + } + if v, ok := streamData["max"].(float64); ok && !math.IsNaN(v) { + stream.Max = librato.Float(v) + } + streams[i] = stream + } + spaceChart.Streams = streams + } + + _, err = client.Spaces.EditChart(spaceID, uint(chartID), spaceChart) + if err != nil { + return fmt.Errorf("Error updating Librato space chart %s: %s", *spaceChart.Name, err) + } + + return resourceLibratoSpaceChartRead(d, meta) +} + +func resourceLibratoSpaceChartDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*librato.Client) + + spaceID := uint(d.Get("space_id").(int)) + + id, err := strconv.ParseUint(d.Id(), 10, 0) + if err != nil { + return err + } + + log.Printf("[INFO] Deleting Chart: %d/%d", spaceID, uint(id)) + _, err = client.Spaces.DeleteChart(spaceID, uint(id)) + if err != nil { + return fmt.Errorf("Error deleting space: %s", err) + } + + resource.Retry(1*time.Minute, func() *resource.RetryError { + _, _, err := client.Spaces.GetChart(spaceID, uint(id)) + if err != nil { + if errResp, ok := err.(*librato.ErrorResponse); ok && errResp.Response.StatusCode == 404 { + return nil + } + return resource.NonRetryableError(err) + } + return resource.RetryableError(fmt.Errorf("space chart still exists")) + }) + + d.SetId("") + return nil +} diff --git a/resource_librato_space_chart_test.go b/resource_librato_space_chart_test.go new file mode 100644 index 0000000..8fe01b7 --- /dev/null +++ b/resource_librato_space_chart_test.go @@ -0,0 +1,230 @@ +package librato + +import ( + "fmt" + "strconv" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/henrikhodne/go-librato/librato" +) + +func TestAccLibratoSpaceChart_Basic(t *testing.T) { + var spaceChart librato.SpaceChart + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckLibratoSpaceChartDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckLibratoSpaceChartConfig_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckLibratoSpaceChartExists("librato_space_chart.foobar", &spaceChart), + testAccCheckLibratoSpaceChartName(&spaceChart, "Foo Bar"), + resource.TestCheckResourceAttr( + "librato_space_chart.foobar", "name", "Foo Bar"), + ), + }, + }, + }) +} + +func TestAccLibratoSpaceChart_Full(t *testing.T) { + var spaceChart librato.SpaceChart + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckLibratoSpaceChartDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckLibratoSpaceChartConfig_full, + Check: resource.ComposeTestCheckFunc( + testAccCheckLibratoSpaceChartExists("librato_space_chart.foobar", &spaceChart), + testAccCheckLibratoSpaceChartName(&spaceChart, "Foo Bar"), + resource.TestCheckResourceAttr( + "librato_space_chart.foobar", "name", "Foo Bar"), + ), + }, + }, + }) +} + +func TestAccLibratoSpaceChart_Updated(t *testing.T) { + var spaceChart librato.SpaceChart + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckLibratoSpaceChartDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckLibratoSpaceChartConfig_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckLibratoSpaceChartExists("librato_space_chart.foobar", &spaceChart), + testAccCheckLibratoSpaceChartName(&spaceChart, "Foo Bar"), + resource.TestCheckResourceAttr( + "librato_space_chart.foobar", "name", "Foo Bar"), + ), + }, + resource.TestStep{ + Config: testAccCheckLibratoSpaceChartConfig_new_value, + Check: resource.ComposeTestCheckFunc( + testAccCheckLibratoSpaceChartExists("librato_space_chart.foobar", &spaceChart), + testAccCheckLibratoSpaceChartName(&spaceChart, "Bar Baz"), + resource.TestCheckResourceAttr( + "librato_space_chart.foobar", "name", "Bar Baz"), + ), + }, + }, + }) +} + +func testAccCheckLibratoSpaceChartDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*librato.Client) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "librato_space_chart" { + continue + } + + id, err := strconv.ParseUint(rs.Primary.ID, 10, 0) + if err != nil { + return fmt.Errorf("ID not a number") + } + + spaceID, err := strconv.ParseUint(rs.Primary.Attributes["space_id"], 10, 0) + if err != nil { + return fmt.Errorf("Space ID not a number") + } + + _, _, err = client.Spaces.GetChart(uint(spaceID), uint(id)) + + if err == nil { + return fmt.Errorf("Space Chart still exists") + } + } + + return nil +} + +func testAccCheckLibratoSpaceChartName(spaceChart *librato.SpaceChart, name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + + if spaceChart.Name == nil || *spaceChart.Name != name { + return fmt.Errorf("Bad name: %s", *spaceChart.Name) + } + + return nil + } +} + +func testAccCheckLibratoSpaceChartExists(n string, spaceChart *librato.SpaceChart) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Space Chart ID is set") + } + + client := testAccProvider.Meta().(*librato.Client) + + id, err := strconv.ParseUint(rs.Primary.ID, 10, 0) + if err != nil { + return fmt.Errorf("ID not a number") + } + + spaceID, err := strconv.ParseUint(rs.Primary.Attributes["space_id"], 10, 0) + if err != nil { + return fmt.Errorf("Space ID not a number") + } + + foundSpaceChart, _, err := client.Spaces.GetChart(uint(spaceID), uint(id)) + + if err != nil { + return err + } + + if foundSpaceChart.ID == nil || *foundSpaceChart.ID != uint(id) { + return fmt.Errorf("Space not found") + } + + *spaceChart = *foundSpaceChart + + return nil + } +} + +const testAccCheckLibratoSpaceChartConfig_basic = ` +resource "librato_space" "foobar" { + name = "Foo Bar" +} + +resource "librato_space_chart" "foobar" { + space_id = "${librato_space.foobar.id}" + name = "Foo Bar" + type = "line" +}` + +const testAccCheckLibratoSpaceChartConfig_new_value = ` +resource "librato_space" "foobar" { + name = "Foo Bar" +} + +resource "librato_space_chart" "foobar" { + space_id = "${librato_space.foobar.id}" + name = "Bar Baz" + type = "line" +}` + +const testAccCheckLibratoSpaceChartConfig_full = ` +resource "librato_space" "foobar" { + name = "Foo Bar" +} + +resource "librato_space" "barbaz" { + name = "Bar Baz" +} + +resource "librato_space_chart" "foobar" { + space_id = "${librato_space.foobar.id}" + name = "Foo Bar" + type = "line" + min = 0 + max = 100 + label = "Percent" + related_space = "${librato_space.barbaz.id}" + + # Minimal metric stream + stream { + metric = "cpu" + source = "*" + } + + # Minimal composite stream + stream { + composite = "s(\"cpu\", \"*\")" + } + + # Full metric stream + stream { + metric = "cpu" + source = "*" + group_function = "average" + summary_function = "max" + name = "CPU usage" + color = "#990000" + units_short = "%" + units_long = "percent" + min = 0 + max = 100 + transform_function = "x * 100" + period = 60 + } +}` diff --git a/resource_librato_space_test.go b/resource_librato_space_test.go new file mode 100644 index 0000000..ce055cc --- /dev/null +++ b/resource_librato_space_test.go @@ -0,0 +1,106 @@ +package librato + +import ( + "fmt" + "strconv" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/henrikhodne/go-librato/librato" +) + +func TestAccLibratoSpace_Basic(t *testing.T) { + var space librato.Space + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckLibratoSpaceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckLibratoSpaceConfig_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckLibratoSpaceExists("librato_space.foobar", &space), + testAccCheckLibratoSpaceAttributes(&space), + resource.TestCheckResourceAttr( + "librato_space.foobar", "name", "Foo Bar"), + ), + }, + }, + }) +} + +func testAccCheckLibratoSpaceDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*librato.Client) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "librato_space" { + continue + } + + id, err := strconv.ParseUint(rs.Primary.ID, 10, 0) + if err != nil { + return fmt.Errorf("ID not a number") + } + + _, _, err = client.Spaces.Get(uint(id)) + + if err == nil { + return fmt.Errorf("Space still exists") + } + } + + return nil +} + +func testAccCheckLibratoSpaceAttributes(space *librato.Space) resource.TestCheckFunc { + return func(s *terraform.State) error { + + if space.Name == nil || *space.Name != "Foo Bar" { + return fmt.Errorf("Bad name: %s", *space.Name) + } + + return nil + } +} + +func testAccCheckLibratoSpaceExists(n string, space *librato.Space) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Space ID is set") + } + + client := testAccProvider.Meta().(*librato.Client) + + id, err := strconv.ParseUint(rs.Primary.ID, 10, 0) + if err != nil { + return fmt.Errorf("ID not a number") + } + + foundSpace, _, err := client.Spaces.Get(uint(id)) + + if err != nil { + return err + } + + if foundSpace.ID == nil || *foundSpace.ID != uint(id) { + return fmt.Errorf("Space not found") + } + + *space = *foundSpace + + return nil + } +} + +const testAccCheckLibratoSpaceConfig_basic = ` +resource "librato_space" "foobar" { + name = "Foo Bar" +}`