provider/librato: Add Librato provider

This commit is contained in:
Henrik Hodne 2015-09-30 15:08:51 -07:00 committed by James Nugent
parent 3e06a75926
commit 8f07a2d6d5
21 changed files with 1871 additions and 0 deletions

4
Godeps/Godeps.json generated
View File

@ -787,6 +787,10 @@
"ImportPath": "github.com/hashicorp/yamux",
"Rev": "df949784da9ed028ee76df44652e42d37a09d7e4"
},
{
"ImportPath": "github.com/henrikhodne/go-librato/librato",
"Rev": "613abdebf4922c4d9d46bcb4bcf14ee18c08d7de"
},
{
"ImportPath": "github.com/hmrc/vmware-govcd",
"Comment": "v0.0.2-37-g5cd82f0",

View File

@ -0,0 +1,12 @@
package main
import (
"github.com/hashicorp/terraform/builtin/providers/librato"
"github.com/hashicorp/terraform/plugin"
)
func main() {
plugin.Serve(&plugin.ServeOpts{
ProviderFunc: librato.Provider,
})
}

View File

@ -0,0 +1 @@
package main

View File

@ -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
}

View File

@ -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")
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}
}`

View File

@ -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"
}`

21
vendor/github.com/henrikhodne/go-librato/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright 2015 Henrik Hodne
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,282 @@
package librato
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"reflect"
"github.com/google/go-querystring/query"
)
const (
libraryVersion = "0.1"
defaultBaseURL = "https://metrics-api.librato.com/v1/"
userAgent = "go-librato/" + libraryVersion
defaultMediaType = "application/json"
)
// A Client manages communication with the Librato API.
type Client struct {
// HTTP client used to communicate with the API
client *http.Client
// Headers to attach to every request made with the client. Headers will be
// used to provide Librato API authentication details and other necessary
// headers.
Headers map[string]string
// Email and Token contains the authentication details needed to authenticate
// against the Librato API.
Email, Token string
// Base URL for API requests. Defaults to the public Librato API, but can be
// set to an alternate endpoint if necessary. BaseURL should always be
// terminated by a slash.
BaseURL *url.URL
// User agent used when communicating with the Librato API.
UserAgent string
// Services used to manipulate API entities.
Spaces *SpacesService
}
// NewClient returns a new Librato API client bound to the public Librato API.
func NewClient(email, token string) *Client {
bu, err := url.Parse(defaultBaseURL)
if err != nil {
panic("Default Librato API base URL couldn't be parsed")
}
return NewClientWithBaseURL(bu, email, token)
}
// NewClientWithBaseURL returned a new Librato API client with a custom base URL.
func NewClientWithBaseURL(baseURL *url.URL, email, token string) *Client {
headers := map[string]string{
"Content-Type": defaultMediaType,
"Accept": defaultMediaType,
}
c := &Client{
client: http.DefaultClient,
Headers: headers,
Email: email,
Token: token,
BaseURL: baseURL,
UserAgent: userAgent,
}
c.Spaces = &SpacesService{client: c}
return c
}
// NewRequest creates an API request. A relative URL can be provided in urlStr,
// in which case it is resolved relative to the BaseURL of the Client.
// Relative URLs should always be specified without a preceding slash. If
// specified, the value pointed to by body is JSON encoded and included as the
// request body. If specified, the map provided by headers will be used to
// update request headers.
func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) {
rel, err := url.Parse(urlStr)
if err != nil {
return nil, err
}
u := c.BaseURL.ResolveReference(rel)
var buf io.ReadWriter
if body != nil {
buf = new(bytes.Buffer)
err := json.NewEncoder(buf).Encode(body)
if err != nil {
return nil, err
}
}
req, err := http.NewRequest(method, u.String(), buf)
if err != nil {
return nil, err
}
req.SetBasicAuth(c.Email, c.Token)
if c.UserAgent != "" {
req.Header.Set("User-Agent", c.UserAgent)
}
for k, v := range c.Headers {
req.Header.Set(k, v)
}
return req, nil
}
// Do sends an API request and returns the API response. The API response is
// JSON decoded and stored in the value pointed to by v, or returned as an
// error if an API error has occurred. If v implements the io.Writer
// interface, the raw response body will be written to v, without attempting to
// first decode it.
func (c *Client) Do(req *http.Request, v interface{}) (*http.Response, error) {
resp, err := c.client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
err = CheckResponse(resp)
if err != nil {
return resp, err
}
if v != nil {
if w, ok := v.(io.Writer); ok {
_, err = io.Copy(w, resp.Body)
} else {
err = json.NewDecoder(resp.Body).Decode(v)
}
}
return resp, err
}
// ErrorResponse reports an error caused by an API request.
// ErrorResponse implements the Error interface.
type ErrorResponse struct {
// HTTP response that caused this error
Response *http.Response
// Error messages produces by Librato API.
Errors ErrorResponseMessages `json:"errors"`
}
func (er *ErrorResponse) Error() string {
buf := new(bytes.Buffer)
if er.Errors.Params != nil && len(er.Errors.Params) > 0 {
buf.WriteString(" Parameter errors:")
for param, errs := range er.Errors.Params {
fmt.Fprintf(buf, " %s:", param)
for _, err := range errs {
fmt.Fprintf(buf, " %s,", err)
}
}
buf.WriteString(".")
}
if er.Errors.Request != nil && len(er.Errors.Request) > 0 {
buf.WriteString(" Request errors:")
for _, err := range er.Errors.Request {
fmt.Fprintf(buf, " %s,", err)
}
buf.WriteString(".")
}
if er.Errors.System != nil && len(er.Errors.System) > 0 {
buf.WriteString(" System errors:")
for _, err := range er.Errors.System {
fmt.Fprintf(buf, " %s,", err)
}
buf.WriteString(".")
}
return fmt.Sprintf(
"%v %v: %d %v",
er.Response.Request.Method,
er.Response.Request.URL,
er.Response.StatusCode,
buf.String(),
)
}
// ErrorResponseMessages contains error messages returned from the Librato API.
type ErrorResponseMessages struct {
Params map[string][]string `json:"params,omitempty"`
Request []string `json:"request,omitempty"`
System []string `json:"system,omitempty"`
}
// CheckResponse checks the API response for errors; and returns them if
// present. A Response is considered an error if it has a status code outside
// the 2XX range.
func CheckResponse(r *http.Response) error {
if c := r.StatusCode; 200 <= c && c <= 299 {
return nil
}
errorResponse := &ErrorResponse{Response: r}
data, err := ioutil.ReadAll(r.Body)
if err == nil && data != nil {
json.Unmarshal(data, errorResponse)
}
return errorResponse
}
func urlWithOptions(s string, opt interface{}) (string, error) {
rv := reflect.ValueOf(opt)
if rv.Kind() == reflect.Ptr && rv.IsNil() {
return s, nil
}
u, err := url.Parse(s)
if err != nil {
return s, err
}
qs, err := query.Values(opt)
if err != nil {
return "", err
}
u.RawQuery = qs.Encode()
return u.String(), nil
}
// Bool is a helper routine that allocates a new bool value
// to store v and returns a pointer to it.
func Bool(v bool) *bool {
p := new(bool)
*p = v
return p
}
// Int is a helper routine that allocates a new int32 value
// to store v and returns a pointer to it, but unlike Int32
// its argument value is an int.
func Int(v int) *int {
p := new(int)
*p = v
return p
}
// Uint is a helper routine that allocates a new uint value
// to store v and returns a pointer to it.
func Uint(v uint) *uint {
p := new(uint)
*p = v
return p
}
// String is a helper routine that allocates a new string value
// to store v and returns a pointer to it.
func String(v string) *string {
p := new(string)
*p = v
return p
}
// Float is a helper routine that allocates a new float64 value
// to store v and returns a pointer to it.
func Float(v float64) *float64 {
p := new(float64)
*p = v
return p
}

View File

@ -0,0 +1,123 @@
package librato
import (
"fmt"
"net/http"
)
// SpacesService handles communication with the Librato API methods related to
// spaces.
type SpacesService struct {
client *Client
}
// Space represents a Librato Space.
type Space struct {
Name *string `json:"name"`
ID *uint `json:"id,omitempty"`
}
func (s Space) String() string {
return Stringify(s)
}
// SpaceListOptions specifies the optional parameters to the SpaceService.Find
// method.
type SpaceListOptions struct {
// filter by name
Name string `url:"name,omitempty"`
}
type listSpacesResponse struct {
Spaces []Space `json:"spaces"`
}
// List spaces using the provided options.
//
// Librato API docs: http://dev.librato.com/v1/get/spaces
func (s *SpacesService) List(opt *SpaceListOptions) ([]Space, *http.Response, error) {
u, err := urlWithOptions("spaces", opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
var spacesResp listSpacesResponse
resp, err := s.client.Do(req, &spacesResp)
if err != nil {
return nil, resp, err
}
return spacesResp.Spaces, resp, nil
}
// Get fetches a space based on the provided ID.
//
// Librato API docs: http://dev.librato.com/v1/get/spaces/:id
func (s *SpacesService) Get(id uint) (*Space, *http.Response, error) {
u, err := urlWithOptions(fmt.Sprintf("spaces/%d", id), nil)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
sp := new(Space)
resp, err := s.client.Do(req, sp)
if err != nil {
return nil, resp, err
}
return sp, resp, err
}
// Create a space with a given name.
//
// Librato API docs: http://dev.librato.com/v1/post/spaces
func (s *SpacesService) Create(space *Space) (*Space, *http.Response, error) {
req, err := s.client.NewRequest("POST", "spaces", space)
if err != nil {
return nil, nil, err
}
sp := new(Space)
resp, err := s.client.Do(req, sp)
if err != nil {
return nil, resp, err
}
return sp, resp, err
}
// Edit a space.
//
// Librato API docs: http://dev.librato.com/v1/put/spaces/:id
func (s *SpacesService) Edit(spaceID uint, space *Space) (*http.Response, error) {
u := fmt.Sprintf("spaces/%d", spaceID)
req, err := s.client.NewRequest("PUT", u, space)
if err != nil {
return nil, err
}
return s.client.Do(req, nil)
}
// Delete a space.
//
// Librato API docs: http://dev.librato.com/v1/delete/spaces/:id
func (s *SpacesService) Delete(id uint) (*http.Response, error) {
u := fmt.Sprintf("spaces/%d", id)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(req, nil)
}

View File

@ -0,0 +1,118 @@
package librato
import (
"fmt"
"net/http"
)
// SpaceChart represents a chart in a Librato Space.
type SpaceChart struct {
ID *uint `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Type *string `json:"type,omitempty"`
Min *float64 `json:"min,omitempty"`
Max *float64 `json:"max,omitempty"`
Label *string `json:"label,omitempty"`
RelatedSpace *uint `json:"related_space,omitempty"`
Streams []SpaceChartStream `json:"streams,omitempty"`
}
// SpaceChartStream represents a single stream in a chart in a Librato Space.
type SpaceChartStream struct {
Metric *string `json:"metric,omitempty"`
Source *string `json:"source,omitempty"`
Composite *string `json:"composite,omitempty"`
GroupFunction *string `json:"group_function,omitempty"`
SummaryFunction *string `json:"summary_function,omitempty"`
Color *string `json:"color,omitempty"`
Name *string `json:"name,omitempty"`
UnitsShort *string `json:"units_short,omitempty"`
UnitsLong *string `json:"units_long,omitempty"`
Min *float64 `json:"min,omitempty"`
Max *float64 `json:"max,omitempty"`
TransformFunction *string `json:"transform_function,omitempty"`
Period *int64 `json:"period,omitempty"`
}
// CreateChart creates a chart in a given Librato Space.
//
// Librato API docs: http://dev.librato.com/v1/post/spaces/:id/charts
func (s *SpacesService) CreateChart(spaceID uint, chart *SpaceChart) (*SpaceChart, *http.Response, error) {
u := fmt.Sprintf("spaces/%d/charts", spaceID)
req, err := s.client.NewRequest("POST", u, chart)
if err != nil {
return nil, nil, err
}
c := new(SpaceChart)
resp, err := s.client.Do(req, c)
if err != nil {
return nil, resp, err
}
return c, resp, err
}
// ListCharts lists all charts in a given Librato Space.
//
// Librato API docs: http://dev.librato.com/v1/get/spaces/:id/charts
func (s *SpacesService) ListCharts(spaceID uint) ([]SpaceChart, *http.Response, error) {
u := fmt.Sprintf("spaces/%d/charts", spaceID)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
charts := new([]SpaceChart)
resp, err := s.client.Do(req, charts)
if err != nil {
return nil, resp, err
}
return *charts, resp, err
}
// GetChart gets a chart with a given ID in a space with a given ID.
//
// Librato API docs: http://dev.librato.com/v1/get/spaces/:id/charts
func (s *SpacesService) GetChart(spaceID, chartID uint) (*SpaceChart, *http.Response, error) {
u := fmt.Sprintf("spaces/%d/charts/%d", spaceID, chartID)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
c := new(SpaceChart)
resp, err := s.client.Do(req, c)
if err != nil {
return nil, resp, err
}
return c, resp, err
}
// EditChart edits a chart.
//
// Librato API docs: http://dev.librato.com/v1/put/spaces/:id/charts/:id
func (s *SpacesService) EditChart(spaceID, chartID uint, chart *SpaceChart) (*http.Response, error) {
u := fmt.Sprintf("spaces/%d/charts/%d", spaceID, chartID)
req, err := s.client.NewRequest("PUT", u, chart)
if err != nil {
return nil, err
}
return s.client.Do(req, nil)
}
// DeleteChart deletes a chart.
//
// Librato API docs: http://dev.librato.com/v1/delete/spaces/:id/charts/:id
func (s *SpacesService) DeleteChart(spaceID, chartID uint) (*http.Response, error) {
u := fmt.Sprintf("spaces/%d/charts/%d", spaceID, chartID)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(req, nil)
}

View File

@ -0,0 +1,79 @@
package librato
import (
"bytes"
"fmt"
"io"
"reflect"
)
// Stringify attempts to create a reasonable string representation of types in
// the Librato library. It does things like resolve pointers to their values
// and omits struct fields with nil values.
func Stringify(message interface{}) string {
var buf bytes.Buffer
v := reflect.ValueOf(message)
stringifyValue(&buf, v)
return buf.String()
}
// stringifyValue was heavily inspired by the goprotobuf library.
func stringifyValue(w io.Writer, val reflect.Value) {
if val.Kind() == reflect.Ptr && val.IsNil() {
w.Write([]byte("<nil>"))
return
}
v := reflect.Indirect(val)
switch v.Kind() {
case reflect.String:
fmt.Fprintf(w, `"%s"`, v)
case reflect.Slice:
w.Write([]byte{'['})
for i := 0; i < v.Len(); i++ {
if i > 0 {
w.Write([]byte{' '})
}
stringifyValue(w, v.Index(i))
}
w.Write([]byte{']'})
return
case reflect.Struct:
if v.Type().Name() != "" {
w.Write([]byte(v.Type().String()))
}
w.Write([]byte{'{'})
var sep bool
for i := 0; i < v.NumField(); i++ {
fv := v.Field(i)
if fv.Kind() == reflect.Ptr && fv.IsNil() {
continue
}
if fv.Kind() == reflect.Slice && fv.IsNil() {
continue
}
if sep {
w.Write([]byte(", "))
} else {
sep = true
}
w.Write([]byte(v.Type().Field(i).Name))
w.Write([]byte{':'})
stringifyValue(w, fv)
}
w.Write([]byte{'}'})
default:
if v.CanInterface() {
fmt.Fprint(w, v.Interface())
}
}
}

View File

@ -0,0 +1,17 @@
package librato
import (
"fmt"
"path/filepath"
"runtime"
"testing"
)
// ok fails the test if an err is not nil.
func ok(tb testing.TB, err error) {
if err != nil {
_, file, line, _ := runtime.Caller(1)
fmt.Printf("\033[31m%s:%d: unexpected error: %s\033[39m\n\n", filepath.Base(file), line, err.Error())
tb.FailNow()
}
}

View File

@ -27,6 +27,7 @@ body.layout-fastly,
body.layout-google,
body.layout-heroku,
body.layout-influxdb,
body.layout-librato,
body.layout-mailgun,
body.layout-mysql,
body.layout-openstack,

View File

@ -0,0 +1,39 @@
---
layout: "librato"
page_title: "Provider: Librato"
sidebar_current: "docs-librato-index"
description: |-
The Librato provider is used to interact with the resources supported by Librato. The provider needs to be configured with the proper credentials before it can be used.
---
# Librato Provider
The Librato provider is used to interact with the
resources supported by Librato. The provider needs to be configured
with the proper credentials before it can be used.
Use the navigation to the left to read about the available resources.
## Example Usage
```
# Configure the Librato provider
provider "librato" {
email = "ops@company.com"
token = "${var.librato_token}"
}
# Create a new space
resource "librato_space" "default" {
...
}
```
## Argument Reference
The following arguments are supported:
* `token` - (Required) Librato API token. It must be provided, but it can also
be sourced from the `LIBRATO_TOKEN` environment variable.
* `email` - (Required) Librato email address. It must be provided, but it can
also be sourced from the `LIBRATO_EMAIL` environment variable.

View File

@ -0,0 +1,34 @@
---
layout: "librato"
page_title: "Librato: librato_space"
sidebar_current: "docs-librato-resource-space"
description: |-
Provides a Librato Space resource. This can be used to create and manage spaces on Librato.
---
# librato\_space
Provides a Librato Space resource. This can be used to
create and manage spaces on Librato.
## Example Usage
```
# Create a new Librato space
resource "librato_space" "default" {
name = "My New Space"
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) The name of the space.
## Attributes Reference
The following attributes are exported:
* `id` - The ID of the space.
* `name` - The name of the space.

View File

@ -0,0 +1,110 @@
---
layout: "librato"
page_title: "Librato: librato_space_chart"
sidebar_current: "docs-librato-resource-space-chart"
description: |-
Provides a Librato Space Chart resource. This can be used to create and manage charts in Librato Spaces.
---
# librato\_space\_chart
Provides a Librato Space Chart resource. This can be used to
create and manage charts in Librato Spaces.
## Example Usage
```
# Create a new Librato space
resource "librato_space" "my_space" {
name = "My New Space"
}
# Create a new chart
resource "librato_space_chart" "server_temperature" {
name = "Server Temperature"
space_id = "${librato_space.my_space.id}"
stream {
metric = "server_temp"
source = "app1"
}
stream {
metric = "environmental_temp"
source = "*"
group_function = "breakout"
summary_function = "average"
}
stream {
metric = "server_temp"
source = "%"
group_function = "average"
}
}
```
## Argument Reference
The following arguments are supported:
* `space_id` - (Required) The ID of the space this chart should be in.
* `name` - (Required) The title of the chart when it is displayed.
* `type` - (Optional) Indicates the type of chart. Must be one of line or
stacked (default to line).
* `min` - (Optional) The minimum display value of the chart's Y-axis.
* `max` - (Optional) The maximum display value of the chart's Y-axis.
* `label` - (Optional) The Y-axis label.
* `related_space` - (Optional) The ID of another space to which this chart is
related.
* `stream` - (Optional) Nested block describing a metric to use for data in the
chart. The structure of this block is described below.
The `stream` block supports:
* `metric` - (Required) The name of the metric. May not be specified if
`composite` is specified.
* `source` - (Required) The name of a source, or `*` to include all sources.
This field will also accept specific wildcard entries. For example
us-west-\*-app will match us-west-21-app but not us-west-12-db. Use % to
specify a dynamic source that will be provided after the instrument or
dashboard has loaded, or in the URL. May not be specified if `composite` is
specified.
* `group_function` - (Required) How to process the results when multiple sources
will be returned. Value must be one of average, sum, breakout. If average or
sum, a single line will be drawn representing the average or sum
(respectively) of all sources. If the group_function is breakout, a separate
line will be drawn for each source. If this property is not supplied, the
behavior will default to average. May not be specified if `composite` is
specified.
* `composite` - (Required) A composite metric query string to execute when this
stream is displayed. May not be specified if `metric`, `source` or
`group_function` is specified.
* `summary_function` - (Optional) When visualizing complex measurements or a
rolled-up measurement, this allows you to choose which statistic to use.
Defaults to "average". Valid options are: "max", "min", "average", "sum" or
"count".
* `name` - (Optional) A display name to use for the stream when generating the
tooltip.
* `color` - (Optional) Sets a color to use when rendering the stream. Must be a
seven character string that represents the hex code of the color e.g.
"#52D74C".
* `units_short` - (Optional) Unit value string to use as the tooltip label.
* `units_long` - (Optional) String value to set as they Y-axis label. All
streams that share the same units_long value will be plotted on the same
Y-axis.
* `min` - (Optional) Theoretical minimum Y-axis value.
* `max` - (Optional) Theoretical maximum Y-axis value.
* `transform_function` - (Optional) Linear formula to run on each measurement
prior to visualizaton.
* `period` - (Optional) An integer value of seconds that defines the period this
stream reports at. This aids in the display of the stream and allows the
period to be used in stream display transforms.
## Attributes Reference
The following attributes are exported:
* `id` - The ID of the chart.
* `space_id` - The ID of the space this chart should be in.
* `title` - The title of the chart when it is displayed.

View File

@ -215,6 +215,10 @@
<li<%= sidebar_current("docs-providers-influxdb") %>>
<a href="/docs/providers/influxdb/index.html">InfluxDB</a>
</li>
<li<%= sidebar_current("docs-providers-librato") %>>
<a href="/docs/providers/librato/index.html">Librato</a>
</li>
<li<%= sidebar_current("docs-providers-mailgun") %>>

View File

@ -0,0 +1,29 @@
<% wrap_layout :inner do %>
<% content_for :sidebar do %>
<div class="docs-sidebar hidden-print affix-top" role="complementary">
<ul class="nav docs-sidenav">
<li<%= sidebar_current("docs-home") %>>
<a href="/docs/providers/index.html">&laquo; Documentation Home</a>
</li>
<li<%= sidebar_current("docs-librato-index") %>>
<a href="/docs/providers/librato/index.html">Librato Provider</a>
</li>
<li<%= sidebar_current(/^docs-librato-resource/) %>>
<a href="#">Resources</a>
<ul class="nav nav-visible">
<li<%= sidebar_current("docs-librato-resource-space") %>>
<a href="/docs/providers/librato/r/space.html">librato_space</a>
</li>
<li<%= sidebar_current("docs-librato-resource-space-chart") %>>
<a href="/docs/providers/librato/r/space_chart.html">librato_space_chart</a>
</li>
</ul>
</li>
</ul>
</div>
<% end %>
<%= yield %>
<% end %>