diff --git a/builtin/providers/librato/resource_librato_alert.go b/builtin/providers/librato/resource_librato_alert.go index ac61f4544..88ca52dc5 100644 --- a/builtin/providers/librato/resource_librato_alert.go +++ b/builtin/providers/librato/resource_librato_alert.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "math" + "reflect" "strconv" "time" @@ -27,10 +28,6 @@ func resourceLibratoAlert() *schema.Resource { Required: true, ForceNew: false, }, - "id": &schema.Schema{ - Type: schema.TypeInt, - Computed: true, - }, "description": &schema.Schema{ Type: schema.TypeString, Optional: true, @@ -214,6 +211,7 @@ func resourceLibratoAlertCreate(d *schema.ResourceData, meta interface{}) error if err != nil { return fmt.Errorf("Error creating Librato alert %s: %s", *alert.Name, err) } + log.Printf("[INFO] Created Librato alert: %s", *alertResult) resource.Retry(1*time.Minute, func() *resource.RetryError { _, _, err := client.Alerts.Get(*alertResult.ID) @@ -226,7 +224,9 @@ func resourceLibratoAlertCreate(d *schema.ResourceData, meta interface{}) error return nil }) - return resourceLibratoAlertReadResult(d, alertResult) + d.SetId(strconv.FormatUint(uint64(*alertResult.ID), 10)) + + return resourceLibratoAlertRead(d, meta) } func resourceLibratoAlertRead(d *schema.ResourceData, meta interface{}) error { @@ -236,6 +236,7 @@ func resourceLibratoAlertRead(d *schema.ResourceData, meta interface{}) error { return err } + log.Printf("[INFO] Reading Librato Alert: %d", id) alert, _, err := client.Alerts.Get(uint(id)) if err != nil { if errResp, ok := err.(*librato.ErrorResponse); ok && errResp.Response.StatusCode == 404 { @@ -244,23 +245,22 @@ func resourceLibratoAlertRead(d *schema.ResourceData, meta interface{}) error { } return fmt.Errorf("Error reading Librato Alert %s: %s", d.Id(), err) } + log.Printf("[INFO] Received Librato Alert: %s", *alert) return resourceLibratoAlertReadResult(d, alert) } func resourceLibratoAlertReadResult(d *schema.ResourceData, alert *librato.Alert) error { - d.SetId(strconv.FormatUint(uint64(*alert.ID), 10)) - d.Set("id", *alert.ID) d.Set("name", *alert.Name) d.Set("description", *alert.Description) d.Set("active", *alert.Active) d.Set("rearm_seconds", *alert.RearmSeconds) services := resourceLibratoAlertServicesGather(d, alert.Services.([]interface{})) - d.Set("services", services) + d.Set("services", schema.NewSet(schema.HashString, services)) conditions := resourceLibratoAlertConditionsGather(d, alert.Conditions) - d.Set("condition", conditions) + d.Set("condition", schema.NewSet(resourceLibratoAlertConditionsHash, conditions)) attributes := resourceLibratoAlertAttributesGather(d, alert.Attributes) d.Set("attributes", attributes) @@ -268,8 +268,8 @@ func resourceLibratoAlertReadResult(d *schema.ResourceData, alert *librato.Alert return nil } -func resourceLibratoAlertServicesGather(d *schema.ResourceData, services []interface{}) []string { - retServices := make([]string, 0, len(services)) +func resourceLibratoAlertServicesGather(d *schema.ResourceData, services []interface{}) []interface{} { + retServices := make([]interface{}, 0, len(services)) for _, s := range services { serviceData := s.(map[string]interface{}) @@ -280,8 +280,8 @@ func resourceLibratoAlertServicesGather(d *schema.ResourceData, services []inter return retServices } -func resourceLibratoAlertConditionsGather(d *schema.ResourceData, conditions []librato.AlertCondition) []map[string]interface{} { - retConditions := make([]map[string]interface{}, 0, len(conditions)) +func resourceLibratoAlertConditionsGather(d *schema.ResourceData, conditions []librato.AlertCondition) []interface{} { + retConditions := make([]interface{}, 0, len(conditions)) for _, c := range conditions { condition := make(map[string]interface{}) if c.Type != nil { @@ -300,7 +300,7 @@ func resourceLibratoAlertConditionsGather(d *schema.ResourceData, conditions []l condition["detect_reset"] = *c.MetricName } if c.Duration != nil { - condition["duration"] = *c.Duration + condition["duration"] = int(*c.Duration) } if c.SummaryFunction != nil { condition["summary_function"] = *c.SummaryFunction @@ -334,16 +334,25 @@ func resourceLibratoAlertUpdate(d *schema.ResourceData, meta interface{}) error return err } + // Just to have whole object for comparison before/after update + fullAlert, _, err := client.Alerts.Get(uint(alertID)) + if err != nil { + return err + } + alert := new(librato.Alert) alert.Name = librato.String(d.Get("name").(string)) if d.HasChange("description") { alert.Description = librato.String(d.Get("description").(string)) + fullAlert.Description = alert.Description } if d.HasChange("active") { alert.Active = librato.Bool(d.Get("active").(bool)) + fullAlert.Active = alert.Active } if d.HasChange("rearm_seconds") { alert.RearmSeconds = librato.Uint(uint(d.Get("rearm_seconds").(int))) + fullAlert.RearmSeconds = alert.RearmSeconds } if d.HasChange("services") { vs := d.Get("services").(*schema.Set) @@ -352,6 +361,7 @@ func resourceLibratoAlertUpdate(d *schema.ResourceData, meta interface{}) error services[i] = librato.String(serviceData.(string)) } alert.Services = services + fullAlert.RearmSeconds = alert.RearmSeconds } vs := d.Get("condition").(*schema.Set) @@ -382,6 +392,7 @@ func resourceLibratoAlertUpdate(d *schema.ResourceData, meta interface{}) error } conditions[i] = condition alert.Conditions = conditions + fullAlert.Conditions = conditions } if d.HasChange("attributes") { attributeData := d.Get("attributes").([]interface{}) @@ -397,14 +408,42 @@ func resourceLibratoAlertUpdate(d *schema.ResourceData, meta interface{}) error attributes.RunbookURL = librato.String(v) } alert.Attributes = attributes + fullAlert.Attributes = attributes } } + log.Printf("[INFO] Updating Librato alert: %s", alert) _, err = client.Alerts.Edit(uint(alertID), alert) if err != nil { return fmt.Errorf("Error updating Librato alert: %s", err) } + log.Printf("[INFO] Updated Librato alert %d", alertID) + + // Wait for propagation since Librato updates are eventually consistent + wait := resource.StateChangeConf{ + Pending: []string{fmt.Sprintf("%t", false)}, + Target: []string{fmt.Sprintf("%t", true)}, + Timeout: 5 * time.Minute, + MinTimeout: 2 * time.Second, + ContinuousTargetOccurence: 5, + Refresh: func() (interface{}, string, error) { + log.Printf("[DEBUG] Checking if Librato Alert %d was updated yet", alertID) + changedAlert, _, err := client.Alerts.Get(uint(alertID)) + if err != nil { + return changedAlert, "", err + } + isEqual := reflect.DeepEqual(*fullAlert, *changedAlert) + log.Printf("[DEBUG] Updated Librato Alert %d match: %t", alertID, isEqual) + return changedAlert, fmt.Sprintf("%t", isEqual), nil + }, + } + + _, err = wait.WaitForState() + if err != nil { + return fmt.Errorf("Failed updating Librato Alert %d: %s", alertID, err) + } + return resourceLibratoAlertRead(d, meta) } diff --git a/builtin/providers/librato/resource_librato_service.go b/builtin/providers/librato/resource_librato_service.go index 786d8c7d8..e289fee0d 100644 --- a/builtin/providers/librato/resource_librato_service.go +++ b/builtin/providers/librato/resource_librato_service.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "log" + "reflect" "strconv" "time" @@ -124,6 +125,7 @@ func resourceLibratoServiceRead(d *schema.ResourceData, meta interface{}) error return err } + log.Printf("[INFO] Reading Librato Service: %d", id) service, _, err := client.Services.Get(uint(id)) if err != nil { if errResp, ok := err.(*librato.ErrorResponse); ok && errResp.Response.StatusCode == 404 { @@ -132,6 +134,7 @@ func resourceLibratoServiceRead(d *schema.ResourceData, meta interface{}) error } return fmt.Errorf("Error reading Librato Service %s: %s", d.Id(), err) } + log.Printf("[INFO] Received Librato Service: %s", service) return resourceLibratoServiceReadResult(d, service) } @@ -155,12 +158,20 @@ func resourceLibratoServiceUpdate(d *schema.ResourceData, meta interface{}) erro return err } + // Just to have whole object for comparison before/after update + fullService, _, err := client.Services.Get(uint(serviceID)) + if err != nil { + return err + } + service := new(librato.Service) if d.HasChange("type") { service.Type = librato.String(d.Get("type").(string)) + fullService.Type = service.Type } if d.HasChange("title") { service.Title = librato.String(d.Get("title").(string)) + fullService.Title = service.Title } if d.HasChange("settings") { res, err := resourceLibratoServicesExpandSettings(normalizeJson(d.Get("settings").(string))) @@ -168,12 +179,39 @@ func resourceLibratoServiceUpdate(d *schema.ResourceData, meta interface{}) erro return fmt.Errorf("Error expanding Librato service settings: %s", err) } service.Settings = res + fullService.Settings = res } + log.Printf("[INFO] Updating Librato Service %d: %s", serviceID, service) _, err = client.Services.Edit(uint(serviceID), service) if err != nil { return fmt.Errorf("Error updating Librato service: %s", err) } + log.Printf("[INFO] Updated Librato Service %d", serviceID) + + // Wait for propagation since Librato updates are eventually consistent + wait := resource.StateChangeConf{ + Pending: []string{fmt.Sprintf("%t", false)}, + Target: []string{fmt.Sprintf("%t", true)}, + Timeout: 5 * time.Minute, + MinTimeout: 2 * time.Second, + ContinuousTargetOccurence: 5, + Refresh: func() (interface{}, string, error) { + log.Printf("[DEBUG] Checking if Librato Service %d was updated yet", serviceID) + changedService, _, err := client.Services.Get(uint(serviceID)) + if err != nil { + return changedService, "", err + } + isEqual := reflect.DeepEqual(*fullService, *changedService) + log.Printf("[DEBUG] Updated Librato Service %d match: %t", serviceID, isEqual) + return changedService, fmt.Sprintf("%t", isEqual), nil + }, + } + + _, err = wait.WaitForState() + if err != nil { + return fmt.Errorf("Failed updating Librato Service %d: %s", serviceID, err) + } return resourceLibratoServiceRead(d, meta) } diff --git a/builtin/providers/librato/resource_librato_space_chart.go b/builtin/providers/librato/resource_librato_space_chart.go index dea499974..a010efc9f 100644 --- a/builtin/providers/librato/resource_librato_space_chart.go +++ b/builtin/providers/librato/resource_librato_space_chart.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "math" + "reflect" "strconv" "time" @@ -339,9 +340,16 @@ func resourceLibratoSpaceChartUpdate(d *schema.ResourceData, meta interface{}) e return err } + // Just to have whole object for comparison before/after update + fullChart, _, err := client.Spaces.GetChart(spaceID, uint(chartID)) + if err != nil { + return err + } + spaceChart := new(librato.SpaceChart) if d.HasChange("name") { spaceChart.Name = librato.String(d.Get("name").(string)) + fullChart.Name = spaceChart.Name } if d.HasChange("min") { if math.IsNaN(d.Get("min").(float64)) { @@ -349,6 +357,7 @@ func resourceLibratoSpaceChartUpdate(d *schema.ResourceData, meta interface{}) e } else { spaceChart.Min = librato.Float(d.Get("min").(float64)) } + fullChart.Min = spaceChart.Min } if d.HasChange("max") { if math.IsNaN(d.Get("max").(float64)) { @@ -356,12 +365,15 @@ func resourceLibratoSpaceChartUpdate(d *schema.ResourceData, meta interface{}) e } else { spaceChart.Max = librato.Float(d.Get("max").(float64)) } + fullChart.Max = spaceChart.Max } if d.HasChange("label") { spaceChart.Label = librato.String(d.Get("label").(string)) + fullChart.Label = spaceChart.Label } if d.HasChange("related_space") { spaceChart.RelatedSpace = librato.Uint(d.Get("related_space").(uint)) + fullChart.RelatedSpace = spaceChart.RelatedSpace } if d.HasChange("stream") { vs := d.Get("stream").(*schema.Set) @@ -405,6 +417,7 @@ func resourceLibratoSpaceChartUpdate(d *schema.ResourceData, meta interface{}) e streams[i] = stream } spaceChart.Streams = streams + fullChart.Streams = streams } _, err = client.Spaces.EditChart(spaceID, uint(chartID), spaceChart) @@ -412,6 +425,30 @@ func resourceLibratoSpaceChartUpdate(d *schema.ResourceData, meta interface{}) e return fmt.Errorf("Error updating Librato space chart %s: %s", *spaceChart.Name, err) } + // Wait for propagation since Librato updates are eventually consistent + wait := resource.StateChangeConf{ + Pending: []string{fmt.Sprintf("%t", false)}, + Target: []string{fmt.Sprintf("%t", true)}, + Timeout: 5 * time.Minute, + MinTimeout: 2 * time.Second, + ContinuousTargetOccurence: 5, + Refresh: func() (interface{}, string, error) { + log.Printf("[DEBUG] Checking if Librato Space Chart %d was updated yet", chartID) + changedChart, _, err := client.Spaces.GetChart(spaceID, uint(chartID)) + if err != nil { + return changedChart, "", err + } + isEqual := reflect.DeepEqual(*fullChart, *changedChart) + log.Printf("[DEBUG] Updated Librato Space Chart %d match: %t", chartID, isEqual) + return changedChart, fmt.Sprintf("%t", isEqual), nil + }, + } + + _, err = wait.WaitForState() + if err != nil { + return fmt.Errorf("Failed updating Librato Space Chart %d: %s", chartID, err) + } + return resourceLibratoSpaceChartRead(d, meta) }