[datadog] add support for new host delay to the datadog_monitor resource (#11975)
* [datadog] Update go-datadog-api library Involves one breaking API change. Also some `gofmt`ing. * [datadog] Add support for new_host_delay to the datadog_monitor resource New API parameter that Datadog added for monitors to ignore new hosts for the specified time period in monitor evaluation.
This commit is contained in:
parent
2075ea84e8
commit
5b7e3701cb
|
@ -1,8 +1,9 @@
|
|||
package datadog
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestDatadogMonitor_import(t *testing.T) {
|
||||
|
@ -41,6 +42,7 @@ resource "datadog_monitor" "foo" {
|
|||
}
|
||||
|
||||
notify_no_data = false
|
||||
new_host_delay = 600
|
||||
renotify_interval = 60
|
||||
|
||||
notify_audit = false
|
||||
|
|
|
@ -80,6 +80,11 @@ func resourceDatadogMonitor() *schema.Resource {
|
|||
Optional: true,
|
||||
Default: false,
|
||||
},
|
||||
"new_host_delay": {
|
||||
Type: schema.TypeInt,
|
||||
Computed: true,
|
||||
Optional: true,
|
||||
},
|
||||
"no_data_timeframe": {
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
|
@ -153,6 +158,12 @@ func buildMonitorStruct(d *schema.ResourceData) *datadog.Monitor {
|
|||
}
|
||||
o.Silenced = s
|
||||
}
|
||||
if attr, ok := d.GetOk("notify_no_data"); ok {
|
||||
o.NotifyNoData = attr.(bool)
|
||||
}
|
||||
if attr, ok := d.GetOk("new_host_delay"); ok {
|
||||
o.NewHostDelay = datadog.Int(attr.(int))
|
||||
}
|
||||
if attr, ok := d.GetOk("no_data_timeframe"); ok {
|
||||
o.NoDataTimeframe = datadog.NoDataTimeframe(attr.(int))
|
||||
}
|
||||
|
@ -268,6 +279,8 @@ func resourceDatadogMonitorRead(d *schema.ResourceData, meta interface{}) error
|
|||
d.Set("query", m.Query)
|
||||
d.Set("type", m.Type)
|
||||
d.Set("thresholds", thresholds)
|
||||
|
||||
d.Set("new_host_delay", m.Options.NewHostDelay)
|
||||
d.Set("notify_no_data", m.Options.NotifyNoData)
|
||||
d.Set("no_data_timeframe", m.Options.NoDataTimeframe)
|
||||
d.Set("renotify_interval", m.Options.RenotifyInterval)
|
||||
|
@ -328,6 +341,12 @@ func resourceDatadogMonitorUpdate(d *schema.ResourceData, meta interface{}) erro
|
|||
}
|
||||
}
|
||||
|
||||
if attr, ok := d.GetOk("notify_no_data"); ok {
|
||||
o.NotifyNoData = attr.(bool)
|
||||
}
|
||||
if attr, ok := d.GetOk("new_host_delay"); ok {
|
||||
o.NewHostDelay = datadog.Int(attr.(int))
|
||||
}
|
||||
if attr, ok := d.GetOk("no_data_timeframe"); ok {
|
||||
o.NoDataTimeframe = datadog.NoDataTimeframe(attr.(int))
|
||||
}
|
||||
|
|
|
@ -31,6 +31,8 @@ func TestAccDatadogMonitor_Basic(t *testing.T) {
|
|||
"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", "new_host_delay", "600"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"datadog_monitor.foo", "renotify_interval", "60"),
|
||||
resource.TestCheckResourceAttr(
|
||||
|
@ -109,6 +111,8 @@ func TestAccDatadogMonitor_Updated(t *testing.T) {
|
|||
"datadog_monitor.foo", "type", "metric alert"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"datadog_monitor.foo", "notify_no_data", "false"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"datadog_monitor.foo", "new_host_delay", "600"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"datadog_monitor.foo", "renotify_interval", "60"),
|
||||
resource.TestCheckResourceAttr(
|
||||
|
@ -147,6 +151,8 @@ func TestAccDatadogMonitor_Updated(t *testing.T) {
|
|||
"datadog_monitor.foo", "type", "metric alert"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"datadog_monitor.foo", "notify_no_data", "true"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"datadog_monitor.foo", "new_host_delay", "900"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"datadog_monitor.foo", "no_data_timeframe", "20"),
|
||||
resource.TestCheckResourceAttr(
|
||||
|
@ -281,6 +287,7 @@ resource "datadog_monitor" "foo" {
|
|||
|
||||
notify_audit = false
|
||||
timeout_h = 60
|
||||
new_host_delay = 600
|
||||
include_tags = true
|
||||
require_full_window = true
|
||||
locked = false
|
||||
|
@ -378,6 +385,7 @@ resource "datadog_monitor" "foo" {
|
|||
}
|
||||
|
||||
notify_no_data = true
|
||||
new_host_delay = 900
|
||||
no_data_timeframe = 20
|
||||
renotify_interval = 40
|
||||
escalation_message = "the situation has escalated! @pagerduty"
|
||||
|
|
|
@ -322,11 +322,7 @@ func appendRequests(datadogGraph *datadog.Graph, terraformRequests *[]interface{
|
|||
if style, ok := t["style"]; ok {
|
||||
s, _ := style.(map[string]interface{})
|
||||
|
||||
style := struct {
|
||||
Palette *string `json:"palette,omitempty"`
|
||||
Width *string `json:"width,omitempty"`
|
||||
Type *string `json:"type,omitempty"`
|
||||
}{}
|
||||
style := &datadog.GraphDefinitionRequestStyle{}
|
||||
|
||||
if palette_, ok := s["palette"]; ok {
|
||||
palette := palette_.(string)
|
||||
|
@ -343,7 +339,7 @@ func appendRequests(datadogGraph *datadog.Graph, terraformRequests *[]interface{
|
|||
style.Type = &style_type
|
||||
}
|
||||
|
||||
d.Style = &style
|
||||
d.Style = style
|
||||
}
|
||||
|
||||
if changeType, ok := t["change_type"]; ok {
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Client is the object that handles talking to the Datadog API. This maintains
|
||||
|
@ -22,7 +23,8 @@ type Client struct {
|
|||
apiKey, appKey string
|
||||
|
||||
//The Http Client that is used to make requests
|
||||
HttpClient *http.Client
|
||||
HttpClient *http.Client
|
||||
RetryTimeout time.Duration
|
||||
}
|
||||
|
||||
// valid is the struct to unmarshal validation endpoint responses into.
|
||||
|
@ -35,9 +37,10 @@ type valid struct {
|
|||
// methods. The expected argument is the API key.
|
||||
func NewClient(apiKey, appKey string) *Client {
|
||||
return &Client{
|
||||
apiKey: apiKey,
|
||||
appKey: appKey,
|
||||
HttpClient: http.DefaultClient,
|
||||
apiKey: apiKey,
|
||||
appKey: appKey,
|
||||
HttpClient: http.DefaultClient,
|
||||
RetryTimeout: time.Duration(60 * time.Second),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
package datadog
|
||||
|
||||
// Int is a helper routine that allocates a new int value
|
||||
// to store v and returns a pointer to it.
|
||||
func Int(v int) *int { return &v }
|
||||
|
||||
// GetInt is a helper routine that returns a boolean representing
|
||||
// if a value was set, and if so, dereferences the pointer to it.
|
||||
func GetInt(v *int) (int, bool) {
|
||||
if v != nil {
|
||||
return *v, true
|
||||
}
|
||||
|
||||
return 0, false
|
||||
}
|
|
@ -13,18 +13,21 @@ import (
|
|||
"fmt"
|
||||
)
|
||||
|
||||
// GraphDefinitionRequestStyle represents the graph style attributes
|
||||
type GraphDefinitionRequestStyle struct {
|
||||
Palette *string `json:"palette,omitempty"`
|
||||
Width *string `json:"width,omitempty"`
|
||||
Type *string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
// GraphDefinitionRequest represents the requests passed into each graph.
|
||||
type GraphDefinitionRequest struct {
|
||||
Query string `json:"q"`
|
||||
Stacked bool `json:"stacked"`
|
||||
Aggregator string
|
||||
Query string `json:"q"`
|
||||
Stacked bool `json:"stacked"`
|
||||
Aggregator string `json:"aggregator"`
|
||||
ConditionalFormats []DashboardConditionalFormat `json:"conditional_formats,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Style *struct {
|
||||
Palette *string `json:"palette,omitempty"`
|
||||
Width *string `json:"width,omitempty"`
|
||||
Type *string `json:"type,omitempty"`
|
||||
} `json:"style,omitempty"`
|
||||
Style *GraphDefinitionRequestStyle `json:"style,omitempty"`
|
||||
|
||||
// For change type graphs
|
||||
ChangeType string `json:"change_type,omitempty"`
|
||||
|
|
|
@ -42,6 +42,7 @@ type Options struct {
|
|||
NoDataTimeframe NoDataTimeframe `json:"no_data_timeframe,omitempty"`
|
||||
NotifyAudit bool `json:"notify_audit,omitempty"`
|
||||
NotifyNoData bool `json:"notify_no_data,omitempty"`
|
||||
NewHostDelay *int `json:"new_host_delay,omitempty"`
|
||||
RenotifyInterval int `json:"renotify_interval,omitempty"`
|
||||
Silenced map[string]int `json:"silenced,omitempty"`
|
||||
TimeoutH int `json:"timeout_h,omitempty"`
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package datadog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
type RateLimit struct {
|
||||
Limit int
|
||||
Period time.Duration
|
||||
Remaining int
|
||||
Reset time.Duration
|
||||
}
|
||||
|
||||
func (r *RateLimit) Error() string {
|
||||
return fmt.Sprintf("Rate limiting: Limit %d, Period %d, Remaining %d, Reset in %d", r.Limit, r.Period, r.Remaining, r.Reset)
|
||||
}
|
||||
|
||||
func NewRateLimit(limit, period, remaining, reset int) *RateLimit {
|
||||
return &RateLimit{
|
||||
Limit: limit,
|
||||
Period: time.Duration(period) * time.Second,
|
||||
Remaining: remaining,
|
||||
Reset: time.Duration(reset) * time.Second,
|
||||
}
|
||||
}
|
|
@ -17,12 +17,20 @@ import (
|
|||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cenkalti/backoff"
|
||||
)
|
||||
|
||||
const (
|
||||
RateLimitHeader = "X-RateLimit-Limit"
|
||||
RatePeriodHeader = "X-RateLimit-Period"
|
||||
RateRemainingHeader = "X-RateLimit-Remaining"
|
||||
RateResetHeader = "X-RateLimit-Reset"
|
||||
)
|
||||
|
||||
// uriForAPI is to be called with something like "/v1/events" and it will give
|
||||
// the proper request URI to be posted to.
|
||||
func (client *Client) uriForAPI(api string) string {
|
||||
|
@ -66,13 +74,19 @@ func (client *Client) doJsonRequest(method, api string,
|
|||
if method == "POST" {
|
||||
resp, err = client.HttpClient.Do(req)
|
||||
} else {
|
||||
resp, err = client.doRequestWithRetries(req, 60*time.Second)
|
||||
resp, err = client.doRequestWithRetries(req, client.RetryTimeout)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if err := client.getRateLimit(resp); err != nil {
|
||||
if err.(*RateLimit).Remaining == 0 {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode > 299 {
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
|
@ -104,6 +118,27 @@ func (client *Client) doJsonRequest(method, api string,
|
|||
return nil
|
||||
}
|
||||
|
||||
func (client *Client) getRateLimit(response *http.Response) error {
|
||||
|
||||
ratelimit := response.Header.Get(RateLimitHeader)
|
||||
if ratelimit == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
rateperiod := response.Header.Get(RatePeriodHeader)
|
||||
rateremaining := response.Header.Get(RateRemainingHeader)
|
||||
ratereset := response.Header.Get(RateResetHeader)
|
||||
|
||||
limit, _ := strconv.Atoi(ratelimit)
|
||||
period, _ := strconv.Atoi(rateperiod)
|
||||
remaining, _ := strconv.Atoi(rateremaining)
|
||||
reset, _ := strconv.Atoi(ratereset)
|
||||
|
||||
ratelimiterror := NewRateLimit(limit, period, remaining, reset)
|
||||
|
||||
return error(ratelimiterror)
|
||||
}
|
||||
|
||||
// doRequestWithRetries performs an HTTP request repeatedly for maxTime or until
|
||||
// no error and no HTTP response code higher than 299 is returned.
|
||||
func (client *Client) doRequestWithRetries(req *http.Request, maxTime time.Duration) (*http.Response, error) {
|
||||
|
@ -120,6 +155,12 @@ func (client *Client) doRequestWithRetries(req *http.Request, maxTime time.Durat
|
|||
return err
|
||||
}
|
||||
|
||||
if err := client.getRateLimit(resp); err != nil {
|
||||
if err.(*RateLimit).Remaining == 0 {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode > 299 {
|
||||
return errors.New("API error: " + resp.Status)
|
||||
}
|
||||
|
|
|
@ -2647,10 +2647,10 @@
|
|||
"revision": "75ce5fbba34b1912a3641adbd58cf317d7315821"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "yMIu8wtilcyADHouhDllFm+kovE=",
|
||||
"checksumSHA1": "uynUzdKeOsfi7flpYWFx835Nafo=",
|
||||
"path": "github.com/zorkian/go-datadog-api",
|
||||
"revision": "a0a72fc5e4cae721b5144ba785f07f4edcf2cd47",
|
||||
"revisionTime": "2016-12-07T17:41:01Z"
|
||||
"revision": "6308094e4aca46eb16a227b50be29772242bf3aa",
|
||||
"revisionTime": "2017-02-02T00:47:50Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "9x64JhJGo6z8TS0Q33cGcR64kjA=",
|
||||
|
|
|
@ -80,6 +80,9 @@ The following arguments are supported:
|
|||
|
||||
* `notify_no_data` (Optional) A boolean indicating whether this monitor will notify when data stops reporting. Defaults
|
||||
to true.
|
||||
* `new_host_delay` (Optional) Time (in seconds) to allow a host to boot and
|
||||
applications to fully start before starting the evaluation of monitor
|
||||
results. Should be a non negative integer. Defaults to 300.
|
||||
* `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.
|
||||
|
|
Loading…
Reference in New Issue