diff --git a/datadog/resource_datadog_monitor.go b/datadog/resource_datadog_monitor.go index e5b37f0b09..0a3151f514 100644 --- a/datadog/resource_datadog_monitor.go +++ b/datadog/resource_datadog_monitor.go @@ -514,8 +514,8 @@ func resourceDatadogMonitorUpdate(d *schema.ResourceData, meta interface{}) erro if _, ok := d.GetOk("silenced"); ok && !silenced { // This means the monitor must be manually unmuted since the API // wouldn't do it automatically when `silenced` is just missing - retval = client.UnmuteMonitor(*m.Id) - d.Set("silenced", nil) + retval = client.UnmuteMonitorScopes(*m.Id, &datadog.UnmuteMonitorScopes{AllScopes: datadog.Bool(true)}) + d.Set("silenced", map[string]int{}) } return retval diff --git a/datadog/resource_datadog_monitor_test.go b/datadog/resource_datadog_monitor_test.go index ffdcd4f5a9..9fdd9ac92c 100644 --- a/datadog/resource_datadog_monitor_test.go +++ b/datadog/resource_datadog_monitor_test.go @@ -555,6 +555,34 @@ func TestAccDatadogMonitor_ThresholdWindows(t *testing.T) { }) } +func TestAccDatadogMonitor_MuteUnmuteSpecificScopes(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDatadogMonitorDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckDatadogMonitorConfigMuteSpecificScopes, + Check: resource.ComposeTestCheckFunc( + testAccCheckDatadogMonitorExists("datadog_monitor.foo"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "silenced.%", "1"), + resource.TestCheckResourceAttr( + "datadog_monitor.foo", "silenced.host:myserver", "0"), + ), + }, + { + Config: testAccCheckDatadogMonitorConfigUnmuteSpecificScopes, + Check: resource.ComposeTestCheckFunc( + testAccCheckDatadogMonitorExists("datadog_monitor.foo"), + resource.TestCheckNoResourceAttr( + "datadog_monitor.foo", "silenced"), + ), + }, + }, + }) +} + func testAccCheckDatadogMonitorDestroy(s *terraform.State) error { client := testAccProvider.Meta().(*datadog.Client) @@ -886,6 +914,38 @@ resource "datadog_monitor" "foo" { } ` +const testAccCheckDatadogMonitorConfigMuteSpecificScopes = ` +resource "datadog_monitor" "foo" { + name = "foo" + type = "metric alert" + message = "test" + + query = "avg(last_5m):max:system.load.1{*} by {host} > 100" + + thresholds = { + critical = 100 + } + + silenced = { + "host:myserver" = 0 + } +} +` + +const testAccCheckDatadogMonitorConfigUnmuteSpecificScopes = ` +resource "datadog_monitor" "foo" { + name = "foo" + type = "metric alert" + message = "test" + + query = "avg(last_5m):max:system.load.1{*} by {host} > 100" + + thresholds = { + critical = 100 + } +} +` + func destroyHelper(s *terraform.State, client *datadog.Client) error { for _, r := range s.RootModule().Resources { i, _ := strconv.Atoi(r.Primary.ID) diff --git a/go.mod b/go.mod index ea5cf2b4f5..5a33510619 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( github.com/terraform-providers/terraform-provider-aws v1.29.0 // indirect github.com/terraform-providers/terraform-provider-template v1.0.0 // indirect github.com/terraform-providers/terraform-provider-tls v1.2.0 // indirect - github.com/zorkian/go-datadog-api v2.20.1-0.20190430091414-fcf4c3b6edfd+incompatible + github.com/zorkian/go-datadog-api v2.20.1-0.20190513084440-9d8b2d52bc3d+incompatible gopkg.in/vmihailenco/msgpack.v2 v2.9.1 // indirect labix.org/v2/mgo v0.0.0-20140701140051-000000000287 // indirect launchpad.net/gocheck v0.0.0-20140225173054-000000000087 // indirect diff --git a/go.sum b/go.sum index 161970b4e0..eaae147129 100644 --- a/go.sum +++ b/go.sum @@ -526,6 +526,8 @@ github.com/zorkian/go-datadog-api v2.20.0+incompatible h1:zfITezz+b9lZuYghMXTdAX github.com/zorkian/go-datadog-api v2.20.0+incompatible/go.mod h1:PkXwHX9CUQa/FpB9ZwAD45N1uhCW4MT/Wj7m36PbKss= github.com/zorkian/go-datadog-api v2.20.1-0.20190430091414-fcf4c3b6edfd+incompatible h1:nBa/g0we0spNByi38CWZNBn7sCs+vfPrWPQKzPSbnVo= github.com/zorkian/go-datadog-api v2.20.1-0.20190430091414-fcf4c3b6edfd+incompatible/go.mod h1:PkXwHX9CUQa/FpB9ZwAD45N1uhCW4MT/Wj7m36PbKss= +github.com/zorkian/go-datadog-api v2.20.1-0.20190513084440-9d8b2d52bc3d+incompatible h1:aPFEeTnGh8pTfVwnb9EJhKJulkdYN9ObEe7e/eSG3UE= +github.com/zorkian/go-datadog-api v2.20.1-0.20190513084440-9d8b2d52bc3d+incompatible/go.mod h1:PkXwHX9CUQa/FpB9ZwAD45N1uhCW4MT/Wj7m36PbKss= go.opencensus.io v0.18.0 h1:Mk5rgZcggtbvtAun5aJzAtjKKN/t0R3jJPlWILlv938= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= diff --git a/vendor/github.com/zorkian/go-datadog-api/datadog-accessors.go b/vendor/github.com/zorkian/go-datadog-api/datadog-accessors.go index 43b36ffbaf..9dd0c0d1a5 100644 --- a/vendor/github.com/zorkian/go-datadog-api/datadog-accessors.go +++ b/vendor/github.com/zorkian/go-datadog-api/datadog-accessors.go @@ -10213,6 +10213,68 @@ func (m *Monitor) SetType(v string) { m.Type = &v } +// GetEnd returns the End field if non-nil, zero value otherwise. +func (m *MuteMonitorScope) GetEnd() int { + if m == nil || m.End == nil { + return 0 + } + return *m.End +} + +// GetEndOk returns a tuple with the End field if it's non-nil, zero value otherwise +// and a boolean to check if the value has been set. +func (m *MuteMonitorScope) GetEndOk() (int, bool) { + if m == nil || m.End == nil { + return 0, false + } + return *m.End, true +} + +// HasEnd returns a boolean if a field has been set. +func (m *MuteMonitorScope) HasEnd() bool { + if m != nil && m.End != nil { + return true + } + + return false +} + +// SetEnd allocates a new m.End and returns the pointer to it. +func (m *MuteMonitorScope) SetEnd(v int) { + m.End = &v +} + +// GetScope returns the Scope field if non-nil, zero value otherwise. +func (m *MuteMonitorScope) GetScope() string { + if m == nil || m.Scope == nil { + return "" + } + return *m.Scope +} + +// GetScopeOk returns a tuple with the Scope field if it's non-nil, zero value otherwise +// and a boolean to check if the value has been set. +func (m *MuteMonitorScope) GetScopeOk() (string, bool) { + if m == nil || m.Scope == nil { + return "", false + } + return *m.Scope, true +} + +// HasScope returns a boolean if a field has been set. +func (m *MuteMonitorScope) HasScope() bool { + if m != nil && m.Scope != nil { + return true + } + + return false +} + +// SetScope allocates a new m.Scope and returns the pointer to it. +func (m *MuteMonitorScope) SetScope(v string) { + m.Scope = &v +} + // GetBackgroundColor returns the BackgroundColor field if non-nil, zero value otherwise. func (n *NoteDefinition) GetBackgroundColor() string { if n == nil || n.BackgroundColor == nil { @@ -17901,6 +17963,68 @@ func (t *TriggeringValue) SetValue(v int) { t.Value = &v } +// GetAllScopes returns the AllScopes field if non-nil, zero value otherwise. +func (u *UnmuteMonitorScopes) GetAllScopes() bool { + if u == nil || u.AllScopes == nil { + return false + } + return *u.AllScopes +} + +// GetAllScopesOk returns a tuple with the AllScopes field if it's non-nil, zero value otherwise +// and a boolean to check if the value has been set. +func (u *UnmuteMonitorScopes) GetAllScopesOk() (bool, bool) { + if u == nil || u.AllScopes == nil { + return false, false + } + return *u.AllScopes, true +} + +// HasAllScopes returns a boolean if a field has been set. +func (u *UnmuteMonitorScopes) HasAllScopes() bool { + if u != nil && u.AllScopes != nil { + return true + } + + return false +} + +// SetAllScopes allocates a new u.AllScopes and returns the pointer to it. +func (u *UnmuteMonitorScopes) SetAllScopes(v bool) { + u.AllScopes = &v +} + +// GetScope returns the Scope field if non-nil, zero value otherwise. +func (u *UnmuteMonitorScopes) GetScope() string { + if u == nil || u.Scope == nil { + return "" + } + return *u.Scope +} + +// GetScopeOk returns a tuple with the Scope field if it's non-nil, zero value otherwise +// and a boolean to check if the value has been set. +func (u *UnmuteMonitorScopes) GetScopeOk() (string, bool) { + if u == nil || u.Scope == nil { + return "", false + } + return *u.Scope, true +} + +// HasScope returns a boolean if a field has been set. +func (u *UnmuteMonitorScopes) HasScope() bool { + if u != nil && u.Scope != nil { + return true + } + + return false +} + +// SetScope allocates a new u.Scope and returns the pointer to it. +func (u *UnmuteMonitorScopes) SetScope(v string) { + u.Scope = &v +} + // GetAccessRole returns the AccessRole field if non-nil, zero value otherwise. func (u *User) GetAccessRole() string { if u == nil || u.AccessRole == nil { diff --git a/vendor/github.com/zorkian/go-datadog-api/monitors.go b/vendor/github.com/zorkian/go-datadog-api/monitors.go index d5a716d2fe..859e04e22f 100644 --- a/vendor/github.com/zorkian/go-datadog-api/monitors.go +++ b/vendor/github.com/zorkian/go-datadog-api/monitors.go @@ -137,6 +137,18 @@ type Creator struct { Name *string `json:"name,omitempty"` } +// MuteMonitorScope specifies which scope to mute and when to end the mute +type MuteMonitorScope struct { + Scope *string `json:"scope,omitempty"` + End *int `json:"end,omitempty"` +} + +// UnmuteMonitorScopes specifies which scope(s) to unmute +type UnmuteMonitorScopes struct { + Scope *string `json:"scope,omitempty"` + AllScopes *bool `json:"all_scopes,omitempty"` +} + // reqMonitors receives a slice of all monitors type reqMonitors struct { Monitors []Monitor `json:"monitors,omitempty"` @@ -229,7 +241,17 @@ func (client *Client) MuteMonitor(id int) error { return client.doJsonRequest("POST", fmt.Sprintf("/v1/monitor/%d/mute", id), nil, nil) } +// MuteMonitorScope turns off monitoring notifications for a monitor for a given scope +func (client *Client) MuteMonitorScope(id int, muteMonitorScope *MuteMonitorScope) error { + return client.doJsonRequest("POST", fmt.Sprintf("/v1/monitor/%d/mute", id), muteMonitorScope, nil) +} + // UnmuteMonitor turns on monitoring notifications for a monitor func (client *Client) UnmuteMonitor(id int) error { return client.doJsonRequest("POST", fmt.Sprintf("/v1/monitor/%d/unmute", id), nil, nil) } + +// UnmuteMonitorScopes is similar to UnmuteMonitor, but provides finer-grained control to unmuting +func (client *Client) UnmuteMonitorScopes(id int, unmuteMonitorScopes *UnmuteMonitorScopes) error { + return client.doJsonRequest("POST", fmt.Sprintf("/v1/monitor/%d/unmute", id), unmuteMonitorScopes, nil) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 97181a0319..2cbc58bf62 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -219,7 +219,7 @@ github.com/zclconf/go-cty/cty/gocty github.com/zclconf/go-cty/cty/set github.com/zclconf/go-cty/cty/function github.com/zclconf/go-cty/cty/function/stdlib -# github.com/zorkian/go-datadog-api v2.20.1-0.20190430091414-fcf4c3b6edfd+incompatible +# github.com/zorkian/go-datadog-api v2.20.1-0.20190513084440-9d8b2d52bc3d+incompatible github.com/zorkian/go-datadog-api # golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734 golang.org/x/crypto/openpgp diff --git a/website/docs/r/downtime.html.markdown b/website/docs/r/downtime.html.markdown index d3a23b0f5e..5a842af991 100644 --- a/website/docs/r/downtime.html.markdown +++ b/website/docs/r/downtime.html.markdown @@ -52,7 +52,7 @@ The following arguments are supported: * `until_occurrences` - (Optional) How many times the downtime will be rescheduled. `until_occurrences` and `until_date` are mutually exclusive. * `until_date` - (Optional) The date at which the recurrence should end as a POSIX timestamp. `until_occurrences` and `until_date` are mutually exclusive. * `message` - (Optional) A message to include with notifications for this downtime. -* `monitor_id` - (Optional) Reference to which monitor this downtime is applied. When scheduling downtime for a given monitor, datadog changes `silenced` property of the monitor to match the `end` POSIX timestamp. +* `monitor_id` - (Optional) Reference to which monitor this downtime is applied. When scheduling downtime for a given monitor, datadog changes `silenced` property of the monitor to match the `end` POSIX timestamp. **Note:** this will effectively change the `silenced` attribute of the referenced monitor. If that monitor is also tracked by Terraform and you don't want it to be unmuted on the next `terraform apply`, see [details](/docs/providers/datadog/r/monitor.html#silencing-by-hand-and-by-downtimes) in the monitor resource documentation. ## Attributes Reference diff --git a/website/docs/r/monitor.html.markdown b/website/docs/r/monitor.html.markdown index f89ac327b5..30739469a7 100644 --- a/website/docs/r/monitor.html.markdown +++ b/website/docs/r/monitor.html.markdown @@ -142,6 +142,25 @@ The following arguments are supported: silenced = ${map("role:${var:role}", 0)} +## Silencing by Hand and by Downtimes + +There are two ways how to silence a single monitor: + +* Mute it by hand +* Create a Downtime + +Both of these actions add a new value to the `silenced` map. This can be problematic if the `silenced` attribute doesn't contain them in your Terraform, as they would be removed on next `terraform apply` invocation. In order to prevent that from happening, you can add following to your monitor: + +``` +lifecycle { + ignore_changes = ["silenced"] +} +``` + +The above will make sure that any changes to the `silenced` attribute are ignored. + +This issue doesn't apply to multi-monitor downtimes (those that don't contain `monitor_id`), as these don't influence contents of the `silenced` attribute. + ## Attributes Reference The following attributes are exported: