From 185cac3cfb7372b26a8f3e0471287f020955499c Mon Sep 17 00:00:00 2001 From: Andrew Hackmann Date: Fri, 7 Nov 2025 13:02:18 -0600 Subject: [PATCH 1/4] add prom mig check --- .../resources/grafana/resource_data_source.go | 59 ++++++++- ...source_data_source_deprecated_auth_test.go | 115 ++++++++++++++++++ .../grafana/resource_data_source_test.go | 70 +++++++++++ 3 files changed, 242 insertions(+), 2 deletions(-) create mode 100644 internal/resources/grafana/resource_data_source_deprecated_auth_test.go diff --git a/internal/resources/grafana/resource_data_source.go b/internal/resources/grafana/resource_data_source.go index d5d6f0bcf..c2022c90c 100644 --- a/internal/resources/grafana/resource_data_source.go +++ b/internal/resources/grafana/resource_data_source.go @@ -245,13 +245,17 @@ func CreateDataSource(ctx context.Context, d *schema.ResourceData, meta any) dia return diag.FromErr(err) } + // Check for deprecated authentication methods + diags := checkDeprecatedPrometheusAuth(d) + resp, err := client.Datasources.AddDataSource(dataSource) if err != nil { return diag.FromErr(err) } d.SetId(MakeOrgResourceID(orgID, resp.Payload.Datasource.UID)) - return ReadDataSource(ctx, d, meta) + readDiags := ReadDataSource(ctx, d, meta) + return append(diags, readDiags...) } // UpdateDataSource updates a Grafana datasource @@ -262,6 +266,10 @@ func UpdateDataSource(ctx context.Context, d *schema.ResourceData, meta any) dia if err != nil { return diag.FromErr(err) } + + // Check for deprecated authentication methods + diags := checkDeprecatedPrometheusAuth(d) + body := models.UpdateDataSourceCommand{ Access: dataSource.Access, BasicAuth: dataSource.BasicAuth, @@ -279,7 +287,11 @@ func UpdateDataSource(ctx context.Context, d *schema.ResourceData, meta any) dia } _, err = client.Datasources.UpdateDataSourceByUID(idStr, &body) - return diag.FromErr(err) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + + return diags } // ReadDataSource reads a Grafana datasource @@ -450,3 +462,46 @@ func removeHeadersFromJSONData(input map[string]any) (map[string]any, map[string return jsonData, headers } + +// checkDeprecatedPrometheusAuth checks if the data source is using deprecated authentication methods +func checkDeprecatedPrometheusAuth(d *schema.ResourceData) diag.Diagnostics { + var diags diag.Diagnostics + + // Only check for Prometheus data sources + dsType := d.Get("type").(string) + if dsType != "prometheus" { + return diags + } + + // Parse the json_data_encoded field + jsonDataEncoded := d.Get("json_data_encoded").(string) + if jsonDataEncoded == "" { + return diags + } + + var jsonData map[string]any + if err := json.Unmarshal([]byte(jsonDataEncoded), &jsonData); err != nil { + // If we can't parse it, don't add a warning + return diags + } + + // Check for deprecated SigV4 authentication + if sigV4Auth, ok := jsonData["sigV4Auth"].(bool); ok && sigV4Auth { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Warning, + Summary: "Deprecated authentication method", + Detail: "SigV4 authentication is deprecated for the core Prometheus data source. Please migrate to the Amazon Managed Service for Prometheus data source plugin (grafana-amazonprometheus-datasource). See https://grafana.com/docs/grafana/latest/datasources/prometheus/configure/aws-authentication/ for more information.", + }) + } + + // Check for deprecated Azure authentication + if azureAuth, ok := jsonData["azureCredentials"]; ok && azureAuth != nil { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Warning, + Summary: "Deprecated authentication method", + Detail: "Azure authentication is deprecated for the core Prometheus data source. Please migrate to the Azure Monitor Managed Service for Prometheus data source plugin (grafana-azureprometheus-datasource). See https://grafana.com/grafana/plugins/grafana-azureprometheus-datasource/ for more information.", + }) + } + + return diags +} diff --git a/internal/resources/grafana/resource_data_source_deprecated_auth_test.go b/internal/resources/grafana/resource_data_source_deprecated_auth_test.go new file mode 100644 index 000000000..3477e57d2 --- /dev/null +++ b/internal/resources/grafana/resource_data_source_deprecated_auth_test.go @@ -0,0 +1,115 @@ +package grafana + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func TestCheckDeprecatedPrometheusAuth_SigV4(t *testing.T) { + d := schema.TestResourceDataRaw(t, resourceDataSource().Schema.Schema, map[string]interface{}{ + "name": "test-prometheus", + "type": "prometheus", + "url": "http://localhost:9090", + "json_data_encoded": `{"sigV4Auth":true,"sigV4Region":"us-east-1"}`, + }) + + diags := checkDeprecatedPrometheusAuth(d) + + if len(diags) != 1 { + t.Errorf("Expected 1 diagnostic, got %d", len(diags)) + } + + if len(diags) > 0 { + if diags[0].Severity != 1 { // Warning + t.Errorf("Expected warning severity, got %d", diags[0].Severity) + } + if diags[0].Summary != "Deprecated authentication method" { + t.Errorf("Unexpected summary: %s", diags[0].Summary) + } + } +} + +func TestCheckDeprecatedPrometheusAuth_Azure(t *testing.T) { + d := schema.TestResourceDataRaw(t, resourceDataSource().Schema.Schema, map[string]interface{}{ + "name": "test-prometheus", + "type": "prometheus", + "url": "http://localhost:9090", + "json_data_encoded": `{"azureAuth":true}`, + }) + + diags := checkDeprecatedPrometheusAuth(d) + + if len(diags) != 1 { + t.Errorf("Expected 1 diagnostic, got %d", len(diags)) + } + + if len(diags) > 0 { + if diags[0].Severity != 1 { // Warning + t.Errorf("Expected warning severity, got %d", diags[0].Severity) + } + if diags[0].Summary != "Deprecated authentication method" { + t.Errorf("Unexpected summary: %s", diags[0].Summary) + } + } +} + +func TestCheckDeprecatedPrometheusAuth_NoDeprecatedAuth(t *testing.T) { + d := schema.TestResourceDataRaw(t, resourceDataSource().Schema.Schema, map[string]interface{}{ + "name": "test-prometheus", + "type": "prometheus", + "url": "http://localhost:9090", + "json_data_encoded": `{"httpMethod":"POST"}`, + }) + + diags := checkDeprecatedPrometheusAuth(d) + + if len(diags) != 0 { + t.Errorf("Expected 0 diagnostics, got %d", len(diags)) + } +} + +func TestCheckDeprecatedPrometheusAuth_NonPrometheusDataSource(t *testing.T) { + d := schema.TestResourceDataRaw(t, resourceDataSource().Schema.Schema, map[string]interface{}{ + "name": "test-loki", + "type": "loki", + "url": "http://localhost:3100", + "json_data_encoded": `{"sigV4Auth":true}`, + }) + + diags := checkDeprecatedPrometheusAuth(d) + + if len(diags) != 0 { + t.Errorf("Expected 0 diagnostics for non-Prometheus datasource, got %d", len(diags)) + } +} + +func TestCheckDeprecatedPrometheusAuth_EmptyJSONData(t *testing.T) { + d := schema.TestResourceDataRaw(t, resourceDataSource().Schema.Schema, map[string]interface{}{ + "name": "test-prometheus", + "type": "prometheus", + "url": "http://localhost:9090", + }) + + diags := checkDeprecatedPrometheusAuth(d) + + if len(diags) != 0 { + t.Errorf("Expected 0 diagnostics for empty JSON data, got %d", len(diags)) + } +} + +func TestCheckDeprecatedPrometheusAuth_BothAuthMethods(t *testing.T) { + d := schema.TestResourceDataRaw(t, resourceDataSource().Schema.Schema, map[string]interface{}{ + "name": "test-prometheus", + "type": "prometheus", + "url": "http://localhost:9090", + "json_data_encoded": `{"sigV4Auth":true,"azureAuth":true}`, + }) + + diags := checkDeprecatedPrometheusAuth(d) + + // Should get warnings for both auth methods + if len(diags) != 2 { + t.Errorf("Expected 2 diagnostics, got %d", len(diags)) + } +} diff --git a/internal/resources/grafana/resource_data_source_test.go b/internal/resources/grafana/resource_data_source_test.go index ca803b0ae..516d35446 100644 --- a/internal/resources/grafana/resource_data_source_test.go +++ b/internal/resources/grafana/resource_data_source_test.go @@ -458,3 +458,73 @@ resource "grafana_data_source" "test" { url = "http://localhost:9090" }`, orgName) } + +func TestAccDataSource_PrometheusDeprecatedSigV4Auth(t *testing.T) { + testutils.CheckOSSTestsEnabled(t) + + var dataSource models.DataSource + dsName := acctest.RandString(10) + + config := fmt.Sprintf(` +resource "grafana_data_source" "test" { + name = "%s" + type = "prometheus" + url = "http://localhost:9090" + + json_data_encoded = jsonencode({ + sigV4Auth = true + sigV4AuthType = "default" + sigV4Region = "us-east-1" + }) +}`, dsName) + + resource.ParallelTest(t, resource.TestCase{ + ProtoV5ProviderFactories: testutils.ProtoV5ProviderFactories, + CheckDestroy: datasourceCheckExists.destroyed(&dataSource, nil), + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + datasourceCheckExists.exists("grafana_data_source.test", &dataSource), + resource.TestCheckResourceAttr("grafana_data_source.test", "name", dsName), + resource.TestCheckResourceAttr("grafana_data_source.test", "type", "prometheus"), + ), + ExpectNonEmptyPlan: false, + }, + }, + }) +} + +func TestAccDataSource_PrometheusDeprecatedAzureAuth(t *testing.T) { + testutils.CheckOSSTestsEnabled(t) + + var dataSource models.DataSource + dsName := acctest.RandString(10) + + config := fmt.Sprintf(` +resource "grafana_data_source" "test" { + name = "%s" + type = "prometheus" + url = "http://localhost:9090" + + json_data_encoded = jsonencode({ + azureAuth = true + }) +}`, dsName) + + resource.ParallelTest(t, resource.TestCase{ + ProtoV5ProviderFactories: testutils.ProtoV5ProviderFactories, + CheckDestroy: datasourceCheckExists.destroyed(&dataSource, nil), + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + datasourceCheckExists.exists("grafana_data_source.test", &dataSource), + resource.TestCheckResourceAttr("grafana_data_source.test", "name", dsName), + resource.TestCheckResourceAttr("grafana_data_source.test", "type", "prometheus"), + ), + ExpectNonEmptyPlan: false, + }, + }, + }) +} From dcb5d69a0947626b1911115105d9a27ce58bfee6 Mon Sep 17 00:00:00 2001 From: Andrew Hackmann Date: Fri, 14 Nov 2025 08:58:53 -0600 Subject: [PATCH 2/4] update wording --- internal/resources/grafana/resource_data_source.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/resources/grafana/resource_data_source.go b/internal/resources/grafana/resource_data_source.go index c2022c90c..1ce5d40ef 100644 --- a/internal/resources/grafana/resource_data_source.go +++ b/internal/resources/grafana/resource_data_source.go @@ -490,7 +490,7 @@ func checkDeprecatedPrometheusAuth(d *schema.ResourceData) diag.Diagnostics { diags = append(diags, diag.Diagnostic{ Severity: diag.Warning, Summary: "Deprecated authentication method", - Detail: "SigV4 authentication is deprecated for the core Prometheus data source. Please migrate to the Amazon Managed Service for Prometheus data source plugin (grafana-amazonprometheus-datasource). See https://grafana.com/docs/grafana/latest/datasources/prometheus/configure/aws-authentication/ for more information.", + Detail: "SigV4 authentication is deprecated for the core Prometheus data source. Please install Amazon Managed Service for Prometheus found here: https://grafana.com/grafana/plugins/grafana-amazonprometheus-datasource/ and then change the type of your data source to 'grafana-amazonprometheus-datasource'.", }) } @@ -499,7 +499,7 @@ func checkDeprecatedPrometheusAuth(d *schema.ResourceData) diag.Diagnostics { diags = append(diags, diag.Diagnostic{ Severity: diag.Warning, Summary: "Deprecated authentication method", - Detail: "Azure authentication is deprecated for the core Prometheus data source. Please migrate to the Azure Monitor Managed Service for Prometheus data source plugin (grafana-azureprometheus-datasource). See https://grafana.com/grafana/plugins/grafana-azureprometheus-datasource/ for more information.", + Detail: "Azure authentication is deprecated for the core Prometheus data source. lease install Amazon Managed Service for Prometheus found here: https://grafana.com/grafana/plugins/grafana-azureprometheus-datasource/ and then change the type of your data source to 'grafana-azureprometheus-datasource'.", }) } From 18056b092f20f78ff57db90c328171be1e8f3edc Mon Sep 17 00:00:00 2001 From: Andrew Hackmann Date: Fri, 14 Nov 2025 14:52:22 -0600 Subject: [PATCH 3/4] tests use require --- ...source_data_source_deprecated_auth_test.go | 73 ++++--------------- 1 file changed, 14 insertions(+), 59 deletions(-) diff --git a/internal/resources/grafana/resource_data_source_deprecated_auth_test.go b/internal/resources/grafana/resource_data_source_deprecated_auth_test.go index 3477e57d2..2c3f719f4 100644 --- a/internal/resources/grafana/resource_data_source_deprecated_auth_test.go +++ b/internal/resources/grafana/resource_data_source_deprecated_auth_test.go @@ -3,7 +3,9 @@ package grafana import ( "testing" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/stretchr/testify/require" ) func TestCheckDeprecatedPrometheusAuth_SigV4(t *testing.T) { @@ -11,7 +13,7 @@ func TestCheckDeprecatedPrometheusAuth_SigV4(t *testing.T) { "name": "test-prometheus", "type": "prometheus", "url": "http://localhost:9090", - "json_data_encoded": `{"sigV4Auth":true,"sigV4Region":"us-east-1"}`, + "json_data_encoded": `{"sigV4Auth":true}`, }) diags := checkDeprecatedPrometheusAuth(d) @@ -20,14 +22,8 @@ func TestCheckDeprecatedPrometheusAuth_SigV4(t *testing.T) { t.Errorf("Expected 1 diagnostic, got %d", len(diags)) } - if len(diags) > 0 { - if diags[0].Severity != 1 { // Warning - t.Errorf("Expected warning severity, got %d", diags[0].Severity) - } - if diags[0].Summary != "Deprecated authentication method" { - t.Errorf("Unexpected summary: %s", diags[0].Summary) - } - } + require.Equal(t, len(diags), 1) + require.Equal(t, diags[0].Severity, diag.Warning) } func TestCheckDeprecatedPrometheusAuth_Azure(t *testing.T) { @@ -35,7 +31,7 @@ func TestCheckDeprecatedPrometheusAuth_Azure(t *testing.T) { "name": "test-prometheus", "type": "prometheus", "url": "http://localhost:9090", - "json_data_encoded": `{"azureAuth":true}`, + "json_data_encoded": `{"azureCredentials":{}}`, }) diags := checkDeprecatedPrometheusAuth(d) @@ -44,47 +40,11 @@ func TestCheckDeprecatedPrometheusAuth_Azure(t *testing.T) { t.Errorf("Expected 1 diagnostic, got %d", len(diags)) } - if len(diags) > 0 { - if diags[0].Severity != 1 { // Warning - t.Errorf("Expected warning severity, got %d", diags[0].Severity) - } - if diags[0].Summary != "Deprecated authentication method" { - t.Errorf("Unexpected summary: %s", diags[0].Summary) - } - } + require.Equal(t, len(diags), 1) + require.Equal(t, diags[0].Severity, diag.Warning) } func TestCheckDeprecatedPrometheusAuth_NoDeprecatedAuth(t *testing.T) { - d := schema.TestResourceDataRaw(t, resourceDataSource().Schema.Schema, map[string]interface{}{ - "name": "test-prometheus", - "type": "prometheus", - "url": "http://localhost:9090", - "json_data_encoded": `{"httpMethod":"POST"}`, - }) - - diags := checkDeprecatedPrometheusAuth(d) - - if len(diags) != 0 { - t.Errorf("Expected 0 diagnostics, got %d", len(diags)) - } -} - -func TestCheckDeprecatedPrometheusAuth_NonPrometheusDataSource(t *testing.T) { - d := schema.TestResourceDataRaw(t, resourceDataSource().Schema.Schema, map[string]interface{}{ - "name": "test-loki", - "type": "loki", - "url": "http://localhost:3100", - "json_data_encoded": `{"sigV4Auth":true}`, - }) - - diags := checkDeprecatedPrometheusAuth(d) - - if len(diags) != 0 { - t.Errorf("Expected 0 diagnostics for non-Prometheus datasource, got %d", len(diags)) - } -} - -func TestCheckDeprecatedPrometheusAuth_EmptyJSONData(t *testing.T) { d := schema.TestResourceDataRaw(t, resourceDataSource().Schema.Schema, map[string]interface{}{ "name": "test-prometheus", "type": "prometheus", @@ -93,23 +53,18 @@ func TestCheckDeprecatedPrometheusAuth_EmptyJSONData(t *testing.T) { diags := checkDeprecatedPrometheusAuth(d) - if len(diags) != 0 { - t.Errorf("Expected 0 diagnostics for empty JSON data, got %d", len(diags)) - } + require.Equal(t, len(diags), 0) } -func TestCheckDeprecatedPrometheusAuth_BothAuthMethods(t *testing.T) { +func TestCheckDeprecatedPrometheusAuth_NonPrometheusDataSource(t *testing.T) { d := schema.TestResourceDataRaw(t, resourceDataSource().Schema.Schema, map[string]interface{}{ - "name": "test-prometheus", - "type": "prometheus", + "name": "test-loki", + "type": "loki", "url": "http://localhost:9090", - "json_data_encoded": `{"sigV4Auth":true,"azureAuth":true}`, + "json_data_encoded": `{"sigV4Auth":true}`, }) diags := checkDeprecatedPrometheusAuth(d) - // Should get warnings for both auth methods - if len(diags) != 2 { - t.Errorf("Expected 2 diagnostics, got %d", len(diags)) - } + require.Equal(t, len(diags), 0) } From de51cea5f424ecfdc64d349787a9e798ed1512dc Mon Sep 17 00:00:00 2001 From: Andrew Hackmann Date: Fri, 14 Nov 2025 14:58:00 -0600 Subject: [PATCH 4/4] clean up --- .../resources/grafana/resource_data_source.go | 6 -- .../grafana/resource_data_source_test.go | 70 ------------------- 2 files changed, 76 deletions(-) diff --git a/internal/resources/grafana/resource_data_source.go b/internal/resources/grafana/resource_data_source.go index 1ce5d40ef..45bd7ab75 100644 --- a/internal/resources/grafana/resource_data_source.go +++ b/internal/resources/grafana/resource_data_source.go @@ -245,7 +245,6 @@ func CreateDataSource(ctx context.Context, d *schema.ResourceData, meta any) dia return diag.FromErr(err) } - // Check for deprecated authentication methods diags := checkDeprecatedPrometheusAuth(d) resp, err := client.Datasources.AddDataSource(dataSource) @@ -267,7 +266,6 @@ func UpdateDataSource(ctx context.Context, d *schema.ResourceData, meta any) dia return diag.FromErr(err) } - // Check for deprecated authentication methods diags := checkDeprecatedPrometheusAuth(d) body := models.UpdateDataSourceCommand{ @@ -467,13 +465,11 @@ func removeHeadersFromJSONData(input map[string]any) (map[string]any, map[string func checkDeprecatedPrometheusAuth(d *schema.ResourceData) diag.Diagnostics { var diags diag.Diagnostics - // Only check for Prometheus data sources dsType := d.Get("type").(string) if dsType != "prometheus" { return diags } - // Parse the json_data_encoded field jsonDataEncoded := d.Get("json_data_encoded").(string) if jsonDataEncoded == "" { return diags @@ -485,7 +481,6 @@ func checkDeprecatedPrometheusAuth(d *schema.ResourceData) diag.Diagnostics { return diags } - // Check for deprecated SigV4 authentication if sigV4Auth, ok := jsonData["sigV4Auth"].(bool); ok && sigV4Auth { diags = append(diags, diag.Diagnostic{ Severity: diag.Warning, @@ -494,7 +489,6 @@ func checkDeprecatedPrometheusAuth(d *schema.ResourceData) diag.Diagnostics { }) } - // Check for deprecated Azure authentication if azureAuth, ok := jsonData["azureCredentials"]; ok && azureAuth != nil { diags = append(diags, diag.Diagnostic{ Severity: diag.Warning, diff --git a/internal/resources/grafana/resource_data_source_test.go b/internal/resources/grafana/resource_data_source_test.go index 516d35446..ca803b0ae 100644 --- a/internal/resources/grafana/resource_data_source_test.go +++ b/internal/resources/grafana/resource_data_source_test.go @@ -458,73 +458,3 @@ resource "grafana_data_source" "test" { url = "http://localhost:9090" }`, orgName) } - -func TestAccDataSource_PrometheusDeprecatedSigV4Auth(t *testing.T) { - testutils.CheckOSSTestsEnabled(t) - - var dataSource models.DataSource - dsName := acctest.RandString(10) - - config := fmt.Sprintf(` -resource "grafana_data_source" "test" { - name = "%s" - type = "prometheus" - url = "http://localhost:9090" - - json_data_encoded = jsonencode({ - sigV4Auth = true - sigV4AuthType = "default" - sigV4Region = "us-east-1" - }) -}`, dsName) - - resource.ParallelTest(t, resource.TestCase{ - ProtoV5ProviderFactories: testutils.ProtoV5ProviderFactories, - CheckDestroy: datasourceCheckExists.destroyed(&dataSource, nil), - Steps: []resource.TestStep{ - { - Config: config, - Check: resource.ComposeTestCheckFunc( - datasourceCheckExists.exists("grafana_data_source.test", &dataSource), - resource.TestCheckResourceAttr("grafana_data_source.test", "name", dsName), - resource.TestCheckResourceAttr("grafana_data_source.test", "type", "prometheus"), - ), - ExpectNonEmptyPlan: false, - }, - }, - }) -} - -func TestAccDataSource_PrometheusDeprecatedAzureAuth(t *testing.T) { - testutils.CheckOSSTestsEnabled(t) - - var dataSource models.DataSource - dsName := acctest.RandString(10) - - config := fmt.Sprintf(` -resource "grafana_data_source" "test" { - name = "%s" - type = "prometheus" - url = "http://localhost:9090" - - json_data_encoded = jsonencode({ - azureAuth = true - }) -}`, dsName) - - resource.ParallelTest(t, resource.TestCase{ - ProtoV5ProviderFactories: testutils.ProtoV5ProviderFactories, - CheckDestroy: datasourceCheckExists.destroyed(&dataSource, nil), - Steps: []resource.TestStep{ - { - Config: config, - Check: resource.ComposeTestCheckFunc( - datasourceCheckExists.exists("grafana_data_source.test", &dataSource), - resource.TestCheckResourceAttr("grafana_data_source.test", "name", dsName), - resource.TestCheckResourceAttr("grafana_data_source.test", "type", "prometheus"), - ), - ExpectNonEmptyPlan: false, - }, - }, - }) -}