Support for Librato Alerts and Services

This commit is contained in:
Anthony Stanton 2016-08-05 23:16:32 +02:00
parent 5316739ba1
commit cb66079538
No known key found for this signature in database
GPG Key ID: 42253AF31A97E303
7 changed files with 1131 additions and 0 deletions

View File

@ -28,6 +28,8 @@ func Provider() terraform.ResourceProvider {
ResourcesMap: map[string]*schema.Resource{
"librato_space": resourceLibratoSpace(),
"librato_space_chart": resourceLibratoSpaceChart(),
"librato_alert": resourceLibratoAlert(),
"librato_service": resourceLibratoService(),
},
ConfigureFunc: providerConfigure,

View File

@ -0,0 +1,446 @@
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 resourceLibratoAlert() *schema.Resource {
return &schema.Resource{
Create: resourceLibratoAlertCreate,
Read: resourceLibratoAlertRead,
Update: resourceLibratoAlertUpdate,
Delete: resourceLibratoAlertDelete,
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: false,
},
"id": &schema.Schema{
Type: schema.TypeInt,
Computed: true,
},
"description": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"active": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: true,
},
"rearm_seconds": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Default: 600,
},
"services": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
"condition": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"type": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"metric_name": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"source": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"detect_reset": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
},
"duration": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
},
"threshold": &schema.Schema{
Type: schema.TypeFloat,
Optional: true,
},
"summary_function": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
},
},
Set: resourceLibratoAlertConditionsHash,
},
"attributes": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"runbook_url": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
},
},
},
// TODO add missing condition attrs
},
}
}
func resourceLibratoAlertConditionsHash(v interface{}) int {
var buf bytes.Buffer
m := v.(map[string]interface{})
buf.WriteString(fmt.Sprintf("%s-", m["type"].(string)))
buf.WriteString(fmt.Sprintf("%f-", m["threshold"].(float64)))
buf.WriteString(fmt.Sprintf("%s-", m["metric_name"].(string)))
return hashcode.String(buf.String())
}
func resourceLibratoAlertCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*librato.Client)
alert := new(librato.Alert)
if v, ok := d.GetOk("name"); ok {
alert.Name = librato.String(v.(string))
}
if v, ok := d.GetOk("description"); ok {
alert.Description = librato.String(v.(string))
}
// GetOK returns not OK for false boolean values, use Get
alert.Active = librato.Bool(d.Get("active").(bool))
if v, ok := d.GetOk("rearm_seconds"); ok {
alert.RearmSeconds = librato.Uint(uint(v.(int)))
}
if v, ok := d.GetOk("services"); ok {
vs := v.(*schema.Set)
services := make([]*string, vs.Len())
for i, serviceData := range vs.List() {
services[i] = librato.String(serviceData.(string))
}
alert.Services = services
}
if v, ok := d.GetOk("condition"); ok {
vs := v.(*schema.Set)
conditions := make([]librato.AlertCondition, vs.Len())
for i, conditionDataM := range vs.List() {
conditionData := conditionDataM.(map[string]interface{})
var condition librato.AlertCondition
if v, ok := conditionData["type"].(string); ok && v != "" {
condition.Type = librato.String(v)
}
if v, ok := conditionData["threshold"].(float64); ok && !math.IsNaN(v) {
condition.Threshold = librato.Float(v)
}
if v, ok := conditionData["metric_name"].(string); ok && v != "" {
condition.MetricName = librato.String(v)
}
if v, ok := conditionData["source"].(string); ok && v != "" {
condition.Source = librato.String(v)
}
if v, ok := conditionData["detect_reset"].(bool); ok {
condition.DetectReset = librato.Bool(v)
}
if v, ok := conditionData["duration"].(uint); ok {
condition.Duration = librato.Uint(v)
}
if v, ok := conditionData["summary_function"].(string); ok && v != "" {
condition.SummaryFunction = librato.String(v)
}
conditions[i] = condition
}
alert.Conditions = conditions
}
if v, ok := d.GetOk("attributes"); ok {
attributeData := v.([]interface{})
if len(attributeData) > 1 {
return fmt.Errorf("Only one set of attributes per alert is supported")
} else if len(attributeData) == 1 {
if attributeData[0] == nil {
return fmt.Errorf("No attributes found in attributes block")
}
attributeDataMap := attributeData[0].(map[string]interface{})
attributes := new(librato.AlertAttributes)
if v, ok := attributeDataMap["runbook_url"].(string); ok && v != "" {
attributes.RunbookURL = librato.String(v)
}
alert.Attributes = attributes
}
}
alertResult, _, err := client.Alerts.Create(alert)
if err != nil {
return fmt.Errorf("Error creating Librato alert %s: %s", *alert.Name, err)
}
resource.Retry(1*time.Minute, func() *resource.RetryError {
_, _, err := client.Alerts.Get(*alertResult.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 resourceLibratoAlertReadResult(d, alertResult)
}
func resourceLibratoAlertRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*librato.Client)
id, err := strconv.ParseUint(d.Id(), 10, 0)
if err != nil {
return err
}
alert, _, err := client.Alerts.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 Alert %s: %s", d.Id(), err)
}
return resourceLibratoAlertReadResult(d, alert)
}
func resourceLibratoAlertReadResult(d *schema.ResourceData, alert *librato.Alert) error {
d.SetId(strconv.FormatUint(uint64(*alert.ID), 10))
if alert.ID != nil {
if err := d.Set("id", *alert.ID); err != nil {
return err
}
}
if alert.Name != nil {
if err := d.Set("name", *alert.Name); err != nil {
return err
}
}
if alert.Description != nil {
if err := d.Set("description", *alert.Description); err != nil {
return err
}
}
if alert.Active != nil {
if err := d.Set("active", *alert.Active); err != nil {
return err
}
}
if alert.RearmSeconds != nil {
if err := d.Set("rearm_seconds", *alert.RearmSeconds); err != nil {
return err
}
}
if alert.Services != nil {
services := resourceLibratoAlertServicesGather(d, alert.Services.([]interface{}))
if err := d.Set("services", services); err != nil {
return err
}
}
if alert.Conditions != nil {
conditions := resourceLibratoAlertConditionsGather(d, alert.Conditions)
if err := d.Set("condition", conditions); err != nil {
return err
}
}
if alert.Attributes != nil {
attributes := resourceLibratoAlertAttributesGather(d, alert.Attributes)
if err := d.Set("attributes", attributes); err != nil {
return err
}
}
return nil
}
func resourceLibratoAlertServicesGather(d *schema.ResourceData, services []interface{}) []string {
retServices := make([]string, 0, len(services))
for _, s := range services {
serviceData := s.(map[string]interface{})
// ID field is returned as float64, for whatever reason
retServices = append(retServices, fmt.Sprintf("%.f", serviceData["id"]))
}
return retServices
}
func resourceLibratoAlertConditionsGather(d *schema.ResourceData, conditions []librato.AlertCondition) []map[string]interface{} {
retConditions := make([]map[string]interface{}, 0, len(conditions))
for _, c := range conditions {
condition := make(map[string]interface{})
if c.Type != nil {
condition["type"] = *c.Type
}
if c.Threshold != nil {
condition["threshold"] = *c.Threshold
}
if c.MetricName != nil {
condition["metric_name"] = *c.MetricName
}
if c.Source != nil {
condition["source"] = *c.Source
}
if c.DetectReset != nil {
condition["detect_reset"] = *c.MetricName
}
if c.Duration != nil {
condition["duration"] = *c.Duration
}
if c.SummaryFunction != nil {
condition["summary_function"] = *c.SummaryFunction
}
retConditions = append(retConditions, condition)
}
return retConditions
}
// Flattens an attributes hash into something that flatmap.Flatten() can handle
func resourceLibratoAlertAttributesGather(d *schema.ResourceData, attributes *librato.AlertAttributes) []map[string]interface{} {
result := make([]map[string]interface{}, 0, 1)
if attributes != nil {
retAttributes := make(map[string]interface{})
if attributes.RunbookURL != nil {
retAttributes["runbook_url"] = *attributes.RunbookURL
}
result = append(result, retAttributes)
}
return result
}
func resourceLibratoAlertUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*librato.Client)
alertID, err := strconv.ParseUint(d.Id(), 10, 0)
if err != nil {
return err
}
alert := new(librato.Alert)
if d.HasChange("name") {
alert.Name = librato.String(d.Get("name").(string))
}
if d.HasChange("description") {
alert.Description = librato.String(d.Get("description").(string))
}
if d.HasChange("active") {
alert.Active = librato.Bool(d.Get("active").(bool))
}
if d.HasChange("rearm_seconds") {
alert.RearmSeconds = librato.Uint(d.Get("rearm_seconds").(uint))
}
if d.HasChange("services") {
vs := d.Get("services").(*schema.Set)
services := make([]*string, vs.Len())
for i, serviceData := range vs.List() {
services[i] = librato.String(serviceData.(string))
}
alert.Services = services
}
if d.HasChange("condition") {
vs := d.Get("condition").(*schema.Set)
conditions := make([]librato.AlertCondition, vs.Len())
for i, conditionDataM := range vs.List() {
conditionData := conditionDataM.(map[string]interface{})
var condition librato.AlertCondition
if v, ok := conditionData["type"].(string); ok && v != "" {
condition.Type = librato.String(v)
}
if v, ok := conditionData["threshold"].(float64); ok && !math.IsNaN(v) {
condition.Threshold = librato.Float(v)
}
if v, ok := conditionData["metric_name"].(string); ok && v != "" {
condition.MetricName = librato.String(v)
}
if v, ok := conditionData["source"].(string); ok && v != "" {
condition.Source = librato.String(v)
}
if v, ok := conditionData["detect_reset"].(bool); ok {
condition.DetectReset = librato.Bool(v)
}
if v, ok := conditionData["duration"].(uint); ok {
condition.Duration = librato.Uint(v)
}
if v, ok := conditionData["summary_function"].(string); ok && v != "" {
condition.SummaryFunction = librato.String(v)
}
conditions[i] = condition
}
alert.Conditions = conditions
}
if d.HasChange("attributes") {
attributeData := d.Get("attributes").([]interface{})
if len(attributeData) > 1 {
return fmt.Errorf("Only one set of attributes per alert is supported")
} else if len(attributeData) == 1 {
if attributeData[0] == nil {
return fmt.Errorf("No attributes found in attributes block")
}
attributeDataMap := attributeData[0].(map[string]interface{})
attributes := new(librato.AlertAttributes)
if v, ok := attributeDataMap["runbook_url"].(string); ok && v != "" {
attributes.RunbookURL = librato.String(v)
}
alert.Attributes = attributes
}
}
_, err = client.Alerts.Edit(uint(alertID), alert)
if err != nil {
return fmt.Errorf("Error updating Librato alert: %s", err)
}
return resourceLibratoAlertRead(d, meta)
}
func resourceLibratoAlertDelete(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 Alert: %d", id)
_, err = client.Alerts.Delete(uint(id))
if err != nil {
return fmt.Errorf("Error deleting Alert: %s", err)
}
resource.Retry(1*time.Minute, func() *resource.RetryError {
_, _, err := client.Alerts.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("alert still exists"))
})
d.SetId("")
return nil
}

View File

@ -0,0 +1,191 @@
package librato
import (
"fmt"
"strconv"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/henrikhodne/go-librato/librato"
)
func TestAccLibratoAlert_Basic(t *testing.T) {
var alert librato.Alert
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckLibratoAlertDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccCheckLibratoAlertConfig_basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckLibratoAlertExists("librato_alert.foobar", &alert),
testAccCheckLibratoAlertName(&alert, "FooBar"),
resource.TestCheckResourceAttr(
"librato_alert.foobar", "name", "FooBar"),
),
},
},
})
}
func TestAccLibratoAlert_Full(t *testing.T) {
var alert librato.Alert
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckLibratoAlertDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccCheckLibratoAlertConfig_full,
Check: resource.ComposeTestCheckFunc(
testAccCheckLibratoAlertExists("librato_alert.foobar", &alert),
testAccCheckLibratoAlertName(&alert, "FooBar"),
resource.TestCheckResourceAttr(
"librato_alert.foobar", "name", "FooBar"),
),
},
},
})
}
func TestAccLibratoAlert_Updated(t *testing.T) {
var alert librato.Alert
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckLibratoAlertDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccCheckLibratoAlertConfig_basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckLibratoAlertExists("librato_alert.foobar", &alert),
testAccCheckLibratoAlertName(&alert, "FooBar"),
resource.TestCheckResourceAttr(
"librato_alert.foobar", "name", "FooBar"),
),
},
resource.TestStep{
Config: testAccCheckLibratoAlertConfig_new_value,
Check: resource.ComposeTestCheckFunc(
testAccCheckLibratoAlertExists("librato_alert.foobar", &alert),
testAccCheckLibratoAlertName(&alert, "BarBaz"),
resource.TestCheckResourceAttr(
"librato_alert.foobar", "name", "BarBaz"),
),
},
},
})
}
func testAccCheckLibratoAlertDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*librato.Client)
for _, rs := range s.RootModule().Resources {
if rs.Type != "librato_alert" {
continue
}
id, err := strconv.ParseUint(rs.Primary.ID, 10, 0)
if err != nil {
return fmt.Errorf("ID not a number")
}
_, _, err = client.Alerts.Get(uint(id))
if err == nil {
return fmt.Errorf("Alert still exists")
}
}
return nil
}
func testAccCheckLibratoAlertName(alert *librato.Alert, name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
if alert.Name == nil || *alert.Name != name {
return fmt.Errorf("Bad name: %s", *alert.Name)
}
return nil
}
}
func testAccCheckLibratoAlertExists(n string, alert *librato.Alert) 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 Alert 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")
}
foundAlert, _, err := client.Alerts.Get(uint(id))
if err != nil {
return err
}
if foundAlert.ID == nil || *foundAlert.ID != uint(id) {
return fmt.Errorf("Alert not found")
}
*alert = *foundAlert
return nil
}
}
const testAccCheckLibratoAlertConfig_basic = `
resource "librato_alert" "foobar" {
name = "FooBar"
description = "A Test Alert"
}`
const testAccCheckLibratoAlertConfig_new_value = `
resource "librato_alert" "foobar" {
name = "BarBaz"
description = "A Test Alert"
}`
const testAccCheckLibratoAlertConfig_full = `
resource "librato_service" "foobar" {
title = "Foo Bar"
type = "mail"
settings = <<EOF
{
"addresses": "admin@example.com"
}
EOF
}
resource "librato_alert" "foobar" {
name = "FooBar"
description = "A Test Alert"
services = [ "${librato_service.foobar.id}" ]
condition {
type = "above"
threshold = 10
metric_name = "librato.cpu.percent.idle"
}
attributes {
runbook_url = "https://www.youtube.com/watch?v=oHg5SJYRHA0"
}
active = false
rearm_seconds = 300
}`

View File

@ -0,0 +1,229 @@
package librato
import (
// "crypto/sha1"
// "encoding/hex"
"encoding/json"
"fmt"
"log"
"strconv"
"time"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
"github.com/henrikhodne/go-librato/librato"
)
func resourceLibratoService() *schema.Resource {
return &schema.Resource{
Create: resourceLibratoServiceCreate,
Read: resourceLibratoServiceRead,
Update: resourceLibratoServiceUpdate,
Delete: resourceLibratoServiceDelete,
Schema: map[string]*schema.Schema{
"id": &schema.Schema{
Type: schema.TypeInt,
Computed: true,
},
"type": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"title": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"settings": &schema.Schema{
Type: schema.TypeString,
Required: true,
StateFunc: normalizeJson,
//StateFunc: func(v interface{}) string {
// hash := sha1.Sum([]byte(v.(string)))
// return hex.EncodeToString(hash[:])
//},
},
},
}
}
// Takes JSON in a string. Decodes JSON into
// settings hash
func resourceLibratoServicesExpandSettings(rawSettings string) (map[string]string, error) {
var settings map[string]string
settings = make(map[string]string)
err := json.Unmarshal([]byte(rawSettings), &settings)
if err != nil {
return nil, fmt.Errorf("Error decoding JSON: %s", err)
}
return settings, err
}
// Encodes a settings hash into a JSON string
func resourceLibratoServicesFlatten(settings map[string]string) (string, error) {
byteArray, err := json.Marshal(settings)
if err != nil {
return "", fmt.Errorf("Error encoding to JSON: %s", err)
}
return string(byteArray), nil
}
func normalizeJson(jsonString interface{}) string {
if jsonString == nil || jsonString == "" {
return ""
}
var j interface{}
err := json.Unmarshal([]byte(jsonString.(string)), &j)
if err != nil {
return fmt.Sprintf("Error parsing JSON: %s", err)
}
b, _ := json.Marshal(j)
return string(b[:])
}
func resourceLibratoServiceCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*librato.Client)
service := new(librato.Service)
if v, ok := d.GetOk("type"); ok {
service.Type = librato.String(v.(string))
}
if v, ok := d.GetOk("title"); ok {
service.Title = librato.String(v.(string))
}
if v, ok := d.GetOk("settings"); ok {
res, err := resourceLibratoServicesExpandSettings(normalizeJson(v.(string)))
if err != nil {
return fmt.Errorf("Error expanding Librato service settings: %s", err)
}
service.Settings = res
}
serviceResult, _, err := client.Services.Create(service)
if err != nil {
return fmt.Errorf("Error creating Librato service: %s", err)
}
resource.Retry(1*time.Minute, func() *resource.RetryError {
_, _, err := client.Services.Get(*serviceResult.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 resourceLibratoServiceReadResult(d, serviceResult)
}
func resourceLibratoServiceRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*librato.Client)
id, err := strconv.ParseUint(d.Id(), 10, 0)
if err != nil {
return err
}
service, _, err := client.Services.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 Service %s: %s", d.Id(), err)
}
return resourceLibratoServiceReadResult(d, service)
}
func resourceLibratoServiceReadResult(d *schema.ResourceData, service *librato.Service) error {
d.SetId(strconv.FormatUint(uint64(*service.ID), 10))
if service.ID != nil {
if err := d.Set("id", *service.ID); err != nil {
return err
}
}
if service.Type != nil {
if err := d.Set("type", *service.Type); err != nil {
return err
}
}
if service.Title != nil {
if err := d.Set("title", *service.Title); err != nil {
return err
}
}
if service.Settings != nil {
settings, _ := resourceLibratoServicesFlatten(service.Settings)
if err := d.Set("settings", settings); err != nil {
return err
}
}
return nil
}
func resourceLibratoServiceUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*librato.Client)
serviceID, err := strconv.ParseUint(d.Id(), 10, 0)
if err != nil {
return err
}
service := new(librato.Service)
if d.HasChange("type") {
service.Type = librato.String(d.Get("type").(string))
}
if d.HasChange("title") {
service.Title = librato.String(d.Get("title").(string))
}
if d.HasChange("settings") {
res, err := resourceLibratoServicesExpandSettings(normalizeJson(d.Get("settings").(string)))
if err != nil {
return fmt.Errorf("Error expanding Librato service settings: %s", err)
}
service.Settings = res
}
_, err = client.Services.Edit(uint(serviceID), service)
if err != nil {
return fmt.Errorf("Error updating Librato service: %s", err)
}
return resourceLibratoServiceRead(d, meta)
}
func resourceLibratoServiceDelete(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 Service: %d", id)
_, err = client.Services.Delete(uint(id))
if err != nil {
return fmt.Errorf("Error deleting Service: %s", err)
}
resource.Retry(1*time.Minute, func() *resource.RetryError {
_, _, err := client.Services.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("service still exists"))
})
d.SetId("")
return nil
}

View File

@ -0,0 +1,153 @@
package librato
import (
"fmt"
"strconv"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/henrikhodne/go-librato/librato"
)
func TestAccLibratoService_Basic(t *testing.T) {
var service librato.Service
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckLibratoServiceDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccCheckLibratoServiceConfig_basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckLibratoServiceExists("librato_service.foobar", &service),
testAccCheckLibratoServiceTitle(&service, "Foo Bar"),
resource.TestCheckResourceAttr(
"librato_service.foobar", "title", "Foo Bar"),
),
},
},
})
}
func TestAccLibratoService_Updated(t *testing.T) {
var service librato.Service
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckLibratoServiceDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccCheckLibratoServiceConfig_basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckLibratoServiceExists("librato_service.foobar", &service),
testAccCheckLibratoServiceTitle(&service, "Foo Bar"),
resource.TestCheckResourceAttr(
"librato_service.foobar", "title", "Foo Bar"),
),
},
resource.TestStep{
Config: testAccCheckLibratoServiceConfig_new_value,
Check: resource.ComposeTestCheckFunc(
testAccCheckLibratoServiceExists("librato_service.foobar", &service),
testAccCheckLibratoServiceTitle(&service, "Bar Baz"),
resource.TestCheckResourceAttr(
"librato_service.foobar", "title", "Bar Baz"),
),
},
},
})
}
func testAccCheckLibratoServiceDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*librato.Client)
for _, rs := range s.RootModule().Resources {
if rs.Type != "librato_service" {
continue
}
id, err := strconv.ParseUint(rs.Primary.ID, 10, 0)
if err != nil {
return fmt.Errorf("ID not a number")
}
_, _, err = client.Services.Get(uint(id))
if err == nil {
return fmt.Errorf("Service still exists")
}
}
return nil
}
func testAccCheckLibratoServiceTitle(service *librato.Service, title string) resource.TestCheckFunc {
return func(s *terraform.State) error {
if service.Title == nil || *service.Title != title {
return fmt.Errorf("Bad title: %s", *service.Title)
}
return nil
}
}
func testAccCheckLibratoServiceExists(n string, service *librato.Service) 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 Service 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")
}
foundService, _, err := client.Services.Get(uint(id))
if err != nil {
return err
}
if foundService.ID == nil || *foundService.ID != uint(id) {
return fmt.Errorf("Service not found")
}
*service = *foundService
return nil
}
}
const testAccCheckLibratoServiceConfig_basic = `
resource "librato_service" "foobar" {
title = "Foo Bar"
type = "mail"
settings = <<EOF
{
"addresses": "admin@example.com"
}
EOF
}`
const testAccCheckLibratoServiceConfig_new_value = `
resource "librato_service" "foobar" {
title = "Bar Baz"
type = "mail"
settings = <<EOF
{
"addresses": "admin@example.com"
}
EOF
}`

View File

@ -0,0 +1,66 @@
---
layout: "librato"
page_title: "Librato: librato_alert"
sidebar_current: "docs-librato-resource-alert"
description: |-
Provides a Librato Alert resource. This can be used to create and manage alerts on Librato.
---
# librato\_alert
Provides a Librato Alert resource. This can be used to
create and manage alerts on Librato.
## Example Usage
```
# Create a new Librato alert
resource "librato_alert" "myalert" {
name = "MyAlert"
description = "A Test Alert"
services = [ "${librato_service.myservice.id}" ]
condition {
type = "above"
threshold = 10
metric_name = "librato.cpu.percent.idle"
}
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) The name of the alert.
* `description` - (Required) Description of the alert.
* `active` - whether the alert is active (can be triggered). Defaults to true.
* `rearm_seconds` - minimum amount of time between sending alert notifications, in seconds.
* `services` - list of notification service IDs.
* `condition` - A trigger condition for the alert. Conditions documented below.
* `attributes` - A hash of additional attribtues for the alert. Attributes documented below.
## Attributes Reference
The following attributes are exported:
* `id` - The ID of the alert.
* `name` - The name of the alert.
* `description` - (Required) Description of the alert.
* `active` - whether the alert is active (can be triggered). Defaults to true.
* `rearm_seconds` - minimum amount of time between sending alert notifications, in seconds.
* `services` - list of notification service IDs.
* `condition` - A trigger condition for the alert. Conditions documented below.
Conditions (`condition`) support the following:
* `type` - The type of condition. Must be one of `above`, `below` or `absent`.
* `metric_name`- The name of the metric this alert condition applies to.
* `source`- A source expression which identifies which sources for the given metric to monitor.
* `detect_reset` - boolean: toggles the method used to calculate the delta from the previous sample when the summary_function is `derivative`.
* `duration` - number of seconds condition must be true to fire the alert (required for type `absent`).
* `threshold` - float: measurements over this number will fire the alert (only for `above` or `below`).
* `summary_function` - Indicates which statistic of an aggregated measurement to alert on. ((only for `above` or `below`).
Attributes (`attributes`) support the following:
* `runbook_url` - a URL for the runbook to be followed when this alert is firing. Used in the Librato UI if set.

View File

@ -0,0 +1,44 @@
---
layout: "librato"
page_title: "Librato: librato_service"
sidebar_current: "docs-librato-resource-service"
description: |-
Provides a Librato service resource. This can be used to create and manage notification services on Librato.
---
# librato\_service
Provides a Librato Service resource. This can be used to
create and manage notification services on Librato.
## Example Usage
```
# Create a new Librato service
resource "librato_service" "email" {
title = "Email the admins"
type = "mail"
settings = <<EOF
{
"addresses": "admin@example.com"
}
EOF
}
```
## Argument Reference
The following arguments are supported. Please check the [relevant documentation](https://github.com/librato/librato-services/tree/master/services) for each type of alert.
* `type` - (Required) The type of notificaion.
* `title` - (Required) The alert title.
* `settings` - (Required) a JSON hash of settings specific to the alert type.
## Attributes Reference
The following attributes are exported:
* `id` - The ID of the alert.
* `type` - The type of notificaion.
* `title` - The alert title.
* `settings` - a JSON hash of settings specific to the alert type.