Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ module github.com/newrelic/terraform-provider-newrelic/v3

go 1.23.6

replace github.com/newrelic/newrelic-client-go/v2 => github.com/newrelic/newrelic-client-go/v2 v2.70.3-0.20251003115336-c6f46391af16

require (
github.com/hashicorp/terraform-plugin-sdk/v2 v2.26.1
github.com/mitchellh/go-homedir v1.1.0
github.com/newrelic/go-agent/v3 v3.30.0
github.com/newrelic/go-insights v1.0.3
github.com/newrelic/newrelic-client-go/v2 v2.70.2
github.com/newrelic/newrelic-client-go/v2 v2.70.3
github.com/stretchr/testify v1.9.0
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8
)
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,8 @@ github.com/newrelic/go-agent/v3 v3.30.0 h1:ZXHCT/Cot4iIPwcegCZURuRQOsfmGA6wilW+S
github.com/newrelic/go-agent/v3 v3.30.0/go.mod h1:9utrgxlSryNqRrTvII2XBL+0lpofXbqXApvVWPpbzUg=
github.com/newrelic/go-insights v1.0.3 h1:zSNp1CEZnXktzSIEsbHJk8v6ZihdPFP2WsO/fzau3OQ=
github.com/newrelic/go-insights v1.0.3/go.mod h1:A20BoT8TNkqPGX2nS/Z2fYmKl3Cqa3iKZd4whzedCY4=
github.com/newrelic/newrelic-client-go/v2 v2.70.2 h1:G1SrAQhjH2bsWZ4FbO/DXqfOqvUsb1e0LrY/f2rE+pc=
github.com/newrelic/newrelic-client-go/v2 v2.70.2/go.mod h1:P6rXSHPtayzr50+UEYvvjzYPiADv7w2SqeyKz0z5HkU=
github.com/newrelic/newrelic-client-go/v2 v2.70.3-0.20251003115336-c6f46391af16 h1:PSUkHEUry211tQx94G3F2Sy5o+Nc5VSoKus/6Oy8RpY=
github.com/newrelic/newrelic-client-go/v2 v2.70.3-0.20251003115336-c6f46391af16/go.mod h1:P6rXSHPtayzr50+UEYvvjzYPiADv7w2SqeyKz0z5HkU=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
Expand Down
82 changes: 82 additions & 0 deletions newrelic/resource_newrelic_one_dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,14 @@ func dashboardWidgetBillboardSchemaElem() *schema.Resource {

s["billboard_settings"] = dashboardWidgetBillboardSettingsSchema()

s["thresholds_with_series_overrides"] = &schema.Schema{
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Description: "Thresholds with series overrides configuration for the billboard widget.",
Elem: dashboardWidgetThresholdsWithSeriesOverridesSchemaElem(),
}

return &schema.Resource{
Schema: s,
}
Expand Down Expand Up @@ -1119,3 +1127,77 @@ func dashboardWidgetBillboardSettingsSchema() *schema.Schema {
},
}
}

func dashboardWidgetThresholdsWithSeriesOverridesSchemaElem() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
"thresholds": {
Type: schema.TypeList,
Optional: true,
Description: "Thresholds for the billboard widget.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"from": {
Type: schema.TypeFloat,
Optional: true,
Description: "The number from which the range starts in thresholds.",
},
"to": {
Type: schema.TypeFloat,
Optional: true,
Description: "The number at which the range ends in thresholds.",
},
"severity": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice([]string{
string(dashboards.DashboardLineTableWidgetsAlertSeverityTypes.SUCCESS),
string(dashboards.DashboardLineTableWidgetsAlertSeverityTypes.WARNING),
string(dashboards.DashboardLineTableWidgetsAlertSeverityTypes.UNAVAILABLE),
string(dashboards.DashboardLineTableWidgetsAlertSeverityTypes.SEVERE),
string(dashboards.DashboardLineTableWidgetsAlertSeverityTypes.CRITICAL),
}, false),
Description: "Severity of the threshold, which would reflect in the widget.",
},
},
},
},
"series_overrides": {
Type: schema.TypeList,
Optional: true,
Description: "Series overrides for thresholds.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"from": {
Type: schema.TypeFloat,
Optional: true,
Description: "The number from which the range starts in thresholds.",
},
"to": {
Type: schema.TypeFloat,
Optional: true,
Description: "The number at which the range ends in thresholds.",
},
"series_name": {
Type: schema.TypeString,
Optional: true,
Description: "Name of the series to which the threshold would be applied.",
},
"severity": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice([]string{
string(dashboards.DashboardLineTableWidgetsAlertSeverityTypes.SUCCESS),
string(dashboards.DashboardLineTableWidgetsAlertSeverityTypes.WARNING),
string(dashboards.DashboardLineTableWidgetsAlertSeverityTypes.UNAVAILABLE),
string(dashboards.DashboardLineTableWidgetsAlertSeverityTypes.SEVERE),
string(dashboards.DashboardLineTableWidgetsAlertSeverityTypes.CRITICAL),
}, false),
Description: "Severity of the threshold, which would reflect in the widget.",
},
},
},
},
},
}
}
219 changes: 219 additions & 0 deletions newrelic/resource_newrelic_one_dashboard_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1545,6 +1545,225 @@ resource "newrelic_one_dashboard" "bar" {
}`
}

// TestAccNewRelicOneDashboard_BillboardThresholdsWithSeriesOverrides tests thresholds_with_series_overrides functionality
func TestAccNewRelicOneDashboard_BillboardThresholdsWithSeriesOverrides(t *testing.T) {
rName := fmt.Sprintf("tf-test-%s", acctest.RandString(5))
rWidgetName := fmt.Sprintf("tf-test-widget-%s", acctest.RandString(5))

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckNewRelicOneDashboardDestroy,
Steps: []resource.TestStep{
{
Config: testAccCheckNewRelicOneDashboardConfig_BillboardWithThresholdsWithSeriesOverrides(rName, rWidgetName),
Check: resource.ComposeTestCheckFunc(
testAccCheckNewRelicOneDashboardExists("newrelic_one_dashboard.bar", 0),
),
},
{
Config: testAccCheckNewRelicOneDashboardConfig_BillboardWithThresholdsWithSeriesOverridesUpdated(rName, rWidgetName),
Check: resource.ComposeTestCheckFunc(
testAccCheckNewRelicOneDashboardExists("newrelic_one_dashboard.bar", 0),
),
},
// Import
{
ResourceName: "newrelic_one_dashboard.bar",
ImportState: true,
ImportStateVerify: true,
},
},
})
}

// TestAccNewRelicOneDashboard_BillboardThresholdsWithSeriesOverridesValidation tests validation for thresholds_with_series_overrides
func TestAccNewRelicOneDashboard_BillboardThresholdsWithSeriesOverridesValidation(t *testing.T) {
rName := fmt.Sprintf("tf-test-%s", acctest.RandString(5))

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
// Test empty thresholds_with_series_overrides block
{
Config: testAccCheckNewRelicOneDashboardConfig_BillboardWithEmptyThresholdsWithSeriesOverrides(rName),
ExpectError: regexp.MustCompile(`thresholds_with_series_overrides.*must contain at least one.*thresholds.*or.*series_overrides.*block with content`),
},
// Test empty threshold block
{
Config: testAccCheckNewRelicOneDashboardConfig_BillboardWithEmptyThresholdBlock(rName),
ExpectError: regexp.MustCompile(`threshold.*thresholds block cannot be null`),
},
// Test empty series override block
{
Config: testAccCheckNewRelicOneDashboardConfig_BillboardWithEmptySeriesOverrideBlock(rName),
ExpectError: regexp.MustCompile(`series_override.*series_overrides block cannot be null`),
},
},
})
}

// testAccCheckNewRelicOneDashboardConfig_BillboardWithThresholdsWithSeriesOverrides generates billboard with thresholds_with_series_overrides
func testAccCheckNewRelicOneDashboardConfig_BillboardWithThresholdsWithSeriesOverrides(dashboardName string, widgetName string) string {
return `
resource "newrelic_one_dashboard" "bar" {
name = "` + dashboardName + `"

page {
name = "` + dashboardName + `"

widget_billboard {
title = "` + widgetName + `"
row = 1
column = 1
nrql_query {
query = "SELECT count(*) FROM ProcessSample FACET hostname SINCE 30 MINUTES AGO TIMESERIES"
}
thresholds_with_series_overrides {
thresholds {
from = 0
to = 100
severity = "warning"
}
thresholds {
from = 100
to = 200
severity = "critical"
}
series_overrides {
from = 50
to = 150
series_name = "test-1"
severity = "critical"
}
series_overrides {
from = 25
to = 75
series_name = "test-2"
severity = "warning"
}
}
}
}
}`
}

// testAccCheckNewRelicOneDashboardConfig_BillboardWithThresholdsWithSeriesOverridesUpdated generates updated billboard with thresholds_with_series_overrides
func testAccCheckNewRelicOneDashboardConfig_BillboardWithThresholdsWithSeriesOverridesUpdated(dashboardName string, widgetName string) string {
return `
resource "newrelic_one_dashboard" "bar" {
name = "` + dashboardName + `"

page {
name = "` + dashboardName + `"

widget_billboard {
title = "` + widgetName + `"
row = 1
column = 1
nrql_query {
query = "SELECT average(cpuPercent) FROM ProcessSample FACET hostname SINCE 30 MINUTES AGO TIMESERIES"
}
thresholds_with_series_overrides {
thresholds {
from = 0
to = 80
severity = "warning"
}
thresholds {
from = 80
to = 100
severity = "critical"
}
series_overrides {
from = 60
to = 90
series_name = "test"
severity = "critical"
}
}
}
}
}`
}

// testAccCheckNewRelicOneDashboardConfig_BillboardWithEmptyThresholdsWithSeriesOverrides generates billboard with empty thresholds_with_series_overrides
func testAccCheckNewRelicOneDashboardConfig_BillboardWithEmptyThresholdsWithSeriesOverrides(dashboardName string) string {
return `
resource "newrelic_one_dashboard" "bar" {
name = "` + dashboardName + `"

page {
name = "` + dashboardName + `"

widget_billboard {
title = "test widget"
row = 1
column = 1
nrql_query {
query = "SELECT count(*) FROM ProcessSample SINCE 30 MINUTES AGO TIMESERIES"
}
thresholds_with_series_overrides {
# Empty block - should trigger validation error
}
}
}
}`
}

// testAccCheckNewRelicOneDashboardConfig_BillboardWithEmptyThresholdBlock generates billboard with empty threshold block
func testAccCheckNewRelicOneDashboardConfig_BillboardWithEmptyThresholdBlock(dashboardName string) string {
return `
resource "newrelic_one_dashboard" "bar" {
name = "` + dashboardName + `"

page {
name = "` + dashboardName + `"

widget_billboard {
title = "test widget"
row = 1
column = 1
nrql_query {
query = "SELECT count(*) FROM ProcessSample SINCE 30 MINUTES AGO TIMESERIES"
}
thresholds_with_series_overrides {
thresholds {
# Empty threshold block - should trigger validation error
}
}
}
}
}`
}

// testAccCheckNewRelicOneDashboardConfig_BillboardWithEmptySeriesOverrideBlock generates billboard with empty series override block
func testAccCheckNewRelicOneDashboardConfig_BillboardWithEmptySeriesOverrideBlock(dashboardName string) string {
return `
resource "newrelic_one_dashboard" "bar" {
name = "` + dashboardName + `"

page {
name = "` + dashboardName + `"

widget_billboard {
title = "test widget"
row = 1
column = 1
nrql_query {
query = "SELECT count(*) FROM ProcessSample FACET hostname SINCE 30 MINUTES AGO TIMESERIES"
}
thresholds_with_series_overrides {
series_overrides {
# Empty series override block - should trigger validation error
}
}
}
}
}`
}

// testAccCheckNewRelicOneDashboardExists fetches the dashboard back, with an optional sleep time
// used when we know the async nature of the API will mess with consistent testing.
func testAccCheckNewRelicOneDashboardExists(name string, sleepSeconds int) resource.TestCheckFunc {
Expand Down
Loading
Loading