diff --git a/datadog/resource_datadog_metric_tag_configuration.go b/datadog/resource_datadog_metric_tag_configuration.go index 2a39cdab4..6cbbd61d0 100644 --- a/datadog/resource_datadog_metric_tag_configuration.go +++ b/datadog/resource_datadog_metric_tag_configuration.go @@ -26,9 +26,9 @@ func resourceDatadogMetricTagConfiguration() *schema.Resource { oldAggrs, newAggrs := diff.GetChange("aggregations") metricType, metricTypeOk := diff.GetOkExists("metric_type") tags, _ := diff.GetOkExists("tags") - excludeTagsMode, _ := diff.GetOkExists("exclude_tags_mode") + excludeTagsMode, excludeTagsModeOk := diff.GetOkExists("exclude_tags_mode") - if excludeTagsMode.(bool) && len(tags.(*schema.Set).List()) == 0 { + if excludeTagsModeOk && excludeTagsMode.(bool) && len(tags.(*schema.Set).List()) == 0 { return fmt.Errorf("cannot use exclude_tags_mode without configuring any tags") } @@ -126,10 +126,9 @@ func resourceDatadogMetricTagConfiguration() *schema.Resource { }, }, "exclude_tags_mode": { - Description: "Toggle to include/exclude tags as queryable for your metric. Can only be applied to metrics that have one or more tags configured.", + Description: "Toggle to include/exclude tags as queryable for your metric. Can only be applied to metrics that have one or more tags configured. If not set, the metric will allow all tags.", Type: schema.TypeBool, Optional: true, - Default: false, }, } }, @@ -185,14 +184,14 @@ func buildDatadogMetricTagConfiguration(d *schema.ResourceData) (*datadogV2.Metr attributes.SetIncludePercentiles(includePercentiles.(bool)) } - excludeTagsMode := d.Get("exclude_tags_mode") - - if excludeTagsMode.(bool) && len(stringTags) == 0 { - return nil, fmt.Errorf("cannot use exclude_tags_mode without configuring any tags") + if excludeTagsMode, ok := d.GetOkExists("exclude_tags_mode"); ok { + excludeTagsModeVal := excludeTagsMode.(bool) + if excludeTagsModeVal && len(stringTags) == 0 { + return nil, fmt.Errorf("cannot use exclude_tags_mode without configuring any tags") + } + attributes.SetExcludeTagsMode(excludeTagsModeVal) } - attributes.SetExcludeTagsMode(excludeTagsMode.(bool)) - aggregationsArray, aggregationsFieldSet := d.GetOk("aggregations") if aggregationsFieldSet { if *metricType == datadogV2.METRICTAGCONFIGURATIONMETRICTYPES_DISTRIBUTION { @@ -235,14 +234,14 @@ func buildDatadogMetricTagConfigurationUpdate(d *schema.ResourceData, existingMe attributes.SetIncludePercentiles(includePercentiles.(bool)) } - excludeTagsMode := d.Get("exclude_tags_mode") - - if excludeTagsMode.(bool) && len(stringTags) == 0 { - return nil, fmt.Errorf("cannot use exclude_tags_mode without configuring any tags") + if excludeTagsMode, ok := d.GetOkExists("exclude_tags_mode"); ok { + excludeTagsModeVal := excludeTagsMode.(bool) + if excludeTagsModeVal && len(stringTags) == 0 { + return nil, fmt.Errorf("cannot use exclude_tags_mode without configuring any tags") + } + attributes.SetExcludeTagsMode(excludeTagsModeVal) } - attributes.SetExcludeTagsMode(excludeTagsMode.(bool)) - aggregationsArray, aggregationsFieldSet := d.GetOk("aggregations") if aggregationsFieldSet { if *existingMetricType == datadogV2.METRICTAGCONFIGURATIONMETRICTYPES_DISTRIBUTION { @@ -317,9 +316,16 @@ func updateMetricTagConfigurationState(d *schema.ResourceData, metricTagConfigur return diag.FromErr(err) } - excludeTagsMode := attributes.GetExcludeTagsMode() - if err := d.Set("exclude_tags_mode", excludeTagsMode); err != nil { - return diag.FromErr(err) + // Only set exclude_tags_mode if it's explicitly present in the API response + if excludeTagsMode, ok := attributes.GetExcludeTagsModeOk(); ok { + if err := d.Set("exclude_tags_mode", *excludeTagsMode); err != nil { + return diag.FromErr(err) + } + } else { + // If the field is not present in the API response, clear it from state + if err := d.Set("exclude_tags_mode", nil); err != nil { + return diag.FromErr(err) + } } } @@ -346,10 +352,7 @@ func resourceDatadogMetricTagConfigurationRead(ctx context.Context, d *schema.Re d.SetId("") return nil } - if err != nil { - return utils.TranslateClientErrorDiag(err, httpresp, "metric tag configuration not found") - } - return diag.Errorf("error fetching metric tag configuration by name") + return utils.TranslateClientErrorDiag(err, httpresp, "metric tag configuration not found") } if httpresp.StatusCode != 200 { return diag.Errorf("error fetching metric tag configuration by name, unexpected status code %d", httpresp.StatusCode) @@ -375,7 +378,7 @@ func resourceDatadogMetricTagConfigurationUpdate(ctx context.Context, d *schema. if httpresp == nil { return diag.Errorf("error determining if tag configuration for metric exists") } - if httpresp != nil && httpresp.StatusCode == 404 { + if httpresp.StatusCode == 404 { return diag.Errorf("error updating tag configuration for metric, tag configuration does not exist") } if err := utils.CheckForUnparsed(metricTagConfigurationResponse); err != nil { diff --git a/datadog/tests/resource_datadog_metric_tag_configuration_test.go b/datadog/tests/resource_datadog_metric_tag_configuration_test.go index 8d062c08d..2c63724c3 100644 --- a/datadog/tests/resource_datadog_metric_tag_configuration_test.go +++ b/datadog/tests/resource_datadog_metric_tag_configuration_test.go @@ -94,6 +94,129 @@ func testAccCheckDatadogMetricTagConfigurationExcludeTagsModeError(uniq string) `, uniq) } +func TestAccDatadogMetricTagConfiguration_ExcludeTagsMode(t *testing.T) { + t.Parallel() + ctx, accProviders := testAccProviders(context.Background(), t) + uniqueMetricTagConfig := strings.ReplaceAll(uniqueEntityName(ctx, t), "-", "_") + accProvider := testAccProvider(t, accProviders) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: accProviders, + CheckDestroy: testAccCheckDatadogMetricTagConfigurationDestroy(accProvider), + Steps: []resource.TestStep{ + { + Config: testAccCheckDatadogMetricTagConfigurationExcludeTagsModeUnset(uniqueMetricTagConfig), + Check: resource.ComposeTestCheckFunc( + testAccCheckDatadogMetricTagConfigurationExists(accProvider), + resource.TestCheckResourceAttr( + "datadog_metric_tag_configuration.testing_unset", "metric_name", uniqueMetricTagConfig), + resource.TestCheckResourceAttr( + "datadog_metric_tag_configuration.testing_unset", "metric_type", "gauge"), + resource.TestCheckResourceAttr( + "datadog_metric_tag_configuration.testing_unset", "tags.#", "2"), + // When exclude_tags_mode is unset, it should not be present in state + resource.TestCheckNoResourceAttr( + "datadog_metric_tag_configuration.testing_unset", "exclude_tags_mode"), + ), + }, + { + Config: testAccCheckDatadogMetricTagConfigurationExcludeTagsModeTrue(uniqueMetricTagConfig), + Check: resource.ComposeTestCheckFunc( + testAccCheckDatadogMetricTagConfigurationExists(accProvider), + resource.TestCheckResourceAttr( + "datadog_metric_tag_configuration.testing_true", "metric_name", uniqueMetricTagConfig), + resource.TestCheckResourceAttr( + "datadog_metric_tag_configuration.testing_true", "exclude_tags_mode", "true"), + ), + }, + { + Config: testAccCheckDatadogMetricTagConfigurationExcludeTagsModeFalse(uniqueMetricTagConfig), + Check: resource.ComposeTestCheckFunc( + testAccCheckDatadogMetricTagConfigurationExists(accProvider), + resource.TestCheckResourceAttr( + "datadog_metric_tag_configuration.testing_false", "metric_name", uniqueMetricTagConfig), + resource.TestCheckResourceAttr( + "datadog_metric_tag_configuration.testing_false", "exclude_tags_mode", "false"), + ), + }, + { + // Test updating from false back to unset + Config: testAccCheckDatadogMetricTagConfigurationExcludeTagsModeUnset(uniqueMetricTagConfig), + Check: resource.ComposeTestCheckFunc( + testAccCheckDatadogMetricTagConfigurationExists(accProvider), + resource.TestCheckResourceAttr( + "datadog_metric_tag_configuration.testing_unset", "metric_name", uniqueMetricTagConfig), + resource.TestCheckNoResourceAttr( + "datadog_metric_tag_configuration.testing_unset", "exclude_tags_mode"), + ), + }, + }, + }) +} + +func testAccCheckDatadogMetricTagConfigurationExcludeTagsModeUnset(uniq string) string { + return fmt.Sprintf(` + resource "datadog_metric_tag_configuration" "testing_unset" { + metric_name = "%s" + metric_type = "gauge" + tags = ["env", "service"] + # exclude_tags_mode is not set - allows all tags + } + `, uniq) +} + +func testAccCheckDatadogMetricTagConfigurationExcludeTagsModeTrue(uniq string) string { + return fmt.Sprintf(` + resource "datadog_metric_tag_configuration" "testing_true" { + metric_name = "%s" + metric_type = "gauge" + tags = ["env", "service"] + exclude_tags_mode = true + } + `, uniq) +} + +func testAccCheckDatadogMetricTagConfigurationExcludeTagsModeFalse(uniq string) string { + return fmt.Sprintf(` + resource "datadog_metric_tag_configuration" "testing_false" { + metric_name = "%s" + metric_type = "gauge" + tags = ["env", "service"] + exclude_tags_mode = false + } + `, uniq) +} + +func testAccCheckDatadogMetricTagConfigurationExists(accProvider func() (*schema.Provider, error)) resource.TestCheckFunc { + return func(s *terraform.State) error { + provider, _ := accProvider() + meta := provider.Meta() + providerConf := meta.(*datadog.ProviderConfiguration) + apiInstances := providerConf.DatadogApiInstances + auth := providerConf.Auth + + for _, r := range s.RootModule().Resources { + if r.Type != "datadog_metric_tag_configuration" { + continue + } + + id := r.Primary.ID + _, resp, err := apiInstances.GetMetricsApiV2().ListTagConfigurationByName(auth, id) + + if err != nil { + return fmt.Errorf("received an error retrieving metric_tag_configuration: %s", err.Error()) + } + + if resp.StatusCode != 200 { + return fmt.Errorf("metric_tag_configuration %s does not exist", id) + } + } + + return nil + } +} + func testAccCheckDatadogMetricTagConfigurationDestroy(accProvider func() (*schema.Provider, error)) func(*terraform.State) error { return func(s *terraform.State) error { provider, _ := accProvider()