Merge branch 'atlassian-datadog_provider'

This commit is contained in:
James Nugent 2016-02-22 16:43:04 -05:00
commit ee7e162a86
51 changed files with 4584 additions and 0 deletions

8
Godeps/Godeps.json generated
View File

@ -445,6 +445,10 @@
"ImportPath": "github.com/bgentry/speakeasy",
"Rev": "36e9cfdd690967f4f690c6edcc9ffacd006014a0"
},
{
"ImportPath": "github.com/cenkalti/backoff",
"Rev": "4dc77674aceaabba2c7e3da25d4c823edfb73f99"
},
{
"ImportPath": "github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec",
"Comment": "v2.3.0-alpha.0-652-ge552791",
@ -1129,6 +1133,10 @@
"Comment": "v1.5.4-13-g75ce5fb",
"Rev": "75ce5fbba34b1912a3641adbd58cf317d7315821"
},
{
"ImportPath": "github.com/zorkian/go-datadog-api",
"Rev": "632146c79714fe4232b496087802f922c1daf96f"
},
{
"ImportPath": "golang.org/x/crypto/curve25519",
"Rev": "1f22c0103821b9390939b6776727195525381532"

View File

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

View File

@ -0,0 +1 @@
package main

View File

@ -0,0 +1,23 @@
package datadog
import (
"log"
"github.com/zorkian/go-datadog-api"
)
// Config holds API and APP keys to authenticate to Datadog.
type Config struct {
APIKey string
APPKey string
}
// Client returns a new Datadog client.
func (c *Config) Client() (*datadog.Client, error) {
client := datadog.NewClient(c.APIKey, c.APPKey)
log.Printf("[INFO] Datadog Client configured ")
return client, nil
}

View File

@ -0,0 +1,42 @@
package datadog
import (
"log"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform"
)
func Provider() terraform.ResourceProvider {
return &schema.Provider{
Schema: map[string]*schema.Schema{
"api_key": &schema.Schema{
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc("DATADOG_API_KEY", nil),
},
"app_key": &schema.Schema{
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc("DATADOG_APP_KEY", nil),
},
},
ResourcesMap: map[string]*schema.Resource{
"datadog_monitor": resourceDatadogMonitor(),
},
ConfigureFunc: providerConfigure,
}
}
func providerConfigure(d *schema.ResourceData) (interface{}, error) {
config := Config{
APIKey: d.Get("api_key").(string),
APPKey: d.Get("app_key").(string),
}
log.Println("[INFO] Initializing Datadog client")
return config.Client()
}

View File

@ -0,0 +1,38 @@
package datadog
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{
"datadog": 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("DATADOG_API_KEY"); v == "" {
t.Fatal("DATADOG_API_KEY must be set for acceptance tests")
}
if v := os.Getenv("DATADOG_APP_KEY"); v == "" {
t.Fatal("DATADOG_APP_KEY must be set for acceptance tests")
}
}

View File

@ -0,0 +1,318 @@
package datadog
import (
"fmt"
"log"
"strconv"
"strings"
"encoding/json"
"github.com/hashicorp/terraform/helper/schema"
"github.com/zorkian/go-datadog-api"
)
func resourceDatadogMonitor() *schema.Resource {
return &schema.Resource{
Create: resourceDatadogMonitorCreate,
Read: resourceDatadogMonitorRead,
Update: resourceDatadogMonitorUpdate,
Delete: resourceDatadogMonitorDelete,
Exists: resourceDatadogMonitorExists,
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"message": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"escalation_message": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"query": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"type": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
// Options
"thresholds": &schema.Schema{
Type: schema.TypeMap,
Required: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"ok": &schema.Schema{
Type: schema.TypeFloat,
Optional: true,
},
"warning": &schema.Schema{
Type: schema.TypeFloat,
Optional: true,
},
"critical": &schema.Schema{
Type: schema.TypeFloat,
Required: true,
},
},
},
},
"notify_no_data": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: true,
},
"no_data_timeframe": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
},
"renotify_interval": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
},
"notify_audit": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
},
"timeout_h": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
},
// TODO should actually be map[string]int
"silenced": &schema.Schema{
Type: schema.TypeMap,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
Elem: &schema.Schema{
Type: schema.TypeInt},
},
},
"include_tags": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
},
},
}
}
func buildMonitorStruct(d *schema.ResourceData) *datadog.Monitor {
var thresholds datadog.ThresholdCount
if r, ok := d.GetOk("thresholds.ok"); ok {
thresholds.Ok = json.Number(r.(string))
}
if r, ok := d.GetOk("thresholds.warning"); ok {
thresholds.Warning = json.Number(r.(string))
}
if r, ok := d.GetOk("thresholds.critical"); ok {
thresholds.Critical = json.Number(r.(string))
}
o := datadog.Options{
Thresholds: thresholds,
}
if attr, ok := d.GetOk("silenced"); ok {
s := make(map[string]int)
// TODO: this is not very defensive, test if we can fail on non int input
for k, v := range attr.(map[string]interface{}) {
s[k], _ = strconv.Atoi(v.(string))
}
o.Silenced = s
}
if attr, ok := d.GetOk("notify_data"); ok {
o.NotifyNoData = attr.(bool)
}
if attr, ok := d.GetOk("no_data_timeframe"); ok {
o.NoDataTimeframe = attr.(int)
}
if attr, ok := d.GetOk("renotify_interval"); ok {
o.RenotifyInterval = attr.(int)
}
if attr, ok := d.GetOk("notify_audit"); ok {
o.NotifyAudit = attr.(bool)
}
if attr, ok := d.GetOk("timeout_h"); ok {
o.TimeoutH = attr.(int)
}
if attr, ok := d.GetOk("escalation_message"); ok {
o.EscalationMessage = attr.(string)
}
if attr, ok := d.GetOk("escalation_message"); ok {
o.EscalationMessage = attr.(string)
}
if attr, ok := d.GetOk("include_tags"); ok {
o.IncludeTags = attr.(bool)
}
m := datadog.Monitor{
Type: d.Get("type").(string),
Query: d.Get("query").(string),
Name: d.Get("name").(string),
Message: d.Get("message").(string),
Options: o,
}
return &m
}
func resourceDatadogMonitorExists(d *schema.ResourceData, meta interface{}) (b bool, e error) {
// Exists - This is called to verify a resource still exists. It is called prior to Read,
// and lowers the burden of Read to be able to assume the resource exists.
client := meta.(*datadog.Client)
i, err := strconv.Atoi(d.Id())
if err != nil {
return false, err
}
if _, err = client.GetMonitor(i); err != nil {
if strings.Contains(err.Error(), "404 Not Found") {
return false, nil
}
return false, err
}
return true, nil
}
func resourceDatadogMonitorCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*datadog.Client)
m := buildMonitorStruct(d)
m, err := client.CreateMonitor(m)
if err != nil {
return fmt.Errorf("error updating montor: %s", err.Error())
}
d.SetId(strconv.Itoa(m.Id))
return nil
}
func resourceDatadogMonitorRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*datadog.Client)
i, err := strconv.Atoi(d.Id())
if err != nil {
return err
}
m, err := client.GetMonitor(i)
if err != nil {
return err
}
log.Printf("[DEBUG] monitor: %v", m)
d.Set("name", m.Name)
d.Set("message", m.Message)
d.Set("query", m.Query)
d.Set("type", m.Type)
d.Set("thresholds", m.Options.Thresholds)
d.Set("notify_no_data", m.Options.NotifyNoData)
d.Set("notify_no_data_timeframe", m.Options.NoDataTimeframe)
d.Set("renotify_interval", m.Options.RenotifyInterval)
d.Set("notify_audit", m.Options.NotifyAudit)
d.Set("timeout_h", m.Options.TimeoutH)
d.Set("escalation_message", m.Options.EscalationMessage)
d.Set("silenced", m.Options.Silenced)
d.Set("include_tags", m.Options.IncludeTags)
return nil
}
func resourceDatadogMonitorUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*datadog.Client)
m := &datadog.Monitor{}
i, err := strconv.Atoi(d.Id())
if err != nil {
return err
}
m.Id = i
if attr, ok := d.GetOk("name"); ok {
m.Name = attr.(string)
}
if attr, ok := d.GetOk("message"); ok {
m.Message = attr.(string)
}
if attr, ok := d.GetOk("query"); ok {
m.Query = attr.(string)
}
o := datadog.Options{}
if attr, ok := d.GetOk("thresholds"); ok {
thresholds := attr.(map[string]interface{})
if thresholds["ok"] != nil {
o.Thresholds.Ok = json.Number(thresholds["ok"].(string))
}
if thresholds["warning"] != nil {
o.Thresholds.Warning = json.Number(thresholds["warning"].(string))
}
if thresholds["critical"] != nil {
o.Thresholds.Critical = json.Number(thresholds["critical"].(string))
}
}
if attr, ok := d.GetOk("notify_no_data"); ok {
o.NotifyNoData = attr.(bool)
}
if attr, ok := d.GetOk("notify_no_data_timeframe"); ok {
o.NoDataTimeframe = attr.(int)
}
if attr, ok := d.GetOk("renotify_interval"); ok {
o.RenotifyInterval = attr.(int)
}
if attr, ok := d.GetOk("notify_audit"); ok {
o.NotifyAudit = attr.(bool)
}
if attr, ok := d.GetOk("timeout_h"); ok {
o.TimeoutH = attr.(int)
}
if attr, ok := d.GetOk("escalation_message"); ok {
o.EscalationMessage = attr.(string)
}
if attr, ok := d.GetOk("silenced"); ok {
// TODO: this is not very defensive, test if we can fail non int input
s := make(map[string]int)
for k, v := range attr.(map[string]interface{}) {
s[k], _ = strconv.Atoi(v.(string))
}
o.Silenced = s
}
if attr, ok := d.GetOk("include_tags"); ok {
o.IncludeTags = attr.(bool)
}
m.Options = o
if err = client.UpdateMonitor(m); err != nil {
return fmt.Errorf("error updating montor: %s", err.Error())
}
return resourceDatadogMonitorRead(d, meta)
}
func resourceDatadogMonitorDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*datadog.Client)
i, err := strconv.Atoi(d.Id())
if err != nil {
return err
}
if err = client.DeleteMonitor(i); err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,216 @@
package datadog
import (
"fmt"
"strconv"
"strings"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/zorkian/go-datadog-api"
)
func TestAccDatadogMonitor_Basic(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckDatadogMonitorDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccCheckDatadogMonitorConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckDatadogMonitorExists("datadog_monitor.foo"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "name", "name for monitor foo"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "message", "some message Notify: @hipchat-channel"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "type", "metric alert"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "query", "avg(last_1h):avg:aws.ec2.cpu{environment:foo,host:foo} by {host} > 2"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "notify_no_data", "false"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "renotify_interval", "60"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "thresholds.ok", "0"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "thresholds.warning", "1"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "thresholds.critical", "2"),
),
},
},
})
}
func TestAccDatadogMonitor_Updated(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckDatadogMonitorDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccCheckDatadogMonitorConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckDatadogMonitorExists("datadog_monitor.foo"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "name", "name for monitor foo"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "message", "some message Notify: @hipchat-channel"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "escalation_message", "the situation has escalated @pagerduty"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "query", "avg(last_1h):avg:aws.ec2.cpu{environment:foo,host:foo} by {host} > 2"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "type", "metric alert"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "notify_no_data", "false"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "renotify_interval", "60"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "thresholds.ok", "0"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "thresholds.warning", "1"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "thresholds.critical", "2"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "notify_audit", "false"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "timeout_h", "60"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "include_tags", "true"),
),
},
resource.TestStep{
Config: testAccCheckDatadogMonitorConfigUpdated,
Check: resource.ComposeTestCheckFunc(
testAccCheckDatadogMonitorExists("datadog_monitor.foo"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "name", "name for monitor bar"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "message", "a different message Notify: @hipchat-channel"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "query", "avg(last_1h):avg:aws.ec2.cpu{environment:bar,host:bar} by {host} > 3"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "escalation_message", "the situation has escalated! @pagerduty"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "type", "metric alert"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "notify_no_data", "true"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "renotify_interval", "40"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "thresholds.ok", "0"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "thresholds.warning", "1"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "thresholds.critical", "3"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "notify_audit", "true"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "timeout_h", "70"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "include_tags", "false"),
resource.TestCheckResourceAttr(
"datadog_monitor.foo", "silenced.*", "0"),
),
},
},
})
}
func testAccCheckDatadogMonitorDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*datadog.Client)
if err := destroyHelper(s, client); err != nil {
return err
}
return nil
}
func testAccCheckDatadogMonitorExists(n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
client := testAccProvider.Meta().(*datadog.Client)
if err := existsHelper(s, client); err != nil {
return err
}
return nil
}
}
const testAccCheckDatadogMonitorConfig = `
resource "datadog_monitor" "foo" {
name = "name for monitor foo"
type = "metric alert"
message = "some message Notify: @hipchat-channel"
escalation_message = "the situation has escalated @pagerduty"
query = "avg(last_1h):avg:aws.ec2.cpu{environment:foo,host:foo} by {host} > 2"
thresholds {
ok = 0
warning = 1
critical = 2
}
notify_no_data = false
renotify_interval = 60
notify_audit = false
timeout_h = 60
include_tags = true
}
`
const testAccCheckDatadogMonitorConfigUpdated = `
resource "datadog_monitor" "foo" {
name = "name for monitor bar"
type = "metric alert"
message = "a different message Notify: @hipchat-channel"
escalation_message = "the situation has escalated @pagerduty"
query = "avg(last_1h):avg:aws.ec2.cpu{environment:bar,host:bar} by {host} > 3"
thresholds {
ok = 0
warning = 1
critical = 3
}
notify_no_data = true
renotify_interval = 40
escalation_message = "the situation has escalated! @pagerduty"
notify_audit = true
timeout_h = 70
include_tags = false
silenced {
"*" = 0
}
}
`
func destroyHelper(s *terraform.State, client *datadog.Client) error {
for _, r := range s.RootModule().Resources {
i, _ := strconv.Atoi(r.Primary.ID)
if _, err := client.GetMonitor(i); err != nil {
if strings.Contains(err.Error(), "404 Not Found") {
continue
}
return fmt.Errorf("Received an error retrieving monitor %s", err)
}
return fmt.Errorf("Monitor still exists")
}
return nil
}
func existsHelper(s *terraform.State, client *datadog.Client) error {
for _, r := range s.RootModule().Resources {
i, _ := strconv.Atoi(r.Primary.ID)
if _, err := client.GetMonitor(i); err != nil {
return fmt.Errorf("Received an error retrieving monitor %s", err)
}
}
return nil
}

22
vendor/github.com/cenkalti/backoff/.gitignore generated vendored Normal file
View File

@ -0,0 +1,22 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe

2
vendor/github.com/cenkalti/backoff/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,2 @@
language: go
go: 1.3.3

20
vendor/github.com/cenkalti/backoff/LICENSE generated vendored Normal file
View File

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2014 Cenk Altı
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.

116
vendor/github.com/cenkalti/backoff/README.md generated vendored Normal file
View File

@ -0,0 +1,116 @@
# Exponential Backoff [![GoDoc][godoc image]][godoc] [![Build Status][travis image]][travis]
This is a Go port of the exponential backoff algorithm from [Google's HTTP Client Library for Java][google-http-java-client].
[Exponential backoff][exponential backoff wiki]
is an algorithm that uses feedback to multiplicatively decrease the rate of some process,
in order to gradually find an acceptable rate.
The retries exponentially increase and stop increasing when a certain threshold is met.
## How To
We define two functions, `Retry()` and `RetryNotify()`.
They receive an `Operation` to execute, a `BackOff` algorithm,
and an optional `Notify` error handler.
The operation will be executed, and will be retried on failure with delay
as given by the backoff algorithm. The backoff algorithm can also decide when to stop
retrying.
In addition, the notify error handler will be called after each failed attempt,
except for the last time, whose error should be handled by the caller.
```go
// An Operation is executing by Retry() or RetryNotify().
// The operation will be retried using a backoff policy if it returns an error.
type Operation func() error
// Notify is a notify-on-error function. It receives an operation error and
// backoff delay if the operation failed (with an error).
//
// NOTE that if the backoff policy stated to stop retrying,
// the notify function isn't called.
type Notify func(error, time.Duration)
func Retry(Operation, BackOff) error
func RetryNotify(Operation, BackOff, Notify)
```
## Examples
See more advanced examples in the [godoc][advanced example].
### Retry
Simple retry helper that uses the default exponential backoff algorithm:
```go
operation := func() error {
// An operation that might fail.
return nil // or return errors.New("some error")
}
err := Retry(operation, NewExponentialBackOff())
if err != nil {
// Handle error.
return err
}
// Operation is successful.
return nil
```
### Ticker
```go
operation := func() error {
// An operation that might fail
return nil // or return errors.New("some error")
}
b := NewExponentialBackOff()
ticker := NewTicker(b)
var err error
// Ticks will continue to arrive when the previous operation is still running,
// so operations that take a while to fail could run in quick succession.
for range ticker.C {
if err = operation(); err != nil {
log.Println(err, "will retry...")
continue
}
ticker.Stop()
break
}
if err != nil {
// Operation has failed.
return err
}
// Operation is successful.
return nil
```
## Getting Started
```bash
# install
$ go get github.com/cenkalti/backoff
# test
$ cd $GOPATH/src/github.com/cenkalti/backoff
$ go get -t ./...
$ go test -v -cover
```
[godoc]: https://godoc.org/github.com/cenkalti/backoff
[godoc image]: https://godoc.org/github.com/cenkalti/backoff?status.png
[travis]: https://travis-ci.org/cenkalti/backoff
[travis image]: https://travis-ci.org/cenkalti/backoff.png
[google-http-java-client]: https://github.com/google/google-http-java-client
[exponential backoff wiki]: http://en.wikipedia.org/wiki/Exponential_backoff
[advanced example]: https://godoc.org/github.com/cenkalti/backoff#example_

117
vendor/github.com/cenkalti/backoff/adv_example_test.go generated vendored Normal file
View File

@ -0,0 +1,117 @@
package backoff
import (
"io/ioutil"
"log"
"net/http"
"time"
)
// This is an example that demonstrates how this package could be used
// to perform various advanced operations.
//
// It executes an HTTP GET request with exponential backoff,
// while errors are logged and failed responses are closed, as required by net/http package.
//
// Note we define a condition function which is used inside the operation to
// determine whether the operation succeeded or failed.
func Example() error {
res, err := GetWithRetry(
"http://localhost:9999",
ErrorIfStatusCodeIsNot(http.StatusOK),
NewExponentialBackOff())
if err != nil {
// Close response body of last (failed) attempt.
// The Last attempt isn't handled by the notify-on-error function,
// which closes the body of all the previous attempts.
if e := res.Body.Close(); e != nil {
log.Printf("error closing last attempt's response body: %s", e)
}
log.Printf("too many failed request attempts: %s", err)
return err
}
defer res.Body.Close() // The response's Body must be closed.
// Read body
_, _ = ioutil.ReadAll(res.Body)
// Do more stuff
return nil
}
// GetWithRetry is a helper function that performs an HTTP GET request
// to the given URL, and retries with the given backoff using the given condition function.
//
// It also uses a notify-on-error function which logs
// and closes the response body of the failed request.
func GetWithRetry(url string, condition Condition, bck BackOff) (*http.Response, error) {
var res *http.Response
err := RetryNotify(
func() error {
var err error
res, err = http.Get(url)
if err != nil {
return err
}
return condition(res)
},
bck,
LogAndClose())
return res, err
}
// Condition is a retry condition function.
// It receives a response, and returns an error
// if the response failed the condition.
type Condition func(*http.Response) error
// ErrorIfStatusCodeIsNot returns a retry condition function.
// The condition returns an error
// if the given response's status code is not the given HTTP status code.
func ErrorIfStatusCodeIsNot(status int) Condition {
return func(res *http.Response) error {
if res.StatusCode != status {
return NewError(res)
}
return nil
}
}
// Error is returned on ErrorIfX() condition functions throughout this package.
type Error struct {
Response *http.Response
}
func NewError(res *http.Response) *Error {
// Sanity check
if res == nil {
panic("response object is nil")
}
return &Error{Response: res}
}
func (err *Error) Error() string { return "request failed" }
// LogAndClose is a notify-on-error function.
// It logs the error and closes the response body.
func LogAndClose() Notify {
return func(err error, wait time.Duration) {
switch e := err.(type) {
case *Error:
defer e.Response.Body.Close()
b, err := ioutil.ReadAll(e.Response.Body)
var body string
if err != nil {
body = "can't read body"
} else {
body = string(b)
}
log.Printf("%s: %s", e.Response.Status, body)
default:
log.Println(err)
}
}
}

59
vendor/github.com/cenkalti/backoff/backoff.go generated vendored Normal file
View File

@ -0,0 +1,59 @@
// Package backoff implements backoff algorithms for retrying operations.
//
// Also has a Retry() helper for retrying operations that may fail.
package backoff
import "time"
// BackOff is a backoff policy for retrying an operation.
type BackOff interface {
// NextBackOff returns the duration to wait before retrying the operation,
// or backoff.Stop to indicate that no more retries should be made.
//
// Example usage:
//
// duration := backoff.NextBackOff();
// if (duration == backoff.Stop) {
// // Do not retry operation.
// } else {
// // Sleep for duration and retry operation.
// }
//
NextBackOff() time.Duration
// Reset to initial state.
Reset()
}
// Indicates that no more retries should be made for use in NextBackOff().
const Stop time.Duration = -1
// ZeroBackOff is a fixed backoff policy whose backoff time is always zero,
// meaning that the operation is retried immediately without waiting, indefinitely.
type ZeroBackOff struct{}
func (b *ZeroBackOff) Reset() {}
func (b *ZeroBackOff) NextBackOff() time.Duration { return 0 }
// StopBackOff is a fixed backoff policy that always returns backoff.Stop for
// NextBackOff(), meaning that the operation should never be retried.
type StopBackOff struct{}
func (b *StopBackOff) Reset() {}
func (b *StopBackOff) NextBackOff() time.Duration { return Stop }
// ConstantBackOff is a backoff policy that always returns the same backoff delay.
// This is in contrast to an exponential backoff policy,
// which returns a delay that grows longer as you call NextBackOff() over and over again.
type ConstantBackOff struct {
Interval time.Duration
}
func (b *ConstantBackOff) Reset() {}
func (b *ConstantBackOff) NextBackOff() time.Duration { return b.Interval }
func NewConstantBackOff(d time.Duration) *ConstantBackOff {
return &ConstantBackOff{Interval: d}
}

27
vendor/github.com/cenkalti/backoff/backoff_test.go generated vendored Normal file
View File

@ -0,0 +1,27 @@
package backoff
import (
"testing"
"time"
)
func TestNextBackOffMillis(t *testing.T) {
subtestNextBackOff(t, 0, new(ZeroBackOff))
subtestNextBackOff(t, Stop, new(StopBackOff))
}
func subtestNextBackOff(t *testing.T, expectedValue time.Duration, backOffPolicy BackOff) {
for i := 0; i < 10; i++ {
next := backOffPolicy.NextBackOff()
if next != expectedValue {
t.Errorf("got: %d expected: %d", next, expectedValue)
}
}
}
func TestConstantBackOff(t *testing.T) {
backoff := NewConstantBackOff(time.Second)
if backoff.NextBackOff() != time.Second {
t.Error("invalid interval")
}
}

51
vendor/github.com/cenkalti/backoff/example_test.go generated vendored Normal file
View File

@ -0,0 +1,51 @@
package backoff
import "log"
func ExampleRetry() error {
operation := func() error {
// An operation that might fail.
return nil // or return errors.New("some error")
}
err := Retry(operation, NewExponentialBackOff())
if err != nil {
// Handle error.
return err
}
// Operation is successful.
return nil
}
func ExampleTicker() error {
operation := func() error {
// An operation that might fail
return nil // or return errors.New("some error")
}
b := NewExponentialBackOff()
ticker := NewTicker(b)
var err error
// Ticks will continue to arrive when the previous operation is still running,
// so operations that take a while to fail could run in quick succession.
for _ = range ticker.C {
if err = operation(); err != nil {
log.Println(err, "will retry...")
continue
}
ticker.Stop()
break
}
if err != nil {
// Operation has failed.
return err
}
// Operation is successful.
return nil
}

151
vendor/github.com/cenkalti/backoff/exponential.go generated vendored Normal file
View File

@ -0,0 +1,151 @@
package backoff
import (
"math/rand"
"time"
)
/*
ExponentialBackOff is a backoff implementation that increases the backoff
period for each retry attempt using a randomization function that grows exponentially.
NextBackOff() is calculated using the following formula:
randomized interval =
RetryInterval * (random value in range [1 - RandomizationFactor, 1 + RandomizationFactor])
In other words NextBackOff() will range between the randomization factor
percentage below and above the retry interval.
For example, given the following parameters:
RetryInterval = 2
RandomizationFactor = 0.5
Multiplier = 2
the actual backoff period used in the next retry attempt will range between 1 and 3 seconds,
multiplied by the exponential, that is, between 2 and 6 seconds.
Note: MaxInterval caps the RetryInterval and not the randomized interval.
If the time elapsed since an ExponentialBackOff instance is created goes past the
MaxElapsedTime, then the method NextBackOff() starts returning backoff.Stop.
The elapsed time can be reset by calling Reset().
Example: Given the following default arguments, for 10 tries the sequence will be,
and assuming we go over the MaxElapsedTime on the 10th try:
Request # RetryInterval (seconds) Randomized Interval (seconds)
1 0.5 [0.25, 0.75]
2 0.75 [0.375, 1.125]
3 1.125 [0.562, 1.687]
4 1.687 [0.8435, 2.53]
5 2.53 [1.265, 3.795]
6 3.795 [1.897, 5.692]
7 5.692 [2.846, 8.538]
8 8.538 [4.269, 12.807]
9 12.807 [6.403, 19.210]
10 19.210 backoff.Stop
Note: Implementation is not thread-safe.
*/
type ExponentialBackOff struct {
InitialInterval time.Duration
RandomizationFactor float64
Multiplier float64
MaxInterval time.Duration
// After MaxElapsedTime the ExponentialBackOff stops.
// It never stops if MaxElapsedTime == 0.
MaxElapsedTime time.Duration
Clock Clock
currentInterval time.Duration
startTime time.Time
}
// Clock is an interface that returns current time for BackOff.
type Clock interface {
Now() time.Time
}
// Default values for ExponentialBackOff.
const (
DefaultInitialInterval = 500 * time.Millisecond
DefaultRandomizationFactor = 0.5
DefaultMultiplier = 1.5
DefaultMaxInterval = 60 * time.Second
DefaultMaxElapsedTime = 15 * time.Minute
)
// NewExponentialBackOff creates an instance of ExponentialBackOff using default values.
func NewExponentialBackOff() *ExponentialBackOff {
b := &ExponentialBackOff{
InitialInterval: DefaultInitialInterval,
RandomizationFactor: DefaultRandomizationFactor,
Multiplier: DefaultMultiplier,
MaxInterval: DefaultMaxInterval,
MaxElapsedTime: DefaultMaxElapsedTime,
Clock: SystemClock,
}
b.Reset()
return b
}
type systemClock struct{}
func (t systemClock) Now() time.Time {
return time.Now()
}
// SystemClock implements Clock interface that uses time.Now().
var SystemClock = systemClock{}
// Reset the interval back to the initial retry interval and restarts the timer.
func (b *ExponentialBackOff) Reset() {
b.currentInterval = b.InitialInterval
b.startTime = b.Clock.Now()
}
// NextBackOff calculates the next backoff interval using the formula:
// Randomized interval = RetryInterval +/- (RandomizationFactor * RetryInterval)
func (b *ExponentialBackOff) NextBackOff() time.Duration {
// Make sure we have not gone over the maximum elapsed time.
if b.MaxElapsedTime != 0 && b.GetElapsedTime() > b.MaxElapsedTime {
return Stop
}
defer b.incrementCurrentInterval()
return getRandomValueFromInterval(b.RandomizationFactor, rand.Float64(), b.currentInterval)
}
// GetElapsedTime returns the elapsed time since an ExponentialBackOff instance
// is created and is reset when Reset() is called.
//
// The elapsed time is computed using time.Now().UnixNano().
func (b *ExponentialBackOff) GetElapsedTime() time.Duration {
return b.Clock.Now().Sub(b.startTime)
}
// Increments the current interval by multiplying it with the multiplier.
func (b *ExponentialBackOff) incrementCurrentInterval() {
// Check for overflow, if overflow is detected set the current interval to the max interval.
if float64(b.currentInterval) >= float64(b.MaxInterval)/b.Multiplier {
b.currentInterval = b.MaxInterval
} else {
b.currentInterval = time.Duration(float64(b.currentInterval) * b.Multiplier)
}
}
// Returns a random value from the following interval:
// [randomizationFactor * currentInterval, randomizationFactor * currentInterval].
func getRandomValueFromInterval(randomizationFactor, random float64, currentInterval time.Duration) time.Duration {
var delta = randomizationFactor * float64(currentInterval)
var minInterval = float64(currentInterval) - delta
var maxInterval = float64(currentInterval) + delta
// Get a random value from the range [minInterval, maxInterval].
// The formula used below has a +1 because if the minInterval is 1 and the maxInterval is 3 then
// we want a 33% chance for selecting either 1, 2 or 3.
return time.Duration(minInterval + (random * (maxInterval - minInterval + 1)))
}

108
vendor/github.com/cenkalti/backoff/exponential_test.go generated vendored Normal file
View File

@ -0,0 +1,108 @@
package backoff
import (
"math"
"testing"
"time"
)
func TestBackOff(t *testing.T) {
var (
testInitialInterval = 500 * time.Millisecond
testRandomizationFactor = 0.1
testMultiplier = 2.0
testMaxInterval = 5 * time.Second
testMaxElapsedTime = 15 * time.Minute
)
exp := NewExponentialBackOff()
exp.InitialInterval = testInitialInterval
exp.RandomizationFactor = testRandomizationFactor
exp.Multiplier = testMultiplier
exp.MaxInterval = testMaxInterval
exp.MaxElapsedTime = testMaxElapsedTime
exp.Reset()
var expectedResults = []time.Duration{500, 1000, 2000, 4000, 5000, 5000, 5000, 5000, 5000, 5000}
for i, d := range expectedResults {
expectedResults[i] = d * time.Millisecond
}
for _, expected := range expectedResults {
assertEquals(t, expected, exp.currentInterval)
// Assert that the next backoff falls in the expected range.
var minInterval = expected - time.Duration(testRandomizationFactor*float64(expected))
var maxInterval = expected + time.Duration(testRandomizationFactor*float64(expected))
var actualInterval = exp.NextBackOff()
if !(minInterval <= actualInterval && actualInterval <= maxInterval) {
t.Error("error")
}
}
}
func TestGetRandomizedInterval(t *testing.T) {
// 33% chance of being 1.
assertEquals(t, 1, getRandomValueFromInterval(0.5, 0, 2))
assertEquals(t, 1, getRandomValueFromInterval(0.5, 0.33, 2))
// 33% chance of being 2.
assertEquals(t, 2, getRandomValueFromInterval(0.5, 0.34, 2))
assertEquals(t, 2, getRandomValueFromInterval(0.5, 0.66, 2))
// 33% chance of being 3.
assertEquals(t, 3, getRandomValueFromInterval(0.5, 0.67, 2))
assertEquals(t, 3, getRandomValueFromInterval(0.5, 0.99, 2))
}
type TestClock struct {
i time.Duration
start time.Time
}
func (c *TestClock) Now() time.Time {
t := c.start.Add(c.i)
c.i += time.Second
return t
}
func TestGetElapsedTime(t *testing.T) {
var exp = NewExponentialBackOff()
exp.Clock = &TestClock{}
exp.Reset()
var elapsedTime = exp.GetElapsedTime()
if elapsedTime != time.Second {
t.Errorf("elapsedTime=%d", elapsedTime)
}
}
func TestMaxElapsedTime(t *testing.T) {
var exp = NewExponentialBackOff()
exp.Clock = &TestClock{start: time.Time{}.Add(10000 * time.Second)}
// Change the currentElapsedTime to be 0 ensuring that the elapsed time will be greater
// than the max elapsed time.
exp.startTime = time.Time{}
assertEquals(t, Stop, exp.NextBackOff())
}
func TestBackOffOverflow(t *testing.T) {
var (
testInitialInterval time.Duration = math.MaxInt64 / 2
testMaxInterval time.Duration = math.MaxInt64
testMultiplier = 2.1
)
exp := NewExponentialBackOff()
exp.InitialInterval = testInitialInterval
exp.Multiplier = testMultiplier
exp.MaxInterval = testMaxInterval
exp.Reset()
exp.NextBackOff()
// Assert that when an overflow is possible the current varerval time.Duration is set to the max varerval time.Duration .
assertEquals(t, testMaxInterval, exp.currentInterval)
}
func assertEquals(t *testing.T, expected, value time.Duration) {
if expected != value {
t.Errorf("got: %d, expected: %d", value, expected)
}
}

46
vendor/github.com/cenkalti/backoff/retry.go generated vendored Normal file
View File

@ -0,0 +1,46 @@
package backoff
import "time"
// An Operation is executing by Retry() or RetryNotify().
// The operation will be retried using a backoff policy if it returns an error.
type Operation func() error
// Notify is a notify-on-error function. It receives an operation error and
// backoff delay if the operation failed (with an error).
//
// NOTE that if the backoff policy stated to stop retrying,
// the notify function isn't called.
type Notify func(error, time.Duration)
// Retry the function f until it does not return error or BackOff stops.
// f is guaranteed to be run at least once.
// It is the caller's responsibility to reset b after Retry returns.
//
// Retry sleeps the goroutine for the duration returned by BackOff after a
// failed operation returns.
func Retry(o Operation, b BackOff) error { return RetryNotify(o, b, nil) }
// RetryNotify calls notify function with the error and wait duration
// for each failed attempt before sleep.
func RetryNotify(operation Operation, b BackOff, notify Notify) error {
var err error
var next time.Duration
b.Reset()
for {
if err = operation(); err == nil {
return nil
}
if next = b.NextBackOff(); next == Stop {
return err
}
if notify != nil {
notify(err, next)
}
time.Sleep(next)
}
}

34
vendor/github.com/cenkalti/backoff/retry_test.go generated vendored Normal file
View File

@ -0,0 +1,34 @@
package backoff
import (
"errors"
"log"
"testing"
)
func TestRetry(t *testing.T) {
const successOn = 3
var i = 0
// This function is successfull on "successOn" calls.
f := func() error {
i++
log.Printf("function is called %d. time\n", i)
if i == successOn {
log.Println("OK")
return nil
}
log.Println("error")
return errors.New("error")
}
err := Retry(f, NewExponentialBackOff())
if err != nil {
t.Errorf("unexpected error: %s", err.Error())
}
if i != successOn {
t.Errorf("invalid number of retries: %d", i)
}
}

79
vendor/github.com/cenkalti/backoff/ticker.go generated vendored Normal file
View File

@ -0,0 +1,79 @@
package backoff
import (
"runtime"
"sync"
"time"
)
// Ticker holds a channel that delivers `ticks' of a clock at times reported by a BackOff.
//
// Ticks will continue to arrive when the previous operation is still running,
// so operations that take a while to fail could run in quick succession.
type Ticker struct {
C <-chan time.Time
c chan time.Time
b BackOff
stop chan struct{}
stopOnce sync.Once
}
// NewTicker returns a new Ticker containing a channel that will send the time at times
// specified by the BackOff argument. Ticker is guaranteed to tick at least once.
// The channel is closed when Stop method is called or BackOff stops.
func NewTicker(b BackOff) *Ticker {
c := make(chan time.Time)
t := &Ticker{
C: c,
c: c,
b: b,
stop: make(chan struct{}),
}
go t.run()
runtime.SetFinalizer(t, (*Ticker).Stop)
return t
}
// Stop turns off a ticker. After Stop, no more ticks will be sent.
func (t *Ticker) Stop() {
t.stopOnce.Do(func() { close(t.stop) })
}
func (t *Ticker) run() {
c := t.c
defer close(c)
t.b.Reset()
// Ticker is guaranteed to tick at least once.
afterC := t.send(time.Now())
for {
if afterC == nil {
return
}
select {
case tick := <-afterC:
afterC = t.send(tick)
case <-t.stop:
t.c = nil // Prevent future ticks from being sent to the channel.
return
}
}
}
func (t *Ticker) send(tick time.Time) <-chan time.Time {
select {
case t.c <- tick:
case <-t.stop:
return nil
}
next := t.b.NextBackOff()
if next == Stop {
t.Stop()
return nil
}
return time.After(next)
}

45
vendor/github.com/cenkalti/backoff/ticker_test.go generated vendored Normal file
View File

@ -0,0 +1,45 @@
package backoff
import (
"errors"
"log"
"testing"
)
func TestTicker(t *testing.T) {
const successOn = 3
var i = 0
// This function is successfull on "successOn" calls.
f := func() error {
i++
log.Printf("function is called %d. time\n", i)
if i == successOn {
log.Println("OK")
return nil
}
log.Println("error")
return errors.New("error")
}
b := NewExponentialBackOff()
ticker := NewTicker(b)
var err error
for _ = range ticker.C {
if err = f(); err != nil {
t.Log(err)
continue
}
break
}
if err != nil {
t.Errorf("unexpected error: %s", err.Error())
}
if i != successOn {
t.Errorf("invalid number of retries: %d", i)
}
}

30
vendor/github.com/zorkian/go-datadog-api/LICENSE generated vendored Normal file
View File

@ -0,0 +1,30 @@
Copyright (c) 2013 by authors and contributors.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER>
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.

42
vendor/github.com/zorkian/go-datadog-api/Makefile generated vendored Normal file
View File

@ -0,0 +1,42 @@
TEST?=.
VETARGS?=-asmdecl -atomic -bool -buildtags -copylocks -methods -nilfunc -printf -rangeloops -shift -structtags -unsafeptr
default: test
# get dependencies
updatedeps:
go list ./... \
| xargs go list -f '{{join .Deps "\n"}}' \
| grep -v go-datadog-api\
| grep -v '/internal/' \
| sort -u \
| xargs go get -f -u -v
# test runs the unit tests and vets the code
test:
go test . $(TESTARGS) -v -timeout=30s -parallel=4
@$(MAKE) vet
# testacc runs acceptance tests
testacc:
go test integration/* -v $(TESTARGS) -timeout 90m
# testrace runs the race checker
testrace:
go test -race $(TEST) $(TESTARGS)
# vet runs the Go source code static analysis tool `vet` to find
# any common errors.
vet:
@go tool vet 2>/dev/null ; if [ $$? -eq 3 ]; then \
go get golang.org/x/tools/cmd/vet; \
fi
@echo "go tool vet $(VETARGS) $(TEST) "
@go tool vet $(VETARGS) $(TEST) ; if [ $$? -eq 1 ]; then \
echo ""; \
echo "Vet found suspicious constructs. Please check the reported constructs"; \
echo "and fix them if necessary before submitting the code for review."; \
exit 1; \
fi
.PHONY: default test testacc updatedeps vet

69
vendor/github.com/zorkian/go-datadog-api/README.md generated vendored Normal file
View File

@ -0,0 +1,69 @@
[![GoDoc](http://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/zorkian/go-datadog-api)
[![Build
status](https://travis-ci.org/zorkian/go-datadog-api.svg)](https://travis-ci.org/zorkian/go-datadog-api)
# Datadog API in Go
Hi!
This is a Go wrapper for the Datadog API. You should use this library if you need to interact
with the Datadog system. You can post metrics with it if you want, but this library is probably
mostly used for automating dashboards/alerting and retrieving data (events, etc).
The source API documentation is here: <http://docs.datadoghq.com/api/>
## USAGE
To use this project, include it in your code like:
``` go
import "github.com/zorkian/go-datadog-api"
```
Then, you can work with it:
``` go
client := datadog.NewClient("api key", "application key")
dash, err := client.GetDashboard(10880)
if err != nil {
log.Fatalf("fatal: %s\n", err)
}
log.Printf("dashboard %d: %s\n", dash.Id, dash.Title)
```
That's all; it's pretty easy to use. Check out the Godoc link for the
available API methods and, if you can't find the one you need,
let us know (or patches welcome)!
## DOCUMENTATION
Please see: <http://godoc.org/github.com/zorkian/go-datadog-api>
## BUGS/PROBLEMS/CONTRIBUTING
There are certainly some, but presently no known major bugs. If you do
find something that doesn't work as expected, please file an issue on
Github:
<https://github.com/zorkian/go-datadog-api/issues>
Thanks in advance! And, as always, patches welcome!
## DEVELOPMENT
* Get dependencies with `make updatedeps`.
* Run tests tests with `make test`.
* Integration tests can be run with `make testacc`.
The acceptance tests require _DATADOG_API_KEY_ and _DATADOG_APP_KEY_ to be available
in your environment variables.
*Warning: the integrations tests will create and remove real resources in your Datadog
account*
## COPYRIGHT AND LICENSE
Please see the LICENSE file for the included license information.
Copyright 2013 by authors and contributors.

85
vendor/github.com/zorkian/go-datadog-api/alerts.go generated vendored Normal file
View File

@ -0,0 +1,85 @@
/*
* Datadog API for Go
*
* Please see the included LICENSE file for licensing information.
*
* Copyright 2013 by authors and contributors.
*/
package datadog
import (
"fmt"
)
// Alert represents the data of an alert: a query that can fire and send a
// message to the users.
type Alert struct {
Id int `json:"id,omitempty"`
Creator int `json:"creator,omitempty"`
Query string `json:"query,omitempty"`
Name string `json:"name,omitempty"`
Message string `json:"message,omitempty"`
Silenced bool `json:"silenced,omitempty"`
NotifyNoData bool `json:"notify_no_data,omitempty"`
State string `json:"state,omitempty"`
}
// reqAlerts receives a slice of all alerts.
type reqAlerts struct {
Alerts []Alert `json:"alerts,omitempty"`
}
// CreateAlert adds a new alert to the system. This returns a pointer to an
// Alert so you can pass that to UpdateAlert later if needed.
func (self *Client) CreateAlert(alert *Alert) (*Alert, error) {
var out Alert
err := self.doJsonRequest("POST", "/v1/alert", alert, &out)
if err != nil {
return nil, err
}
return &out, nil
}
// UpdateAlert takes an alert that was previously retrieved through some method
// and sends it back to the server.
func (self *Client) UpdateAlert(alert *Alert) error {
return self.doJsonRequest("PUT", fmt.Sprintf("/v1/alert/%d", alert.Id),
alert, nil)
}
// GetAlert retrieves an alert by identifier.
func (self *Client) GetAlert(id int) (*Alert, error) {
var out Alert
err := self.doJsonRequest("GET", fmt.Sprintf("/v1/alert/%d", id), nil, &out)
if err != nil {
return nil, err
}
return &out, nil
}
// DeleteAlert removes an alert from the system.
func (self *Client) DeleteAlert(id int) error {
return self.doJsonRequest("DELETE", fmt.Sprintf("/v1/alert/%d", id),
nil, nil)
}
// GetAlerts returns a slice of all alerts.
func (self *Client) GetAlerts() ([]Alert, error) {
var out reqAlerts
err := self.doJsonRequest("GET", "/v1/alert", nil, &out)
if err != nil {
return nil, err
}
return out.Alerts, nil
}
// MuteAlerts turns off alerting notifications.
func (self *Client) MuteAlerts() error {
return self.doJsonRequest("POST", "/v1/mute_alerts", nil, nil)
}
// UnmuteAlerts turns on alerting notifications.
func (self *Client) UnmuteAlerts() error {
return self.doJsonRequest("POST", "/v1/unmute_alerts", nil, nil)
}

65
vendor/github.com/zorkian/go-datadog-api/comments.go generated vendored Normal file
View File

@ -0,0 +1,65 @@
/*
* Datadog API for Go
*
* Please see the included LICENSE file for licensing information.
*
* Copyright 2013 by authors and contributors.
*/
package datadog
import (
"fmt"
)
// Comment is a special form of event that appears in a stream.
type Comment struct {
Id int `json:"id"`
RelatedId int `json:"related_event_id"`
Handle string `json:"handle"`
Message string `json:"message"`
Resource string `json:"resource"`
Url string `json:"url"`
}
// reqComment is the container for receiving commenst.
type reqComment struct {
Comment Comment `json:"comment"`
}
// CreateComment adds a new comment to the system.
func (self *Client) CreateComment(handle, message string) (*Comment, error) {
var out reqComment
comment := Comment{Handle: handle, Message: message}
err := self.doJsonRequest("POST", "/v1/comments", &comment, &out)
if err != nil {
return nil, err
}
return &out.Comment, nil
}
// CreateRelatedComment adds a new comment, but lets you specify the related
// identifier for the comment.
func (self *Client) CreateRelatedComment(handle, message string,
relid int) (*Comment, error) {
var out reqComment
comment := Comment{Handle: handle, Message: message, RelatedId: relid}
err := self.doJsonRequest("POST", "/v1/comments", &comment, &out)
if err != nil {
return nil, err
}
return &out.Comment, nil
}
// EditComment changes the message and possibly handle of a particular comment.
func (self *Client) EditComment(id int, handle, message string) error {
comment := Comment{Handle: handle, Message: message}
return self.doJsonRequest("PUT", fmt.Sprintf("/v1/comments/%d", id),
&comment, nil)
}
// DeleteComment does exactly what you expect.
func (self *Client) DeleteComment(id int) error {
return self.doJsonRequest("DELETE", fmt.Sprintf("/v1/comments/%d", id),
nil, nil)
}

107
vendor/github.com/zorkian/go-datadog-api/dashboards.go generated vendored Normal file
View File

@ -0,0 +1,107 @@
/*
* Datadog API for Go
*
* Please see the included LICENSE file for licensing information.
*
* Copyright 2013 by authors and contributors.
*/
package datadog
import (
"fmt"
)
// Graph represents a graph that might exist on a dashboard.
type Graph struct {
Title string `json:"title"`
Events []struct{} `json:"events"`
Definition struct {
Viz string `json:"viz"`
Requests []struct {
Query string `json:"q"`
Stacked bool `json:"stacked"`
} `json:"requests"`
} `json:"definition"`
}
// Template variable represents a template variable that might exist on a dashboard
type TemplateVariable struct {
Name string `json:"name"`
Prefix string `json:"prefix"`
Default string `json:"default"`
}
// Dashboard represents a user created dashboard. This is the full dashboard
// struct when we load a dashboard in detail.
type Dashboard struct {
Id int `json:"id"`
Description string `json:"description"`
Title string `json:"title"`
Graphs []Graph `json:"graphs"`
TemplateVariables []TemplateVariable `json:"template_variables,omitempty"`
}
// DashboardLite represents a user created dashboard. This is the mini
// struct when we load the summaries.
type DashboardLite struct {
Id int `json:"id,string"` // TODO: Remove ',string'.
Resource string `json:"resource"`
Description string `json:"description"`
Title string `json:"title"`
}
// reqGetDashboards from /api/v1/dash
type reqGetDashboards struct {
Dashboards []DashboardLite `json:"dashes"`
}
// reqGetDashboard from /api/v1/dash/:dashboard_id
type reqGetDashboard struct {
Resource string `json:"resource"`
Url string `json:"url"`
Dashboard Dashboard `json:"dash"`
}
// GetDashboard returns a single dashboard created on this account.
func (self *Client) GetDashboard(id int) (*Dashboard, error) {
var out reqGetDashboard
err := self.doJsonRequest("GET", fmt.Sprintf("/v1/dash/%d", id), nil, &out)
if err != nil {
return nil, err
}
return &out.Dashboard, nil
}
// GetDashboards returns a list of all dashboards created on this account.
func (self *Client) GetDashboards() ([]DashboardLite, error) {
var out reqGetDashboards
err := self.doJsonRequest("GET", "/v1/dash", nil, &out)
if err != nil {
return nil, err
}
return out.Dashboards, nil
}
// DeleteDashboard deletes a dashboard by the identifier.
func (self *Client) DeleteDashboard(id int) error {
return self.doJsonRequest("DELETE", fmt.Sprintf("/v1/dash/%d", id), nil, nil)
}
// CreateDashboard creates a new dashboard when given a Dashboard struct. Note
// that the Id, Resource, Url and similar elements are not used in creation.
func (self *Client) CreateDashboard(dash *Dashboard) (*Dashboard, error) {
var out reqGetDashboard
err := self.doJsonRequest("POST", "/v1/dash", dash, &out)
if err != nil {
return nil, err
}
return &out.Dashboard, nil
}
// UpdateDashboard in essence takes a Dashboard struct and persists it back to
// the server. Use this if you've updated your local and need to push it back.
func (self *Client) UpdateDashboard(dash *Dashboard) error {
return self.doJsonRequest("PUT", fmt.Sprintf("/v1/dash/%d", dash.Id),
dash, nil)
}

83
vendor/github.com/zorkian/go-datadog-api/downtimes.go generated vendored Normal file
View File

@ -0,0 +1,83 @@
/*
* Datadog API for Go
*
* Please see the included LICENSE file for licensing information.
*
* Copyright 2013 by authors and contributors.
*/
package datadog
import (
"fmt"
)
type Recurrence struct {
Period int `json:"period,omitempty"`
Type string `json:"type,omitempty"`
UntilDate int `json:"until_date,omitempty"`
UntilOccurrences int `json:"until_occurrences,omitempty"`
WeekDays []string `json:"week_days,omitempty"`
}
type Downtime struct {
Active bool `json:"active,omitempty"`
Canceled int `json:"canceled,omitempty"`
Disabled bool `json:"disabled,omitempty"`
End int `json:"end,omitempty"`
Id int `json:"id,omitempty"`
Message string `json:"message,omitempty"`
Recurrence *Recurrence `json:"recurrence,omitempty"`
Scope []string `json:"scope,omitempty"`
Start int `json:"start,omitempty"`
}
// reqDowntimes retrieves a slice of all Downtimes.
type reqDowntimes struct {
Downtimes []Downtime `json:"downtimes,omitempty"`
}
// CreateDowntime adds a new downtme to the system. This returns a pointer
// to a Downtime so you can pass that to UpdateDowntime or CancelDowntime
// later if needed.
func (self *Client) CreateDowntime(downtime *Downtime) (*Downtime, error) {
var out Downtime
err := self.doJsonRequest("POST", "/v1/downtime", downtime, &out)
if err != nil {
return nil, err
}
return &out, nil
}
// UpdateDowntime takes a downtime that was previously retrieved through some method
// and sends it back to the server.
func (self *Client) UpdateDowntime(downtime *Downtime) error {
return self.doJsonRequest("PUT", fmt.Sprintf("/v1/downtime/%d", downtime.Id),
downtime, nil)
}
// Getdowntime retrieves an downtime by identifier.
func (self *Client) GetDowntime(id int) (*Downtime, error) {
var out Downtime
err := self.doJsonRequest("GET", fmt.Sprintf("/v1/downtime/%d", id), nil, &out)
if err != nil {
return nil, err
}
return &out, nil
}
// DeleteDowntime removes an downtime from the system.
func (self *Client) DeleteDowntime(id int) error {
return self.doJsonRequest("DELETE", fmt.Sprintf("/v1/downtime/%d", id),
nil, nil)
}
// GetDowntimes returns a slice of all downtimes.
func (self *Client) GetDowntimes() ([]Downtime, error) {
var out reqDowntimes
err := self.doJsonRequest("GET", "/v1/downtime", nil, &out.Downtimes)
if err != nil {
return nil, err
}
return out.Downtimes, nil
}

89
vendor/github.com/zorkian/go-datadog-api/events.go generated vendored Normal file
View File

@ -0,0 +1,89 @@
/*
* Datadog API for Go
*
* Please see the included LICENSE file for licensing information.
*
* Copyright 2013 by authors and contributors.
*/
package datadog
import (
"fmt"
"net/url"
"strconv"
)
// Event is a single event. If this is being used to post an event, then not
// all fields will be filled out.
type Event struct {
Id int `json:"id,omitempty"`
Title string `json:"title,omitempty"`
Text string `json:"text,omitempty"`
Time int `json:"date_happened,omitempty"` // UNIX time.
Priority string `json:"priority,omitempty"`
AlertType string `json:"alert_type,omitempty"`
Host string `json:"host,omitempty"`
Aggregation string `json:"aggregation_key,omitempty"`
SourceType string `json:"source_type,omitempty"`
Tags []string `json:"tags,omitempty"`
Url string `json:"url,omitempty"`
Resource string `json:"resource,omitempty"`
}
// reqGetEvent is the container for receiving a single event.
type reqGetEvent struct {
Event Event `json:"event,omitempty"`
}
// reqGetEvents is for returning many events.
type reqGetEvents struct {
Events []Event `json:"events,omitempty"`
}
// PostEvent takes as input an event and then posts it to the server.
func (self *Client) PostEvent(event *Event) (*Event, error) {
var out reqGetEvent
err := self.doJsonRequest("POST", "/v1/events", event, &out)
if err != nil {
return nil, err
}
return &out.Event, nil
}
// GetEvent gets a single event given an identifier.
func (self *Client) GetEvent(id int) (*Event, error) {
var out reqGetEvent
err := self.doJsonRequest("GET", fmt.Sprintf("/v1/events/%d", id), nil, &out)
if err != nil {
return nil, err
}
return &out.Event, nil
}
// QueryEvents returns a slice of events from the query stream.
func (self *Client) GetEvents(start, end int,
priority, sources, tags string) ([]Event, error) {
// Since this is a GET request, we need to build a query string.
vals := url.Values{}
vals.Add("start", strconv.Itoa(start))
vals.Add("end", strconv.Itoa(end))
if priority != "" {
vals.Add("priority", priority)
}
if sources != "" {
vals.Add("sources", sources)
}
if tags != "" {
vals.Add("tags", tags)
}
// Now the request and response.
var out reqGetEvents
err := self.doJsonRequest("GET",
fmt.Sprintf("/v1/events?%s", vals.Encode()), nil, &out)
if err != nil {
return nil, err
}
return out.Events, nil
}

View File

@ -0,0 +1,138 @@
package integration
import (
"github.com/zorkian/go-datadog-api"
"testing"
)
func init() {
client = initTest()
}
func TestCreateAndDeleteDashboard(t *testing.T) {
expected := getTestDashboard()
// create the dashboard and compare it
actual, err := client.CreateDashboard(expected)
if err != nil {
t.Fatalf("Creating a dashboard failed when it shouldn't. (%s)", err)
}
defer cleanUpDashboard(t, actual.Id)
assertDashboardEquals(t, actual, expected)
// now try to fetch it freshly and compare it again
actual, err = client.GetDashboard(actual.Id)
if err != nil {
t.Fatalf("Retrieving a dashboard failed when it shouldn't. (%s)", err)
}
assertDashboardEquals(t, actual, expected)
}
func TestUpdateDashboard(t *testing.T) {
expected := getTestDashboard()
board, err := client.CreateDashboard(expected)
if err != nil {
t.Fatalf("Creating a dashboard failed when it shouldn't. (%s)", err)
}
defer cleanUpDashboard(t, board.Id)
board.Title = "___New-Test-Board___"
if err := client.UpdateDashboard(board); err != nil {
t.Fatalf("Updating a dashboard failed when it shouldn't: %s", err)
}
actual, err := client.GetDashboard(board.Id)
if err != nil {
t.Fatalf("Retrieving a dashboard failed when it shouldn't: %s", err)
}
assertDashboardEquals(t, actual, board)
}
func TestGetDashboards(t *testing.T) {
boards, err := client.GetDashboards()
if err != nil {
t.Fatalf("Retrieving dashboards failed when it shouldn't: %s", err)
}
num := len(boards)
board := createTestDashboard(t)
defer cleanUpDashboard(t, board.Id)
boards, err = client.GetDashboards()
if err != nil {
t.Fatalf("Retrieving dashboards failed when it shouldn't: %s", err)
}
if num+1 != len(boards) {
t.Fatalf("Number of dashboards didn't match expected: %d != %d", len(boards), num+1)
}
}
func getTestDashboard() *datadog.Dashboard {
return &datadog.Dashboard{
Title: "___Test-Board___",
Description: "Testboard description",
TemplateVariables: []datadog.TemplateVariable{},
Graphs: createGraph(),
}
}
func createTestDashboard(t *testing.T) *datadog.Dashboard {
board := getTestDashboard()
board, err := client.CreateDashboard(board)
if err != nil {
t.Fatalf("Creating a dashboard failed when it shouldn't: %s", err)
}
return board
}
func cleanUpDashboard(t *testing.T, id int) {
if err := client.DeleteDashboard(id); err != nil {
t.Fatalf("Deleting a dashboard failed when it shouldn't. Manual cleanup needed. (%s)", err)
}
deletedBoard, err := client.GetDashboard(id)
if deletedBoard != nil {
t.Fatal("Dashboard hasn't been deleted when it should have been. Manual cleanup needed.")
}
if err == nil {
t.Fatal("Fetching deleted dashboard didn't lead to an error. Manual cleanup needed.")
}
}
type TestGraphDefintionRequests struct {
Query string `json:"q"`
Stacked bool `json:"stacked"`
}
func createGraph() []datadog.Graph {
graphDefinition := datadog.Graph{}.Definition
graphDefinition.Viz = "timeseries"
r := datadog.Graph{}.Definition.Requests
graphDefinition.Requests = append(r, TestGraphDefintionRequests{Query: "avg:system.mem.free{*}", Stacked: false})
graph := datadog.Graph{Title: "Mandatory graph", Definition: graphDefinition}
graphs := []datadog.Graph{}
graphs = append(graphs, graph)
return graphs
}
func assertDashboardEquals(t *testing.T, actual, expected *datadog.Dashboard) {
if actual.Title != expected.Title {
t.Errorf("Dashboard title does not match: %s != %s", actual.Title, expected.Title)
}
if actual.Description != expected.Description {
t.Errorf("Dashboard description does not match: %s != %s", actual.Description, expected.Description)
}
if len(actual.Graphs) != len(expected.Graphs) {
t.Errorf("Number of Dashboard graphs does not match: %d != %d", len(actual.Graphs), len(expected.Graphs))
}
if len(actual.TemplateVariables) != len(expected.TemplateVariables) {
t.Errorf("Number of Dashboard template variables does not match: %d != %d", len(actual.TemplateVariables), len(expected.TemplateVariables))
}
}

View File

@ -0,0 +1,110 @@
package integration
import (
"github.com/stretchr/testify/assert"
"github.com/zorkian/go-datadog-api"
"testing"
)
func init() {
client = initTest()
}
func TestCreateAndDeleteDowntime(t *testing.T) {
expected := getTestDowntime()
// create the downtime and compare it
actual := createTestDowntime(t)
defer cleanUpDowntime(t, actual.Id)
// Set ID of our original struct to zero we we can easily compare the results
expected.Id = actual.Id
assert.Equal(t, expected, actual)
actual, err := client.GetDowntime(actual.Id)
if err != nil {
t.Fatalf("Retrieving a downtime failed when it shouldn't: (%s)", err)
}
assert.Equal(t, expected, actual)
}
func TestUpdateDowntime(t *testing.T) {
downtime := createTestDowntime(t)
downtime.Scope = []string{"env:downtime_test", "env:downtime_test2"}
defer cleanUpDowntime(t, downtime.Id)
if err := client.UpdateDowntime(downtime); err != nil {
t.Fatalf("Updating a downtime failed when it shouldn't: %s", err)
}
actual, err := client.GetDowntime(downtime.Id)
if err != nil {
t.Fatalf("Retrieving a downtime failed when it shouldn't: %s", err)
}
assert.Equal(t, downtime, actual)
}
func TestGetDowntime(t *testing.T) {
downtimes, err := client.GetDowntimes()
if err != nil {
t.Fatalf("Retrieving downtimes failed when it shouldn't: %s", err)
}
num := len(downtimes)
downtime := createTestDowntime(t)
defer cleanUpDowntime(t, downtime.Id)
downtimes, err = client.GetDowntimes()
if err != nil {
t.Fatalf("Retrieving downtimes failed when it shouldn't: %s", err)
}
if num+1 != len(downtimes) {
t.Fatalf("Number of downtimes didn't match expected: %d != %d", len(downtimes), num+1)
}
}
func getTestDowntime() *datadog.Downtime {
r := &datadog.Recurrence{
Type: "weeks",
Period: 1,
WeekDays: []string{"Mon", "Tue", "Wed", "Thu", "Fri"},
}
return &datadog.Downtime{
Message: "Test downtime message",
Scope: []string{"env:downtime_test"},
Start: 1577836800,
End: 1577840400,
Recurrence: r,
}
}
func createTestDowntime(t *testing.T) *datadog.Downtime {
downtime := getTestDowntime()
downtime, err := client.CreateDowntime(downtime)
if err != nil {
t.Fatalf("Creating a downtime failed when it shouldn't: %s", err)
}
return downtime
}
func cleanUpDowntime(t *testing.T, id int) {
if err := client.DeleteDowntime(id); err != nil {
t.Fatalf("Deleting a downtime failed when it shouldn't. Manual cleanup needed. (%s)", err)
}
deletedDowntime, err := client.GetDowntime(id)
if deletedDowntime != nil && deletedDowntime.Canceled == 0 {
t.Fatal("Downtime hasn't been deleted when it should have been. Manual cleanup needed.")
}
if err == nil && deletedDowntime.Canceled == 0 {
t.Fatal("Fetching deleted downtime didn't lead to an error and downtime Canceled not set.")
}
}

View File

@ -0,0 +1,152 @@
package integration
import (
"github.com/stretchr/testify/assert"
"github.com/zorkian/go-datadog-api"
"testing"
)
func init() {
client = initTest()
}
func TestCreateAndDeleteMonitor(t *testing.T) {
expected := getTestMonitor()
// create the monitor and compare it
actual := createTestMonitor(t)
defer cleanUpMonitor(t, actual.Id)
// Set ID of our original struct to zero we we can easily compare the results
expected.Id = actual.Id
assert.Equal(t, expected, actual)
actual, err := client.GetMonitor(actual.Id)
if err != nil {
t.Fatalf("Retrieving a monitor failed when it shouldn't: (%s)", err)
}
assert.Equal(t, expected, actual)
}
func TestUpdateMonitor(t *testing.T) {
monitor := createTestMonitor(t)
defer cleanUpMonitor(t, monitor.Id)
monitor.Name = "___New-Test-Monitor___"
if err := client.UpdateMonitor(monitor); err != nil {
t.Fatalf("Updating a monitor failed when it shouldn't: %s", err)
}
actual, err := client.GetMonitor(monitor.Id)
if err != nil {
t.Fatalf("Retrieving a monitor failed when it shouldn't: %s", err)
}
assert.Equal(t, monitor, actual)
}
func TestGetMonitor(t *testing.T) {
monitors, err := client.GetMonitors()
if err != nil {
t.Fatalf("Retrieving monitors failed when it shouldn't: %s", err)
}
num := len(monitors)
monitor := createTestMonitor(t)
defer cleanUpMonitor(t, monitor.Id)
monitors, err = client.GetMonitors()
if err != nil {
t.Fatalf("Retrieving monitors failed when it shouldn't: %s", err)
}
if num+1 != len(monitors) {
t.Fatalf("Number of monitors didn't match expected: %d != %d", len(monitors), num+1)
}
}
func TestMuteUnmuteMonitor(t *testing.T) {
monitor := createTestMonitor(t)
defer cleanUpMonitor(t, monitor.Id)
// Mute
err := client.MuteMonitor(monitor.Id)
if err != nil {
t.Fatalf("Failed to mute monitor")
}
monitor, err = client.GetMonitor(monitor.Id)
if err != nil {
t.Fatalf("Retrieving monitors failed when it shouldn't: %s", err)
}
// Mute without options will result in monitor.Options.Silenced
// to have a key of "*" with value 0
assert.Equal(t, 0, monitor.Options.Silenced["*"])
// Unmute
err = client.UnmuteMonitor(monitor.Id)
if err != nil {
t.Fatalf("Failed to unmute monitor")
}
// Update remote state
monitor, err = client.GetMonitor(monitor.Id)
if err != nil {
t.Fatalf("Retrieving monitors failed when it shouldn't: %s", err)
}
// Assert this map is empty
assert.Equal(t, 0, len(monitor.Options.Silenced))
}
/*
Testing of global mute and unmuting has not been added for following reasons:
* Disabling and enabling of global monitoring does an @all mention which is noisy
* It exposes risk to users that run integration tests in their main account
* There is no endpoint to verify success
*/
func getTestMonitor() *datadog.Monitor {
o := datadog.Options{
NotifyNoData: true,
NoDataTimeframe: 60,
Silenced: map[string]int{},
}
return &datadog.Monitor{
Message: "Test message",
Query: "avg(last_15m):avg:system.disk.in_use{*} by {host,device} > 0.8",
Name: "Test monitor",
Options: o,
Type: "metric alert",
}
}
func createTestMonitor(t *testing.T) *datadog.Monitor {
monitor := getTestMonitor()
monitor, err := client.CreateMonitor(monitor)
if err != nil {
t.Fatalf("Creating a monitor failed when it shouldn't: %s", err)
}
return monitor
}
func cleanUpMonitor(t *testing.T, id int) {
if err := client.DeleteMonitor(id); err != nil {
t.Fatalf("Deleting a monitor failed when it shouldn't. Manual cleanup needed. (%s)", err)
}
deletedMonitor, err := client.GetMonitor(id)
if deletedMonitor != nil {
t.Fatal("Monitor hasn't been deleted when it should have been. Manual cleanup needed.")
}
if err == nil {
t.Fatal("Fetching deleted monitor didn't lead to an error.")
}
}

View File

@ -0,0 +1,766 @@
package integration
import (
"testing"
"github.com/zorkian/go-datadog-api"
)
func TestAlertValueWidget(t *testing.T) {
board := createTestScreenboard(t)
defer cleanUpScreenboard(t, board.Id)
expected := datadog.Widget{}.AlertValueWidget
expected.X = 1
expected.Y = 1
expected.Width = 5
expected.Height = 5
expected.TitleText = "foo"
expected.TitleAlign = "center"
expected.TitleSize = 1
expected.Title = true
expected.TextSize = "auto"
expected.Precision = 2
expected.AlertId = 1
expected.Type = "alert_value"
expected.Unit = "auto"
expected.AddTimeframe = false
w := datadog.Widget{AlertValueWidget: expected}
board.Widgets = append(board.Widgets, w)
if err := client.UpdateScreenboard(board); err != nil {
t.Fatalf("Updating a screenboard failed: %s", err)
}
actual, err := client.GetScreenboard(board.Id)
if err != nil {
t.Fatalf("Retrieving a screenboard failed: %s", err)
}
actualWidget := actual.Widgets[0].AlertValueWidget
assertEquals(t, "x", actualWidget.X, expected.X)
assertEquals(t, "y", actualWidget.Y, expected.Y)
assertEquals(t, "height", actualWidget.Height, expected.Height)
assertEquals(t, "width", actualWidget.Width, expected.Width)
assertEquals(t, "title_text", actualWidget.TitleText, expected.TitleText)
assertEquals(t, "title_size", actualWidget.TitleSize, expected.TitleSize)
assertEquals(t, "title_align", actualWidget.TitleAlign, expected.TitleAlign)
assertEquals(t, "title", actualWidget.Title, expected.Title)
assertEquals(t, "text_size", actualWidget.TextSize, expected.TextSize)
assertEquals(t, "precision", actualWidget.Precision, expected.Precision)
assertEquals(t, "alert_id", actualWidget.AlertId, expected.AlertId)
assertEquals(t, "type", actualWidget.Type, expected.Type)
assertEquals(t, "unit", actualWidget.Unit, expected.Unit)
assertEquals(t, "add_timeframe", actualWidget.AddTimeframe, expected.AddTimeframe)
}
func TestChangeWidget(t *testing.T) {
board := createTestScreenboard(t)
defer cleanUpScreenboard(t, board.Id)
expected := datadog.Widget{}.ChangeWidget
expected.X = 1
expected.Y = 1
expected.Width = 5
expected.Height = 5
expected.TitleText = "foo"
expected.TitleAlign = "center"
expected.TitleSize = 1
expected.Title = true
expected.Aggregator = "min"
expected.TileDef = datadog.TileDef{}
w := datadog.Widget{ChangeWidget: expected}
board.Widgets = append(board.Widgets, w)
if err := client.UpdateScreenboard(board); err != nil {
t.Fatalf("Updating a screenboard failed: %s", err)
}
actual, err := client.GetScreenboard(board.Id)
if err != nil {
t.Fatalf("Retrieving a screenboard failed: %s", err)
}
actualWidget := actual.Widgets[0].ChangeWidget
assertEquals(t, "x", actualWidget.X, expected.X)
assertEquals(t, "y", actualWidget.Y, expected.Y)
assertEquals(t, "height", actualWidget.Height, expected.Height)
assertEquals(t, "width", actualWidget.Width, expected.Width)
assertEquals(t, "title_text", actualWidget.TitleText, expected.TitleText)
assertEquals(t, "title_size", actualWidget.TitleSize, expected.TitleSize)
assertEquals(t, "title_align", actualWidget.TitleAlign, expected.TitleAlign)
assertEquals(t, "title", actualWidget.Title, expected.Title)
assertEquals(t, "aggregator", actualWidget.Aggregator, expected.Aggregator)
assertTileDefEquals(t, actualWidget.TileDef, expected.TileDef)
}
func TestGraphWidget(t *testing.T) {
board := createTestScreenboard(t)
defer cleanUpScreenboard(t, board.Id)
expected := datadog.Widget{}.GraphWidget
expected.X = 1
expected.Y = 1
expected.Width = 5
expected.Height = 5
expected.TitleText = "foo"
expected.TitleAlign = "center"
expected.TitleSize = 1
expected.Title = true
expected.Timeframe = "1d"
expected.Type = "alert_graph"
expected.Legend = true
expected.LegendSize = 5
expected.TileDef = datadog.TileDef{}
w := datadog.Widget{GraphWidget: expected}
board.Widgets = append(board.Widgets, w)
if err := client.UpdateScreenboard(board); err != nil {
t.Fatalf("Updating a screenboard failed: %s", err)
}
actual, err := client.GetScreenboard(board.Id)
if err != nil {
t.Fatalf("Retrieving a screenboard failed: %s", err)
}
actualWidget := actual.Widgets[0].GraphWidget
assertEquals(t, "x", actualWidget.X, expected.X)
assertEquals(t, "y", actualWidget.Y, expected.Y)
assertEquals(t, "height", actualWidget.Height, expected.Height)
assertEquals(t, "width", actualWidget.Width, expected.Width)
assertEquals(t, "title_text", actualWidget.TitleText, expected.TitleText)
assertEquals(t, "title_size", actualWidget.TitleSize, expected.TitleSize)
assertEquals(t, "title_align", actualWidget.TitleAlign, expected.TitleAlign)
assertEquals(t, "title", actualWidget.Title, expected.Title)
assertEquals(t, "type", actualWidget.Type, expected.Type)
assertEquals(t, "timeframe", actualWidget.Timeframe, expected.Timeframe)
assertEquals(t, "legend", actualWidget.Legend, expected.Legend)
assertEquals(t, "legend_size", actualWidget.LegendSize, expected.LegendSize)
assertTileDefEquals(t, actualWidget.TileDef, expected.TileDef)
}
func TestEventTimelineWidget(t *testing.T) {
board := createTestScreenboard(t)
defer cleanUpScreenboard(t, board.Id)
expected := datadog.Widget{}.EventTimelineWidget
expected.X = 1
expected.Y = 1
expected.Width = 5
expected.Height = 5
expected.TitleText = "foo"
expected.TitleAlign = "center"
expected.TitleSize = 1
expected.Title = true
expected.Query = "avg:system.load.1{foo} by {bar}"
expected.Timeframe = "1d"
expected.Type = "alert_graph"
w := datadog.Widget{EventTimelineWidget: expected}
board.Widgets = append(board.Widgets, w)
if err := client.UpdateScreenboard(board); err != nil {
t.Fatalf("Updating a screenboard failed: %s", err)
}
actual, err := client.GetScreenboard(board.Id)
if err != nil {
t.Fatalf("Retrieving a screenboard failed: %s", err)
}
actualWidget := actual.Widgets[0].EventTimelineWidget
assertEquals(t, "x", actualWidget.X, expected.X)
assertEquals(t, "y", actualWidget.Y, expected.Y)
assertEquals(t, "height", actualWidget.Height, expected.Height)
assertEquals(t, "width", actualWidget.Width, expected.Width)
assertEquals(t, "title_text", actualWidget.TitleText, expected.TitleText)
assertEquals(t, "title_size", actualWidget.TitleSize, expected.TitleSize)
assertEquals(t, "title_align", actualWidget.TitleAlign, expected.TitleAlign)
assertEquals(t, "title", actualWidget.Title, expected.Title)
assertEquals(t, "type", actualWidget.Type, expected.Type)
assertEquals(t, "query", actualWidget.Query, expected.Query)
assertEquals(t, "timeframe", actualWidget.Timeframe, expected.Timeframe)
}
func TestAlertGraphWidget(t *testing.T) {
board := createTestScreenboard(t)
defer cleanUpScreenboard(t, board.Id)
expected := datadog.Widget{}.AlertGraphWidget
expected.X = 1
expected.Y = 1
expected.Width = 5
expected.Height = 5
expected.TitleText = "foo"
expected.TitleAlign = "center"
expected.TitleSize = 1
expected.Title = true
expected.VizType = ""
expected.Timeframe = "1d"
expected.AddTimeframe = false
expected.AlertId = 1
expected.Type = "alert_graph"
w := datadog.Widget{AlertGraphWidget: expected}
board.Widgets = append(board.Widgets, w)
if err := client.UpdateScreenboard(board); err != nil {
t.Fatalf("Updating a screenboard failed: %s", err)
}
actual, err := client.GetScreenboard(board.Id)
if err != nil {
t.Fatalf("Retrieving a screenboard failed: %s", err)
}
actualWidget := actual.Widgets[0].AlertGraphWidget
assertEquals(t, "x", actualWidget.X, expected.X)
assertEquals(t, "y", actualWidget.Y, expected.Y)
assertEquals(t, "height", actualWidget.Height, expected.Height)
assertEquals(t, "width", actualWidget.Width, expected.Width)
assertEquals(t, "title_text", actualWidget.TitleText, expected.TitleText)
assertEquals(t, "title_size", actualWidget.TitleSize, expected.TitleSize)
assertEquals(t, "title_align", actualWidget.TitleAlign, expected.TitleAlign)
assertEquals(t, "title", actualWidget.Title, expected.Title)
assertEquals(t, "type", actualWidget.Type, expected.Type)
assertEquals(t, "viz_type", actualWidget.VizType, expected.VizType)
assertEquals(t, "timeframe", actualWidget.Timeframe, expected.Timeframe)
assertEquals(t, "add_timeframe", actualWidget.AddTimeframe, expected.AddTimeframe)
assertEquals(t, "alert_id", actualWidget.AlertId, expected.AlertId)
}
func TestHostMapWidget(t *testing.T) {
board := createTestScreenboard(t)
defer cleanUpScreenboard(t, board.Id)
expected := datadog.Widget{}.HostMapWidget
expected.X = 1
expected.Y = 1
expected.Width = 5
expected.Height = 5
expected.TitleText = "foo"
expected.TitleAlign = "center"
expected.TitleSize = 1
expected.Title = true
expected.Type = "check_status"
expected.Query = "avg:system.load.1{foo} by {bar}"
expected.Timeframe = "1d"
expected.Legend = true
expected.LegendSize = 5
expected.TileDef = datadog.TileDef{}
w := datadog.Widget{HostMapWidget: expected}
board.Widgets = append(board.Widgets, w)
if err := client.UpdateScreenboard(board); err != nil {
t.Fatalf("Updating a screenboard failed: %s", err)
}
actual, err := client.GetScreenboard(board.Id)
if err != nil {
t.Fatalf("Retrieving a screenboard failed: %s", err)
}
actualWidget := actual.Widgets[0].HostMapWidget
assertEquals(t, "x", actualWidget.X, expected.X)
assertEquals(t, "y", actualWidget.Y, expected.Y)
assertEquals(t, "height", actualWidget.Height, expected.Height)
assertEquals(t, "width", actualWidget.Width, expected.Width)
assertEquals(t, "title_text", actualWidget.TitleText, expected.TitleText)
assertEquals(t, "title_size", actualWidget.TitleSize, expected.TitleSize)
assertEquals(t, "title_align", actualWidget.TitleAlign, expected.TitleAlign)
assertEquals(t, "title", actualWidget.Title, expected.Title)
assertEquals(t, "type", actualWidget.Type, expected.Type)
assertEquals(t, "query", actualWidget.Query, expected.Query)
assertEquals(t, "timeframe", actualWidget.Timeframe, expected.Timeframe)
assertEquals(t, "query", actualWidget.Query, expected.Query)
assertEquals(t, "legend", actualWidget.Legend, expected.Legend)
assertEquals(t, "legend_size", actualWidget.LegendSize, expected.LegendSize)
assertTileDefEquals(t, actualWidget.TileDef, expected.TileDef)
}
func TestCheckStatusWidget(t *testing.T) {
board := createTestScreenboard(t)
defer cleanUpScreenboard(t, board.Id)
expected := datadog.Widget{}.CheckStatusWidget
expected.X = 1
expected.Y = 1
expected.Width = 5
expected.Height = 5
expected.TitleText = "foo"
expected.TitleAlign = "center"
expected.TitleSize = 1
expected.Title = true
expected.Type = "check_status"
expected.Tags = "foo"
expected.Timeframe = "1d"
expected.Timeframe = "1d"
expected.Check = "datadog.agent.up"
expected.Group = "foo"
expected.Grouping = "check"
w := datadog.Widget{CheckStatusWidget: expected}
board.Widgets = append(board.Widgets, w)
if err := client.UpdateScreenboard(board); err != nil {
t.Fatalf("Updating a screenboard failed: %s", err)
}
actual, err := client.GetScreenboard(board.Id)
if err != nil {
t.Fatalf("Retrieving a screenboard failed: %s", err)
}
actualWidget := actual.Widgets[0].CheckStatusWidget
assertEquals(t, "x", actualWidget.X, expected.X)
assertEquals(t, "y", actualWidget.Y, expected.Y)
assertEquals(t, "height", actualWidget.Height, expected.Height)
assertEquals(t, "width", actualWidget.Width, expected.Width)
assertEquals(t, "title_text", actualWidget.TitleText, expected.TitleText)
assertEquals(t, "title_size", actualWidget.TitleSize, expected.TitleSize)
assertEquals(t, "title_align", actualWidget.TitleAlign, expected.TitleAlign)
assertEquals(t, "title", actualWidget.Title, expected.Title)
assertEquals(t, "type", actualWidget.Type, expected.Type)
assertEquals(t, "tags", actualWidget.Tags, expected.Tags)
assertEquals(t, "timeframe", actualWidget.Timeframe, expected.Timeframe)
assertEquals(t, "check", actualWidget.Check, expected.Check)
assertEquals(t, "group", actualWidget.Group, expected.Group)
assertEquals(t, "grouping", actualWidget.Grouping, expected.Grouping)
}
func TestIFrameWidget(t *testing.T) {
board := createTestScreenboard(t)
defer cleanUpScreenboard(t, board.Id)
expected := datadog.Widget{}.IFrameWidget
expected.X = 1
expected.Y = 1
expected.Width = 5
expected.Height = 5
expected.TitleText = "foo"
expected.TitleAlign = "center"
expected.TitleSize = 1
expected.Title = true
expected.Url = "http://www.example.com"
expected.Type = "iframe"
w := datadog.Widget{IFrameWidget: expected}
board.Widgets = append(board.Widgets, w)
if err := client.UpdateScreenboard(board); err != nil {
t.Fatalf("Updating a screenboard failed: %s", err)
}
actual, err := client.GetScreenboard(board.Id)
if err != nil {
t.Fatalf("Retrieving a screenboard failed: %s", err)
}
actualWidget := actual.Widgets[0].IFrameWidget
assertEquals(t, "x", actualWidget.X, expected.X)
assertEquals(t, "y", actualWidget.Y, expected.Y)
assertEquals(t, "height", actualWidget.Height, expected.Height)
assertEquals(t, "width", actualWidget.Width, expected.Width)
assertEquals(t, "title_text", actualWidget.TitleText, expected.TitleText)
assertEquals(t, "title_size", actualWidget.TitleSize, expected.TitleSize)
assertEquals(t, "title_align", actualWidget.TitleAlign, expected.TitleAlign)
assertEquals(t, "title", actualWidget.Title, expected.Title)
assertEquals(t, "url", actualWidget.Url, expected.Url)
assertEquals(t, "type", actualWidget.Type, expected.Type)
}
func TestNoteWidget(t *testing.T) {
board := createTestScreenboard(t)
defer cleanUpScreenboard(t, board.Id)
expected := datadog.Widget{}.NoteWidget
expected.X = 1
expected.Y = 1
expected.Width = 5
expected.Height = 5
expected.TitleText = "foo"
expected.TitleAlign = "center"
expected.TitleSize = 1
expected.Title = true
expected.Color = "green"
expected.FontSize = 5
expected.RefreshEvery = 60
expected.TickPos = "foo"
expected.TickEdge = "bar"
expected.Html = "<strong>baz</strong>"
expected.Tick = false
expected.Note = "quz"
expected.AutoRefresh = false
w := datadog.Widget{NoteWidget: expected}
board.Widgets = append(board.Widgets, w)
if err := client.UpdateScreenboard(board); err != nil {
t.Fatalf("Updating a screenboard failed: %s", err)
}
actual, err := client.GetScreenboard(board.Id)
if err != nil {
t.Fatalf("Retrieving a screenboard failed: %s", err)
}
actualWidget := actual.Widgets[0].NoteWidget
assertEquals(t, "x", actualWidget.X, expected.X)
assertEquals(t, "y", actualWidget.Y, expected.Y)
assertEquals(t, "height", actualWidget.Height, expected.Height)
assertEquals(t, "width", actualWidget.Width, expected.Width)
assertEquals(t, "title_text", actualWidget.TitleText, expected.TitleText)
assertEquals(t, "title_size", actualWidget.TitleSize, expected.TitleSize)
assertEquals(t, "title_align", actualWidget.TitleAlign, expected.TitleAlign)
assertEquals(t, "title", actualWidget.Title, expected.Title)
assertEquals(t, "color", actualWidget.Color, expected.Color)
assertEquals(t, "front_size", actualWidget.FontSize, expected.FontSize)
assertEquals(t, "refresh_every", actualWidget.RefreshEvery, expected.RefreshEvery)
assertEquals(t, "tick_pos", actualWidget.TickPos, expected.TickPos)
assertEquals(t, "tick_edge", actualWidget.TickEdge, expected.TickEdge)
assertEquals(t, "tick", actualWidget.Tick, expected.Tick)
assertEquals(t, "html", actualWidget.Html, expected.Html)
assertEquals(t, "note", actualWidget.Note, expected.Note)
assertEquals(t, "auto_refresh", actualWidget.AutoRefresh, expected.AutoRefresh)
}
func TestToplistWidget(t *testing.T) {
board := createTestScreenboard(t)
defer cleanUpScreenboard(t, board.Id)
expected := datadog.Widget{}.ToplistWidget
expected.X = 1
expected.Y = 1
expected.Width = 5
expected.Height = 5
expected.Type = "toplist"
expected.TitleText = "foo"
expected.TitleSize.Auto = false
expected.TitleSize.Size = 5
expected.TitleAlign = "center"
expected.Title = false
expected.Timeframe = "5m"
expected.Legend = false
expected.LegendSize = 5
w := datadog.Widget{ToplistWidget: expected}
board.Widgets = append(board.Widgets, w)
if err := client.UpdateScreenboard(board); err != nil {
t.Fatalf("Updating a screenboard failed: %s", err)
}
actual, err := client.GetScreenboard(board.Id)
if err != nil {
t.Fatalf("Retrieving a screenboard failed: %s", err)
}
actualWidget := actual.Widgets[0].ToplistWidget
assertEquals(t, "x", actualWidget.X, expected.X)
assertEquals(t, "y", actualWidget.Y, expected.Y)
assertEquals(t, "height", actualWidget.Height, expected.Height)
assertEquals(t, "width", actualWidget.Width, expected.Width)
assertEquals(t, "title_text", actualWidget.TitleText, expected.TitleText)
assertEquals(t, "title_size", actualWidget.TitleSize, expected.TitleSize)
assertEquals(t, "title_align", actualWidget.TitleAlign, expected.TitleAlign)
assertEquals(t, "legend", actualWidget.Legend, expected.Legend)
assertEquals(t, "legend_size", actualWidget.LegendSize, expected.LegendSize)
}
func TestEventSteamWidget(t *testing.T) {
board := createTestScreenboard(t)
defer cleanUpScreenboard(t, board.Id)
expected := datadog.Widget{}.EventStreamWidget
expected.EventSize = "1"
expected.Width = 1
expected.Height = 1
expected.X = 1
expected.Y = 1
expected.Query = "foo"
expected.Timeframe = "5w"
expected.Title = false
expected.TitleAlign = "center"
expected.TitleSize.Auto = false
expected.TitleSize.Size = 5
expected.TitleText = "bar"
expected.Type = "baz"
w := datadog.Widget{EventStreamWidget: expected}
board.Widgets = append(board.Widgets, w)
if err := client.UpdateScreenboard(board); err != nil {
t.Fatalf("Updating a screenboard failed: %s", err)
}
actual, err := client.GetScreenboard(board.Id)
if err != nil {
t.Fatalf("Retrieving a screenboard failed: %s", err)
}
actualWidget := actual.Widgets[0].EventStreamWidget
assertEquals(t, "event_size", actualWidget.EventSize, expected.EventSize)
assertEquals(t, "width", actualWidget.Width, expected.Width)
assertEquals(t, "height", actualWidget.Height, expected.Height)
assertEquals(t, "x", actualWidget.X, expected.X)
assertEquals(t, "y", actualWidget.Y, expected.Y)
assertEquals(t, "query", actualWidget.Query, expected.Query)
assertEquals(t, "timeframe", actualWidget.Timeframe, expected.Timeframe)
assertEquals(t, "title", actualWidget.Title, expected.Title)
assertEquals(t, "title_align", actualWidget.TitleAlign, expected.TitleAlign)
assertEquals(t, "title_size", actualWidget.TitleSize, expected.TitleSize)
assertEquals(t, "title_text", actualWidget.TitleText, expected.TitleText)
assertEquals(t, "type", actualWidget.Type, expected.Type)
}
func TestImageWidget(t *testing.T) {
board := createTestScreenboard(t)
defer cleanUpScreenboard(t, board.Id)
expected := datadog.Widget{}.ImageWidget
expected.Width = 1
expected.Height = 1
expected.X = 1
expected.Y = 1
expected.Title = false
expected.TitleAlign = "center"
expected.TitleSize.Auto = false
expected.TitleSize.Size = 5
expected.TitleText = "bar"
expected.Type = "baz"
expected.Url = "qux"
expected.Sizing = "quuz"
w := datadog.Widget{ImageWidget: expected}
board.Widgets = append(board.Widgets, w)
if err := client.UpdateScreenboard(board); err != nil {
t.Fatalf("Updating a screenboard failed: %s", err)
}
actual, err := client.GetScreenboard(board.Id)
if err != nil {
t.Fatalf("Retrieving a screenboard failed: %s", err)
}
actualWidget := actual.Widgets[0].ImageWidget
assertEquals(t, "width", actualWidget.Width, expected.Width)
assertEquals(t, "height", actualWidget.Height, expected.Height)
assertEquals(t, "x", actualWidget.X, expected.X)
assertEquals(t, "y", actualWidget.Y, expected.Y)
assertEquals(t, "title", actualWidget.Title, expected.Title)
assertEquals(t, "title_align", actualWidget.TitleAlign, expected.TitleAlign)
assertEquals(t, "title_size", actualWidget.TitleSize, expected.TitleSize)
assertEquals(t, "title_text", actualWidget.TitleText, expected.TitleText)
assertEquals(t, "type", actualWidget.Type, expected.Type)
assertEquals(t, "url", actualWidget.Url, expected.Url)
assertEquals(t, "sizing", actualWidget.Sizing, expected.Sizing)
}
func TestFreeTextWidget(t *testing.T) {
board := createTestScreenboard(t)
defer cleanUpScreenboard(t, board.Id)
expected := datadog.Widget{}.FreeTextWidget
expected.X = 1
expected.Y = 1
expected.Height = 10
expected.Width = 10
expected.Text = "Test"
expected.FontSize = "16"
expected.TextAlign = "center"
w := datadog.Widget{FreeTextWidget: expected}
board.Widgets = append(board.Widgets, w)
if err := client.UpdateScreenboard(board); err != nil {
t.Fatalf("Updating a screenboard failed: %s", err)
}
actual, err := client.GetScreenboard(board.Id)
if err != nil {
t.Fatalf("Retrieving a screenboard failed: %s", err)
}
actualWidget := actual.Widgets[0].FreeTextWidget
assertEquals(t, "font-size", actualWidget.FontSize, expected.FontSize)
assertEquals(t, "height", actualWidget.Height, expected.Height)
assertEquals(t, "width", actualWidget.Width, expected.Width)
assertEquals(t, "x", actualWidget.X, expected.X)
assertEquals(t, "y", actualWidget.Y, expected.Y)
assertEquals(t, "text", actualWidget.Text, expected.Text)
assertEquals(t, "text-align", actualWidget.TextAlign, expected.TextAlign)
assertEquals(t, "type", actualWidget.Type, expected.Type)
}
func TestTimeseriesWidget(t *testing.T) {
board := createTestScreenboard(t)
defer cleanUpScreenboard(t, board.Id)
expected := datadog.Widget{}.TimeseriesWidget
expected.X = 1
expected.Y = 1
expected.Width = 20
expected.Height = 30
expected.Title = true
expected.TitleAlign = "centre"
expected.TitleSize = datadog.TextSize{Size: 16}
expected.TitleText = "Test"
expected.Timeframe = "1m"
w := datadog.Widget{TimeseriesWidget: expected}
board.Widgets = append(board.Widgets, w)
if err := client.UpdateScreenboard(board); err != nil {
t.Fatalf("Updating a screenboard failed: %s", err)
}
actual, err := client.GetScreenboard(board.Id)
if err != nil {
t.Fatalf("Retrieving a screenboard failed: %s", err)
}
actualWidget := actual.Widgets[0].TimeseriesWidget
assertEquals(t, "height", actualWidget.Height, expected.Height)
assertEquals(t, "width", actualWidget.Width, expected.Width)
assertEquals(t, "x", actualWidget.X, expected.X)
assertEquals(t, "y", actualWidget.Y, expected.Y)
assertEquals(t, "title", actualWidget.Title, expected.Title)
assertEquals(t, "title-align", actualWidget.TitleAlign, expected.TitleAlign)
assertEquals(t, "title-size.size", actualWidget.TitleSize.Size, expected.TitleSize.Size)
assertEquals(t, "title-size.auto", actualWidget.TitleSize.Auto, expected.TitleSize.Auto)
assertEquals(t, "title-text", actualWidget.TitleText, expected.TitleText)
assertEquals(t, "type", actualWidget.Type, expected.Type)
assertEquals(t, "timeframe", actualWidget.Timeframe, expected.Timeframe)
assertEquals(t, "legend", actualWidget.Legend, expected.Legend)
assertTileDefEquals(t, actualWidget.TileDef, expected.TileDef)
}
func TestQueryValueWidget(t *testing.T) {
board := createTestScreenboard(t)
defer cleanUpScreenboard(t, board.Id)
expected := datadog.Widget{}.QueryValueWidget
expected.X = 1
expected.Y = 1
expected.Width = 20
expected.Height = 30
expected.Title = true
expected.TitleAlign = "centre"
expected.TitleSize = datadog.TextSize{Size: 16}
expected.TitleText = "Test"
expected.Timeframe = "1m"
expected.TimeframeAggregator = "sum"
expected.Aggregator = "min"
expected.Query = "docker.containers.running"
expected.MetricType = "standard"
/* TODO: add test for conditional formats
"conditional_formats": [{
"comparator": ">",
"color": "white_on_red",
"custom_bg_color": null,
"value": 1,
"invert": false,
"custom_fg_color": null}],
*/
expected.IsValidQuery = true
expected.ResultCalcFunc = "raw"
expected.Aggregator = "avg"
expected.CalcFunc = "raw"
w := datadog.Widget{QueryValueWidget: expected}
board.Widgets = append(board.Widgets, w)
if err := client.UpdateScreenboard(board); err != nil {
t.Fatalf("Updating a screenboard failed: %s", err)
}
actual, err := client.GetScreenboard(board.Id)
if err != nil {
t.Fatalf("Retrieving a screenboard failed: %s", err)
}
actualWidget := actual.Widgets[0].QueryValueWidget
assertEquals(t, "height", actualWidget.Height, expected.Height)
assertEquals(t, "width", actualWidget.Width, expected.Width)
assertEquals(t, "x", actualWidget.X, expected.X)
assertEquals(t, "y", actualWidget.Y, expected.Y)
assertEquals(t, "title", actualWidget.Title, expected.Title)
assertEquals(t, "title-align", actualWidget.TitleAlign, expected.TitleAlign)
assertEquals(t, "title-size.size", actualWidget.TitleSize.Size, expected.TitleSize.Size)
assertEquals(t, "title-size.auto", actualWidget.TitleSize.Auto, expected.TitleSize.Auto)
assertEquals(t, "title-text", actualWidget.TitleText, expected.TitleText)
assertEquals(t, "type", actualWidget.Type, expected.Type)
assertEquals(t, "timeframe", actualWidget.Timeframe, expected.Timeframe)
assertEquals(t, "timeframe-aggregator", actualWidget.TimeframeAggregator, expected.TimeframeAggregator)
assertEquals(t, "aggregator", actualWidget.Aggregator, expected.Aggregator)
assertEquals(t, "query", actualWidget.Query, expected.Query)
assertEquals(t, "is_valid_query", actualWidget.IsValidQuery, expected.IsValidQuery)
assertEquals(t, "res_calc_func", actualWidget.ResultCalcFunc, expected.ResultCalcFunc)
assertEquals(t, "aggr", actualWidget.Aggregator, expected.Aggregator)
}
func assertTileDefEquals(t *testing.T, actual datadog.TileDef, expected datadog.TileDef) {
assertEquals(t, "num-events", len(actual.Events), len(expected.Events))
assertEquals(t, "num-requests", len(actual.Requests), len(expected.Requests))
assertEquals(t, "viz", actual.Viz, expected.Viz)
for i, event := range actual.Events {
assertEquals(t, "event-query", event.Query, expected.Events[i].Query)
}
for i, request := range actual.Requests {
assertEquals(t, "request-query", request.Query, expected.Requests[i].Query)
assertEquals(t, "request-type", request.Type, expected.Requests[i].Type)
}
}
func assertEquals(t *testing.T, attribute string, a, b interface{}) {
if a != b {
t.Errorf("The two %s values '%v' and '%v' are not equal", attribute, a, b)
}
}

View File

@ -0,0 +1,143 @@
package integration
import (
"github.com/zorkian/go-datadog-api"
"testing"
)
func init() {
client = initTest()
}
func TestCreateAndDeleteScreenboard(t *testing.T) {
expected := getTestScreenboard()
// create the screenboard and compare it
actual, err := client.CreateScreenboard(expected)
if err != nil {
t.Fatalf("Creating a screenboard failed when it shouldn't. (%s)", err)
}
defer cleanUpScreenboard(t, actual.Id)
assertScreenboardEquals(t, actual, expected)
// now try to fetch it freshly and compare it again
actual, err = client.GetScreenboard(actual.Id)
if err != nil {
t.Fatalf("Retrieving a screenboard failed when it shouldn't. (%s)", err)
}
assertScreenboardEquals(t, actual, expected)
}
func TestShareAndRevokeScreenboard(t *testing.T) {
expected := getTestScreenboard()
// create the screenboard
actual, err := client.CreateScreenboard(expected)
if err != nil {
t.Fatalf("Creating a screenboard failed when it shouldn't: %s", err)
}
defer cleanUpScreenboard(t, actual.Id)
// share screenboard and verify it was shared
var response datadog.ScreenShareResponse
err = client.ShareScreenboard(actual.Id, &response)
if err != nil {
t.Fatalf("Failed to share screenboard: %s", err)
}
// revoke screenboard
err = client.RevokeScreenboard(actual.Id)
if err != nil {
t.Fatalf("Failed to revoke sharing of screenboard: %s", err)
}
}
func TestUpdateScreenboard(t *testing.T) {
board := createTestScreenboard(t)
defer cleanUpScreenboard(t, board.Id)
board.Title = "___New-Test-Board___"
if err := client.UpdateScreenboard(board); err != nil {
t.Fatalf("Updating a screenboard failed when it shouldn't: %s", err)
}
actual, err := client.GetScreenboard(board.Id)
if err != nil {
t.Fatalf("Retrieving a screenboard failed when it shouldn't: %s", err)
}
assertScreenboardEquals(t, actual, board)
}
func TestGetScreenboards(t *testing.T) {
boards, err := client.GetScreenboards()
if err != nil {
t.Fatalf("Retrieving screenboards failed when it shouldn't: %s", err)
}
num := len(boards)
board := createTestScreenboard(t)
defer cleanUpScreenboard(t, board.Id)
boards, err = client.GetScreenboards()
if err != nil {
t.Fatalf("Retrieving screenboards failed when it shouldn't: %s", err)
}
if num+1 != len(boards) {
t.Fatalf("Number of screenboards didn't match expected: %d != %d", len(boards), num+1)
}
}
func getTestScreenboard() *datadog.Screenboard {
return &datadog.Screenboard{
Title: "___Test-Board___",
Height: "600",
Width: "800",
Widgets: []datadog.Widget{},
}
}
func createTestScreenboard(t *testing.T) *datadog.Screenboard {
board := getTestScreenboard()
board, err := client.CreateScreenboard(board)
if err != nil {
t.Fatalf("Creating a screenboard failed when it shouldn't: %s", err)
}
return board
}
func cleanUpScreenboard(t *testing.T, id int) {
if err := client.DeleteScreenboard(id); err != nil {
t.Fatalf("Deleting a screenboard failed when it shouldn't. Manual cleanup needed. (%s)", err)
}
deletedBoard, err := client.GetScreenboard(id)
if deletedBoard != nil {
t.Fatal("Screenboard hasn't been deleted when it should have been. Manual cleanup needed.")
}
if err == nil {
t.Fatal("Fetching deleted screenboard didn't lead to an error. Manual cleanup needed.")
}
}
func assertScreenboardEquals(t *testing.T, actual, expected *datadog.Screenboard) {
if actual.Title != expected.Title {
t.Errorf("Screenboard title does not match: %s != %s", actual.Title, expected.Title)
}
if actual.Width != expected.Width {
t.Errorf("Screenboard width does not match: %s != %s", actual.Width, expected.Width)
}
if actual.Height != expected.Height {
t.Errorf("Screenboard width does not match: %s != %s", actual.Height, expected.Height)
}
if len(actual.Widgets) != len(expected.Widgets) {
t.Errorf("Number of Screenboard widgets does not match: %d != %d", len(actual.Widgets), len(expected.Widgets))
}
}

View File

@ -0,0 +1,24 @@
package integration
import (
"github.com/zorkian/go-datadog-api"
"log"
"os"
)
var (
apiKey string
appKey string
client *datadog.Client
)
func initTest() *datadog.Client {
apiKey = os.Getenv("DATADOG_API_KEY")
appKey = os.Getenv("DATADOG_APP_KEY")
if apiKey == "" || appKey == "" {
log.Fatal("Please make sure to set the env variables 'DATADOG_API_KEY' and 'DATADOG_APP_KEY' before running this test")
}
return datadog.NewClient(apiKey, appKey)
}

30
vendor/github.com/zorkian/go-datadog-api/main.go generated vendored Normal file
View File

@ -0,0 +1,30 @@
/*
* Datadog API for Go
*
* Please see the included LICENSE file for licensing information.
*
* Copyright 2013 by authors and contributors.
*/
package datadog
import "net/http"
// Client is the object that handles talking to the Datadog API. This maintains
// state information for a particular application connection.
type Client struct {
apiKey, appKey string
//The Http Client that is used to make requests
HttpClient *http.Client
}
// NewClient returns a new datadog.Client which can be used to access the API
// methods. The expected argument is the API key.
func NewClient(apiKey, appKey string) *Client {
return &Client{
apiKey: apiKey,
appKey: appKey,
HttpClient: http.DefaultClient,
}
}

113
vendor/github.com/zorkian/go-datadog-api/monitors.go generated vendored Normal file
View File

@ -0,0 +1,113 @@
/*
* Datadog API for Go
*
* Please see the included LICENSE file for licensing information.
*
* Copyright 2013 by authors and contributors.
*/
package datadog
import (
"encoding/json"
"fmt"
)
type ThresholdCount struct {
Ok json.Number `json:"ok,omitempty"`
Critical json.Number `json:"critical,omitempty"`
Warning json.Number `json:"warning,omitempty"`
}
type Options struct {
NoDataTimeframe int `json:"no_data_timeframe,omitempty"`
NotifyAudit bool `json:"notify_audit,omitempty"`
NotifyNoData bool `json:"notify_no_data,omitempty"`
RenotifyInterval int `json:"renotify_interval,omitempty"`
Silenced map[string]int `json:"silenced,omitempty"`
TimeoutH int `json:"timeout_h,omitempty"`
EscalationMessage string `json:"escalation_message,omitempty"`
Thresholds ThresholdCount `json:"thresholds,omitempty"`
IncludeTags bool `json:"include_tags,omitempty"`
}
//Monitors allow you to watch a metric or check that you care about,
//notifying your team when some defined threshold is exceeded.
type Monitor struct {
Id int `json:"id,omitempty"`
Type string `json:"type,omitempty"`
Query string `json:"query,omitempty"`
Name string `json:"name,omitempty"`
Message string `json:"message,omitempty"`
Tags []string `json:"tags,omitempty"`
Options Options `json:"options,omitempty"`
}
// reqMonitors receives a slice of all monitors
type reqMonitors struct {
Monitors []Monitor `json:"monitors,omitempty"`
}
// Createmonitor adds a new monitor to the system. This returns a pointer to an
// monitor so you can pass that to Updatemonitor later if needed.
func (self *Client) CreateMonitor(monitor *Monitor) (*Monitor, error) {
var out Monitor
err := self.doJsonRequest("POST", "/v1/monitor", monitor, &out)
if err != nil {
return nil, err
}
return &out, nil
}
// Updatemonitor takes an monitor that was previously retrieved through some method
// and sends it back to the server.
func (self *Client) UpdateMonitor(monitor *Monitor) error {
return self.doJsonRequest("PUT", fmt.Sprintf("/v1/monitor/%d", monitor.Id),
monitor, nil)
}
// Getmonitor retrieves an monitor by identifier.
func (self *Client) GetMonitor(id int) (*Monitor, error) {
var out Monitor
err := self.doJsonRequest("GET", fmt.Sprintf("/v1/monitor/%d", id), nil, &out)
if err != nil {
return nil, err
}
return &out, nil
}
// Deletemonitor removes an monitor from the system.
func (self *Client) DeleteMonitor(id int) error {
return self.doJsonRequest("DELETE", fmt.Sprintf("/v1/monitor/%d", id),
nil, nil)
}
// GetMonitors returns a slice of all monitors.
func (self *Client) GetMonitors() ([]Monitor, error) {
var out reqMonitors
err := self.doJsonRequest("GET", "/v1/monitor", nil, &out.Monitors)
if err != nil {
return nil, err
}
return out.Monitors, nil
}
// MuteMonitors turns off monitoring notifications.
func (self *Client) MuteMonitors() error {
return self.doJsonRequest("POST", "/v1/monitor/mute_all", nil, nil)
}
// UnmuteMonitors turns on monitoring notifications.
func (self *Client) UnmuteMonitors() error {
return self.doJsonRequest("POST", "/v1/monitor/unmute_all", nil, nil)
}
// MuteMonitor turns off monitoring notifications for a monitor.
func (self *Client) MuteMonitor(id int) error {
return self.doJsonRequest("POST", fmt.Sprintf("/v1/monitor/%d/mute", id), nil, nil)
}
// UnmuteMonitor turns on monitoring notifications for a monitor.
func (self *Client) UnmuteMonitor(id int) error {
return self.doJsonRequest("POST", fmt.Sprintf("/v1/monitor/%d/unmute", id), nil, nil)
}

131
vendor/github.com/zorkian/go-datadog-api/request.go generated vendored Normal file
View File

@ -0,0 +1,131 @@
/*
* Datadog API for Go
*
* Please see the included LICENSE file for licensing information.
*
* Copyright 2013 by authors and contributors.
*/
package datadog
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"strings"
"time"
"github.com/cenkalti/backoff"
)
// uriForAPI is to be called with something like "/v1/events" and it will give
// the proper request URI to be posted to.
func (self *Client) uriForAPI(api string) string {
url := os.Getenv("DATADOG_HOST")
if url == "" {
url = "https://app.datadoghq.com"
}
if strings.Index(api, "?") > -1 {
return url + "/api" + api + "&api_key=" +
self.apiKey + "&application_key=" + self.appKey
} else {
return url + "/api" + api + "?api_key=" +
self.apiKey + "&application_key=" + self.appKey
}
}
// doJsonRequest is the simplest type of request: a method on a URI that returns
// some JSON result which we unmarshal into the passed interface.
func (self *Client) doJsonRequest(method, api string,
reqbody, out interface{}) error {
// Handle the body if they gave us one.
var bodyreader io.Reader
if method != "GET" && reqbody != nil {
bjson, err := json.Marshal(reqbody)
if err != nil {
return err
}
bodyreader = bytes.NewReader(bjson)
}
req, err := http.NewRequest(method, self.uriForAPI(api), bodyreader)
if err != nil {
return err
}
if bodyreader != nil {
req.Header.Add("Content-Type", "application/json")
}
// Perform the request and retry it if it's not a POST request
var resp *http.Response
if method == "POST" {
resp, err = self.HttpClient.Do(req)
} else {
resp, err = self.doRequestWithRetries(req, 60*time.Second)
}
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode > 299 {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
return fmt.Errorf("API error %s: %s", resp.Status, body)
}
// If they don't care about the body, then we don't care to give them one,
// so bail out because we're done.
if out == nil {
return nil
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
// If we got no body, by default let's just make an empty JSON dict. This
// saves us some work in other parts of the code.
if len(body) == 0 {
body = []byte{'{', '}'}
}
err = json.Unmarshal(body, &out)
if err != nil {
return err
}
return nil
}
// doRequestWithRetries performs an HTTP request repeatedly for maxTime or until
// no error and no HTTP response code higher than 299 is returned.
func (self *Client) doRequestWithRetries(req *http.Request, maxTime time.Duration) (*http.Response, error) {
var (
err error
resp *http.Response
bo = backoff.NewExponentialBackOff()
)
bo.MaxElapsedTime = maxTime
err = backoff.Retry(func() error {
resp, err = self.HttpClient.Do(req)
if err != nil {
return err
}
if resp.StatusCode < 200 || resp.StatusCode > 299 {
return errors.New("API error: " + resp.Status)
}
return nil
}, bo)
return resp, err
}

View File

@ -0,0 +1,287 @@
package datadog
type TextSize struct {
Size int
Auto bool
}
type TileDef struct {
Events []TileDefEvent `json:"events,omitempty"`
Markers []TimeseriesMarker `json:"markers,omitempty"`
Requests []TimeseriesRequest `json:"requests,omitempty"`
Viz string `json:"viz,omitempty"`
}
type TimeseriesRequest struct {
Query string `json:"q,omitempty"`
Type string `json:"type,omitempty"`
ConditionalFormats []ConditionalFormat `json:"conditional_formats,omitempty"`
Style TimeseriesRequestStyle `json:"style,omitempty"`
}
type TimeseriesRequestStyle struct {
Palette string `json:"palette,omitempty"`
}
type TimeseriesMarker struct {
Label string `json:"label,omitempty"`
Type string `json:"type,omitempty"`
Value string `json:"value,omitempty"`
}
type TileDefEvent struct {
Query string `json:"q"`
}
type AlertValueWidget struct {
TitleSize int `json:"title_size,omitempty"`
Title bool `json:"title,omitempty"`
TitleAlign string `json:"title_align,omitempty"`
TextAlign string `json:"text_align,omitempty"`
TitleText string `json:"title_text,omitempty"`
Precision int `json:"precision,omitempty"`
AlertId int `json:"alert_id,omitempty"`
Timeframe string `json:"timeframe,omitempty"`
AddTimeframe bool `json:"add_timeframe,omitempty"`
Y int `json:"y,omitempty"`
X int `json:"x,omitempty"`
TextSize string `json:"text_size,omitempty"`
Height int `json:"height,omitempty"`
Width int `json:"width,omitempty"`
Type string `json:"type,omitempty"`
Unit string `json:"unit,omitempty"`
}
type ChangeWidget struct {
TitleSize int `json:"title_size,omitempty"`
Title bool `json:"title,omitempty"`
TitleAlign string `json:"title_align,omitempty"`
TitleText string `json:"title_text,omitempty"`
Height int `json:"height,omitempty"`
Width int `json:"width,omitempty"`
X int `json:"y,omitempty"`
Y int `json:"x,omitempty"`
Aggregator string `json:"aggregator,omitempty"`
TileDef TileDef `json:"tile_def,omitempty"`
}
type GraphWidget struct {
TitleSize int `json:"title_size,omitempty"`
Title bool `json:"title,omitempty"`
TitleAlign string `json:"title_align,omitempty"`
TitleText string `json:"title_text,omitempty"`
Height int `json:"height,omitempty"`
Width int `json:"width,omitempty"`
X int `json:"y,omitempty"`
Y int `json:"x,omitempty"`
Type string `json:"type,omitempty"`
Timeframe string `json:"timeframe,omitempty"`
LegendSize int `json:"legend_size,omitempty"`
Legend bool `json:"legend,omitempty"`
TileDef TileDef `json:"tile_def,omitempty"`
}
type EventTimelineWidget struct {
TitleSize int `json:"title_size,omitempty"`
Title bool `json:"title,omitempty"`
TitleAlign string `json:"title_align,omitempty"`
TitleText string `json:"title_text,omitempty"`
Height int `json:"height,omitempty"`
Width int `json:"width,omitempty"`
X int `json:"y,omitempty"`
Y int `json:"x,omitempty"`
Type string `json:"type,omitempty"`
Timeframe string `json:"timeframe,omitempty"`
Query string `json:"query,omitempty"`
}
type AlertGraphWidget struct {
TitleSize int `json:"title_size,omitempty"`
VizType string `json:"timeseries,omitempty"`
Title bool `json:"title,omitempty"`
TitleAlign string `json:"title_align,omitempty"`
TitleText string `json:"title_text,omitempty"`
Height int `json:"height,omitempty"`
Width int `json:"width,omitempty"`
X int `json:"y,omitempty"`
Y int `json:"x,omitempty"`
AlertId int `json:"alert_id,omitempty"`
Timeframe string `json:"timeframe,omitempty"`
Type string `json:"type,omitempty"`
AddTimeframe bool `json:"add_timeframe,omitempty"`
}
type HostMapWidget struct {
TitleSize int `json:"title_size,omitempty"`
Title bool `json:"title,omitempty"`
TitleAlign string `json:"title_align,omitempty"`
TitleText string `json:"title_text,omitempty"`
Height int `json:"height,omitempty"`
Width int `json:"width,omitempty"`
X int `json:"y,omitempty"`
Y int `json:"x,omitempty"`
Query string `json:"query,omitempty"`
Timeframe string `json:"timeframe,omitempty"`
LegendSize int `json:"legend_size,omitempty"`
Type string `json:"type,omitempty"`
Legend bool `json:"legend,omitempty"`
TileDef TileDef `json:"tile_def,omitempty"`
}
type CheckStatusWidget struct {
TitleSize int `json:"title_size,omitempty"`
Title bool `json:"title,omitempty"`
TitleAlign string `json:"title_align,omitempty"`
TextAlign string `json:"text_align,omitempty"`
TitleText string `json:"title_text,omitempty"`
Height int `json:"height,omitempty"`
Width int `json:"width,omitempty"`
X int `json:"y,omitempty"`
Y int `json:"x,omitempty"`
Tags string `json:"tags,omitempty"`
Timeframe string `json:"timeframe,omitempty"`
TextSize string `json:"text_size,omitempty"`
Type string `json:"type,omitempty"`
Check string `json:"check,omitempty"`
Group string `json:"group,omitempty"`
Grouping string `json:"grouping,omitempty"`
}
type IFrameWidget struct {
TitleSize int `json:"title_size,omitempty"`
Title bool `json:"title,omitempty"`
Url string `json:"url,omitempty"`
TitleAlign string `json:"title_align,omitempty"`
TitleText string `json:"title_text,omitempty"`
Height int `json:"height,omitempty"`
Width int `json:"width,omitempty"`
X int `json:"y,omitempty"`
Y int `json:"x,omitempty"`
Type string `json:"type,omitempty"`
}
type NoteWidget struct {
TitleSize int `json:"title_size,omitempty"`
Title bool `json:"title,omitempty"`
RefreshEvery int `json:"refresh_every,omitempty"`
TickPos string `json:"tick_pos,omitempty"`
TitleAlign string `json:"title_align,omitempty"`
TickEdge string `json:"tick_edge,omitempty"`
TextAlign string `json:"text_align,omitempty"`
TitleText string `json:"title_text,omitempty"`
Height int `json:"height,omitempty"`
Color string `json:"bgcolor,omitempty"`
Html string `json:"html,omitempty"`
Y int `json:"y,omitempty"`
X int `json:"x,omitempty"`
FontSize int `json:"font_size,omitempty"`
Tick bool `json:"tick,omitempty"`
Note string `json:"type,omitempty"`
Width int `json:"width,omitempty"`
AutoRefresh bool `json:"auto_refresh,omitempty"`
}
type TimeseriesWidget struct {
Height int `json:"height,omitempty"`
Legend bool `json:"legend,omitempty"`
TileDef TileDef `json:"tile_def,omitempty"`
Timeframe string `json:"timeframe,omitempty"`
Title bool `json:"title,omitempty"`
TitleAlign string `json:"title_align,omitempty"`
TitleSize TextSize `json:"title_size,omitempty"`
TitleText string `json:"title_text,omitempty"`
Type string `json:"type,omitempty"`
Width int `json:"width,omitempty"`
X int `json:"x,omitempty"`
Y int `json:"y,omitempty"`
}
type QueryValueWidget struct {
Timeframe string `json:"timeframe,omitempty"`
TimeframeAggregator string `json:"aggr,omitempty"`
Aggregator string `json:"aggregator,omitempty"`
CalcFunc string `json:"calc_func,omitempty"`
ConditionalFormats []ConditionalFormat `json:"conditional_formats,omitempty"`
Height int `json:"height,omitempty"`
IsValidQuery bool `json:"is_valid_query,omitempty,omitempty"`
Metric string `json:"metric,omitempty"`
MetricType string `json:"metric_type,omitempty"`
Precision int `json:"precision,omitempty"`
Query string `json:"query,omitempty"`
ResultCalcFunc string `json:"res_calc_func,omitempty"`
Tags []string `json:"tags,omitempty"`
TextAlign string `json:"text_align,omitempty"`
TextSize TextSize `json:"text_size,omitempty"`
Title bool `json:"title,omitempty"`
TitleAlign string `json:"title_align,omitempty"`
TitleSize TextSize `json:"title_size,omitempty"`
TitleText string `json:"title_text,omitempty"`
Type string `json:"type,omitempty"`
Unit string `json:"auto,omitempty"`
Width int `json:"width,omitempty"`
X int `json:"x,omitempty"`
Y int `json:"y,omitempty"`
}
type ConditionalFormat struct {
Color string `json:"color,omitempty"`
Comparator string `json:"comparator,omitempty"`
Inverted bool `json:"invert,omitempty"`
Value int `json:"value,omitempty"`
}
type ToplistWidget struct {
Height int `json:"height,omitempty"`
Legend bool `json:"legend,omitempty"`
LegendSize int `json:"legend_size,omitempty"`
TileDef TileDef `json:"tile_def,omitempty"`
Timeframe string `json:"timeframe,omitempty"`
Title bool `json:"title,omitempty"`
TitleAlign string `json:"title_align,omitempty"`
TitleSize TextSize `json:"title_size,omitempty"`
TitleText string `json:"title_text,omitempty"`
Type string `json:"type,omitempty"`
Width int `json:"width,omitempty"`
X int `json:"x,omitempty"`
Y int `json:"y,omitempty"`
}
type EventStreamWidget struct {
EventSize string `json:"event_size,omitempty"`
Height int `json:"height,omitempty"`
Query string `json:"query,omitempty"`
Timeframe string `json:"timeframe,omitempty"`
Title bool `json:"title,omitempty"`
TitleAlign string `json:"title_align,omitempty"`
TitleSize TextSize `json:"title_size,omitempty"`
TitleText string `json:"title_text,omitempty"`
Type string `json:"type,omitempty"`
Width int `json:"width,omitempty"`
X int `json:"x,omitempty"`
Y int `json:"y,omitempty"`
}
type FreeTextWidget struct {
Color string `json:"color,omitempty"`
FontSize string `json:"font_size,omitempty"`
Height int `json:"height,omitempty"`
Text string `json:"text,omitempty"`
TextAlign string `json:"text_align,omitempty"`
Type string `json:"type,omitempty"`
Width int `json:"width,omitempty"`
X int `json:"x,omitempty"`
Y int `json:"y,omitempty"`
}
type ImageWidget struct {
Height int `json:"height,omitempty"`
Sizing string `json:"sizing,omitempty"`
Title bool `json:"title,omitempty"`
TitleAlign string `json:"title_align,omitempty"`
TitleSize TextSize `json:"title_size,omitempty"`
TitleText string `json:"title_text,omitempty"`
Type string `json:"type,omitempty"`
Url string `json:"url,omitempty"`
Width int `json:"width,omitempty"`
X int `json:"x,omitempty"`
Y int `json:"y,omitempty"`
}

View File

@ -0,0 +1,117 @@
/*
* Datadog API for Go
*
* Please see the included LICENSE file for licensing information.
*
* Copyright 2013 by authors and contributors.
*/
package datadog
import (
"fmt"
)
// Screenboard represents a user created screenboard. This is the full screenboard
// struct when we load a screenboard in detail.
type Screenboard struct {
Id int `json:"id,omitempty"`
Title string `json:"board_title,omitempty"`
Height string `json:"height,omitempty"`
Width string `json:"width,omitempty"`
Shared bool `json:"shared,omitempty"`
Templated bool `json:"templated,omitempty"`
TemplateVariables []TemplateVariable `json:"template_variables,omitempty"`
Widgets []Widget `json:"widgets,omitempty"`
}
//type Widget struct {
type Widget struct {
Default string `json:"default,omitempty"`
Name string `json:"name,omitempty"`
Prefix string `json:"prefix,omitempty"`
TimeseriesWidget TimeseriesWidget `json:"timeseries,omitempty"`
QueryValueWidget QueryValueWidget `json:"query_value,omitempty"`
EventStreamWidget EventStreamWidget `json:"event_stream,omitempty"`
FreeTextWidget FreeTextWidget `json:"free_text,omitempty"`
ToplistWidget ToplistWidget `json:"toplist,omitempty"`
ImageWidget ImageWidget `json:"image,omitempty"`
ChangeWidget ChangeWidget `json:"change,omitempty"`
GraphWidget GraphWidget `json:"graph,omitempty"`
EventTimelineWidget EventTimelineWidget `json:"event_timeline,omitempty"`
AlertValueWidget AlertValueWidget `json:"alert_value,omitempty"`
AlertGraphWidget AlertGraphWidget `json:"alert_graph,omitempty"`
HostMapWidget HostMapWidget `json:"hostmap,omitempty"`
CheckStatusWidget CheckStatusWidget `json:"check_status,omitempty"`
IFrameWidget IFrameWidget `json:"iframe,omitempty"`
NoteWidget NoteWidget `json:"frame,omitempty"`
}
// ScreenboardLite represents a user created screenboard. This is the mini
// struct when we load the summaries.
type ScreenboardLite struct {
Id int `json:"id,omitempty"`
Resource string `json:"resource,omitempty"`
Title string `json:"title,omitempty"`
}
// reqGetScreenboards from /api/v1/screen
type reqGetScreenboards struct {
Screenboards []*ScreenboardLite `json:"screenboards"`
}
// GetScreenboard returns a single screenboard created on this account.
func (self *Client) GetScreenboard(id int) (*Screenboard, error) {
out := &Screenboard{}
err := self.doJsonRequest("GET", fmt.Sprintf("/v1/screen/%d", id), nil, out)
if err != nil {
return nil, err
}
return out, nil
}
// GetScreenboards returns a list of all screenboards created on this account.
func (self *Client) GetScreenboards() ([]*ScreenboardLite, error) {
var out reqGetScreenboards
err := self.doJsonRequest("GET", "/v1/screen", nil, &out)
if err != nil {
return nil, err
}
return out.Screenboards, nil
}
// DeleteScreenboard deletes a screenboard by the identifier.
func (self *Client) DeleteScreenboard(id int) error {
return self.doJsonRequest("DELETE", fmt.Sprintf("/v1/screen/%d", id), nil, nil)
}
// CreateScreenboard creates a new screenboard when given a Screenboard struct. Note
// that the Id, Resource, Url and similar elements are not used in creation.
func (self *Client) CreateScreenboard(board *Screenboard) (*Screenboard, error) {
out := &Screenboard{}
if err := self.doJsonRequest("POST", "/v1/screen", board, out); err != nil {
return nil, err
}
return out, nil
}
// UpdateScreenboard in essence takes a Screenboard struct and persists it back to
// the server. Use this if you've updated your local and need to push it back.
func (self *Client) UpdateScreenboard(board *Screenboard) error {
return self.doJsonRequest("PUT", fmt.Sprintf("/v1/screen/%d", board.Id), board, nil)
}
type ScreenShareResponse struct {
BoardId int `json:"board_id"`
PublicUrl string `json:"public_url"`
}
// ShareScreenboard shares an existing screenboard, it takes and updates ScreenShareResponse
func (self *Client) ShareScreenboard(id int, response *ScreenShareResponse) error {
return self.doJsonRequest("GET", fmt.Sprintf("/v1/screen/share/%d", id), nil, response)
}
// RevokeScreenboard revokes a currently shared screenboard
func (self *Client) RevokeScreenboard(id int) error {
return self.doJsonRequest("DELETE", fmt.Sprintf("/v1/screen/share/%d", id), nil, nil)
}

37
vendor/github.com/zorkian/go-datadog-api/search.go generated vendored Normal file
View File

@ -0,0 +1,37 @@
/*
* Datadog API for Go
*
* Please see the included LICENSE file for licensing information.
*
* Copyright 2013 by authors and contributors.
*/
package datadog
// reqSearch is the container for receiving search results.
type reqSearch struct {
Results struct {
Hosts []string `json:"hosts,omitempty"`
Metrics []string `json:"metrics,omitempty"`
} `json:"results"`
}
// SearchHosts searches through the hosts facet, returning matching hostnames.
func (self *Client) SearchHosts(search string) ([]string, error) {
var out reqSearch
err := self.doJsonRequest("GET", "/v1/search?q=hosts:"+search, nil, &out)
if err != nil {
return nil, err
}
return out.Results.Hosts, nil
}
// SearchMetrics searches through the metrics facet, returning matching ones.
func (self *Client) SearchMetrics(search string) ([]string, error) {
var out reqSearch
err := self.doJsonRequest("GET", "/v1/search?q=metrics:"+search, nil, &out)
if err != nil {
return nil, err
}
return out.Results.Metrics, nil
}

68
vendor/github.com/zorkian/go-datadog-api/series.go generated vendored Normal file
View File

@ -0,0 +1,68 @@
/*
* Datadog API for Go
*
* Please see the included LICENSE file for licensing information.
*
* Copyright 2013 by authors and contributors.
*/
package datadog
import "strconv"
// DataPoint is a tuple of [UNIX timestamp, value]. This has to use floats
// because the value could be non-integer.
type DataPoint [2]float64
// Metric represents a collection of data points that we might send or receive
// on one single metric line.
type Metric struct {
Metric string `json:"metric,omitempty"`
Points []DataPoint `json:"points,omitempty"`
Type string `json:"type,omitempty"`
Host string `json:"host,omitempty"`
Tags []string `json:"tags,omitempty"`
}
// Series represents a collection of data points we get when we query for timeseries data
type Series struct {
Metric string `json:"metric,omitempty"`
DisplayName string `json:"display_name,omitempty"`
Points []DataPoint `json:"pointlist,omitempty"`
Start float64 `json:"start,omitempty"`
End float64 `json:"end,omitempty"`
Interval int `json:"interval,omitempty"`
Aggr string `json:"aggr,omitempty"`
Length int `json:"length,omitempty"`
Scope string `json:"scope,omitempty"`
Expression string `json:"expression,omitempty"`
}
// reqPostSeries from /api/v1/series
type reqPostSeries struct {
Series []Metric `json:"series,omitempty"`
}
// reqMetrics is the container for receiving metric results.
type reqMetrics struct {
Series []Series `json:"series,omitempty"`
}
// PostMetrics takes as input a slice of metrics and then posts them up to the
// server for posting data.
func (client *Client) PostMetrics(series []Metric) error {
return client.doJsonRequest("POST", "/v1/series",
reqPostSeries{Series: series}, nil)
}
// QueryMetrics takes as input from, to (seconds from Unix Epoch) and query string and then requests
// timeseries data for that time peried
func (client *Client) QueryMetrics(from, to int64, query string) ([]Series, error) {
var out reqMetrics
err := client.doJsonRequest("GET", "/v1/query?from="+strconv.FormatInt(from, 10)+"&to="+strconv.FormatInt(to, 10)+"&query="+query,
nil, &out)
if err != nil {
return nil, err
}
return out.Series, nil
}

33
vendor/github.com/zorkian/go-datadog-api/snapshot.go generated vendored Normal file
View File

@ -0,0 +1,33 @@
/*
* Datadog API for Go
*
* Please see the included LICENSE file for licensing information.
*
* Copyright 2016 by authors and contributors.
*/
package datadog
import (
"fmt"
"net/url"
"time"
)
// Snapshot creates an image from a graph and returns the URL of the image.
func (self *Client) Snapshot(query string, start, end time.Time, eventQuery string) (string, error) {
v := url.Values{}
v.Add("start", fmt.Sprintf("%d", start.Unix()))
v.Add("end", fmt.Sprintf("%d", end.Unix()))
v.Add("metric_query", query)
v.Add("event_query", eventQuery)
out := struct {
SnapshotURL string `json:"snapshot_url"`
}{}
err := self.doJsonRequest("GET", "/v1/graph/snapshot?"+v.Encode(), nil, &out)
if err != nil {
return "", err
}
return out.SnapshotURL, nil
}

96
vendor/github.com/zorkian/go-datadog-api/tags.go generated vendored Normal file
View File

@ -0,0 +1,96 @@
/*
* Datadog API for Go
*
* Please see the included LICENSE file for licensing information.
*
* Copyright 2013 by authors and contributors.
*/
package datadog
// TagMap is used to receive the format given to us by the API.
type TagMap map[string][]string
// reqGetTags is the container for receiving tags.
type reqGetTags struct {
Tags TagMap `json:"tags,omitempty"`
}
// regGetHostTags is for receiving a slice of tags.
type reqGetHostTags struct {
Tags []string `json:"tags,omitempty"`
}
// GetTags returns a map of tags.
func (self *Client) GetTags(source string) (TagMap, error) {
var out reqGetTags
uri := "/v1/tags/hosts"
if source != "" {
uri += "?source=" + source
}
err := self.doJsonRequest("GET", uri, nil, &out)
if err != nil {
return nil, err
}
return out.Tags, nil
}
// GetHostTags returns a slice of tags for a given host and source.
func (self *Client) GetHostTags(host, source string) ([]string, error) {
var out reqGetHostTags
uri := "/v1/tags/hosts/" + host
if source != "" {
uri += "?source=" + source
}
err := self.doJsonRequest("GET", uri, nil, &out)
if err != nil {
return nil, err
}
return out.Tags, nil
}
// GetHostTagsBySource is a different way of viewing the tags. It returns a map
// of source:[tag,tag].
func (self *Client) GetHostTagsBySource(host, source string) (TagMap, error) {
var out reqGetTags
uri := "/v1/tags/hosts/" + host + "?by_source=true"
if source != "" {
uri += "&source=" + source
}
err := self.doJsonRequest("GET", uri, nil, &out)
if err != nil {
return nil, err
}
return out.Tags, nil
}
// AddTagsToHost does exactly what it says on the tin. Given a list of tags,
// add them to the host. The source is optionally specificed, and defaults to
// "users" as per the API documentation.
func (self *Client) AddTagsToHost(host, source string, tags []string) error {
uri := "/v1/tags/hosts/" + host
if source != "" {
uri += "?source=" + source
}
return self.doJsonRequest("POST", uri, reqGetHostTags{Tags: tags}, nil)
}
// UpdateHostTags overwrites existing tags for a host, allowing you to specify
// a new set of tags for the given source. This defaults to "users".
func (self *Client) UpdateHostTags(host, source string, tags []string) error {
uri := "/v1/tags/hosts/" + host
if source != "" {
uri += "?source=" + source
}
return self.doJsonRequest("PUT", uri, reqGetHostTags{Tags: tags}, nil)
}
// RemoveHostTags removes all tags from a host for the given source. If none is
// given, the API defaults to "users".
func (self *Client) RemoveHostTags(host, source string) error {
uri := "/v1/tags/hosts/" + host
if source != "" {
uri += "?source=" + source
}
return self.doJsonRequest("DELETE", uri, nil, nil)
}

71
vendor/github.com/zorkian/go-datadog-api/users.go generated vendored Normal file
View File

@ -0,0 +1,71 @@
/*
* Datadog API for Go
*
* Please see the included LICENSE file for licensing information.
*
* Copyright 2013 by authors and contributors.
*/
package datadog
type User struct {
Handle string `json:"handle,omitempty"`
Email string `json:"email,omitempty"`
Name string `json:"name,omitempty"`
Role string `json:"role,omitempty"`
IsAdmin bool `json:"is_admin,omitempty"`
Verified bool `json:"verified,omitempty"`
Disabled bool `json:"disabled,omitempty"`
}
// reqInviteUsers contains email addresses to send invitations to.
type reqInviteUsers struct {
Emails []string `json:"emails,omitempty"`
}
// InviteUsers takes a slice of email addresses and sends invitations to them.
func (self *Client) InviteUsers(emails []string) error {
return self.doJsonRequest("POST", "/v1/invite_users",
reqInviteUsers{Emails: emails}, nil)
}
// internal type to retrieve users from the api
type usersData struct {
Users []User `json:"users"`
}
// GetUsers returns all user, or an error if not found
func (self *Client) GetUsers() (users []User, err error) {
var udata usersData
uri := "/v1/user"
err = self.doJsonRequest("GET", uri, nil, &udata)
users = udata.Users
return
}
// internal type to retrieve single user from the api
type userData struct {
User User `json:"user"`
}
// GetUser returns the user that match a handle, or an error if not found
func (self *Client) GetUser(handle string) (user User, err error) {
var udata userData
uri := "/v1/user/" + handle
err = self.doJsonRequest("GET", uri, nil, &udata)
user = udata.User
return
}
// UpdateUser updates a user with the content of `user`,
// and returns an error if the update failed
func (self *Client) UpdateUser(user User) error {
uri := "/v1/user/" + user.Handle
return self.doJsonRequest("PUT", uri, user, nil)
}
// DeleteUser deletes a user and returns an error if deletion failed
func (self *Client) DeleteUser(handle string) error {
uri := "/v1/user/" + handle
return self.doJsonRequest("DELETE", uri, nil, nil)
}

View File

@ -14,6 +14,7 @@ body.layout-azurerm,
body.layout-cloudflare,
body.layout-cloudstack,
body.layout-consul,
body.layout-datadog,
body.layout-digitalocean,
body.layout-dme,
body.layout-dnsimple,

View File

@ -0,0 +1,38 @@
---
layout: "datadog"
page_title: "Provider: Datadog"
sidebar_current: "docs-datadog-index"
description: |-
The Datadog provider is used to interact with the resources supported by Datadog. The provider needs to be configured with the proper credentials before it can be used.
---
# Datadog Provider
The [Datadog](https://www.datadoghq.com) provider is used to interact with the
resources supported by Datadog. 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 Datadog provider
provider "datadog" {
api_key = "${var.datadog_api_key}"
app_key = "${var.datadog_app_key}"
}
# Create a new monitor
resource "datadog_monitor" "default" {
...
}
```
## Argument Reference
The following arguments are supported:
* `api_key` - (Required) Datadog API key
* `app_key` - (Required) Datadog APP key

View File

@ -0,0 +1,94 @@
---
layout: "datadog"
page_title: "Datadog: datadog_monitor"
sidebar_current: "docs-datadog-resource-monitor"
description: |-
Provides a Datadog monitor resource. This can be used to create and manage monitors.
---
# datadog\_monitor
Provides a Datadog monitor resource. This can be used to create and manage Datadog monitors.
## Example Usage
```
# Create a new Datadog monitor
resource "datadog_monitor" "foo" {
name = "Name for monitor foo"
type = "Metric alert"
message = "Monitor triggered. Notify: @hipchat-channel"
escalation_message = "Escalation message @pagerduty"
query = "avg(last_1h):avg:aws.ec2.cpu{environment:foo,host:foo} by {host} > 2"
thresholds {
ok = 0
warning = 1
critical = 2
}
notify_no_data = false
renotify_interval = 60
notify_audit = false
timeout_h = 60
include_tags = true
silenced {
"*" = 0
}
}
```
## Argument Reference
The following arguments are supported:
* `type` - (Required) The type of the monitor, chosen from:
* `metric alert`
* `service check`
* `event alert`
* `query alert`
* `name` - (Required) Name of Datadog monitor
* `query` - (Required) The monitor query to notify on with syntax varying depending on what type of monitor
you are creating. See [API Reference](http://docs.datadoghq.com/api) for options.
* `message` - (Required) A message to include with notifications for this monitor.
Email notifications can be sent to specific users by using the same '@username' notation as events.
* `escalation_message` - (Optional) A message to include with a re-notification. Supports the '@username'
notification allowed elsewhere.
* `thresholds` - (Required) Thresholds by threshold type:
* `ok`
* `warning`
* `critical`
* `notify_no_data` (Optional) A boolean indicating whether this monitor will notify when data stops reporting. Defaults
to false.
* `no_data_timeframe` (Optional) The number of minutes before a monitor will notify when data stops reporting. Must be at
least 2x the monitor timeframe for metric alerts or 2 minutes for service checks. Default: 2x timeframe for
metric alerts, 2 minutes for service checks.
* `renotify_interval` (Optional) The number of minutes after the last notification before a monitor will re-notify
on the current status. It will only re-notify if it's not resolved.
* `notify_audit` (Optional) A boolean indicating whether tagged users will be notified on changes to this monitor.
Defaults to false.
* `timeout_h` (Optional) The number of hours of the monitor not reporting data before it will automatically resolve
from a triggered state. Defaults to false.
* `include_tags` (Optional) A boolean indicating whether notifications from this monitor will automatically insert its
triggering tags into the title. Defaults to true.
* `silenced` (Optional) Each scope will be muted until the given POSIX timestamp or forever if the value is 0.
To mute the alert completely:
silenced {
'*' = 0
}
To mute role:db for a short time:
silenced {
'role:db' = 1412798116
}
## Attributes Reference
The following attributes are exported:
* `id` - ID of the Datadog monitor

View File

@ -0,0 +1,26 @@
<% 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-datadog-index") %>>
<a href="/docs/providers/datadog/index.html">Datadog Provider</a>
</li>
<li<%= sidebar_current(/^docs-datadog-resource/) %>>
<a href="#">Resources</a>
<ul class="nav nav-visible">
<li<%= sidebar_current("docs-datadog-resource-monitor") %>>
<a href="/docs/providers/datadog/r/monitor.html">datadog_monitor</a>
</li>
</ul>
</li>
</ul>
</div>
<% end %>
<%= yield %>
<% end %>

View File

@ -158,6 +158,10 @@
<a href="/docs/providers/consul/index.html">Consul</a>
</li>
<li<%= sidebar_current("docs-providers-datadog") %>>
<a href="/docs/providers/datadog/index.html">Datadog</a>
</li>
<li<%= sidebar_current("docs-providers-do") %>>
<a href="/docs/providers/do/index.html">DigitalOcean</a>
</li>