From 446a4a07330ede5d4ba60abe0067c0e5551dc41b Mon Sep 17 00:00:00 2001 From: Wodans Son <20408400+WodansSon@users.noreply.github.com> Date: Wed, 14 Feb 2024 18:36:35 -0700 Subject: [PATCH] Including #24830 * Initial Check-in... * Add regression test case... * Change fix design... * Address PR comments... * Updated description of the regression test case... --- .../cosmos/cosmosdb_account_resource.go | 32 ++++++--- .../cosmos/cosmosdb_account_resource_test.go | 68 ++++++++++++++++++- website/docs/r/cosmosdb_account.html.markdown | 36 +++++----- 3 files changed, 110 insertions(+), 26 deletions(-) diff --git a/internal/services/cosmos/cosmosdb_account_resource.go b/internal/services/cosmos/cosmosdb_account_resource.go index fb5df793e489..4665d07650f0 100644 --- a/internal/services/cosmos/cosmosdb_account_resource.go +++ b/internal/services/cosmos/cosmosdb_account_resource.go @@ -354,18 +354,20 @@ func resourceCosmosDbAccount() *pluginsdk.Resource { }, false), }, + // This value can only change if the 'consistency_level' is set to 'BoundedStaleness' "max_interval_in_seconds": { Type: pluginsdk.TypeInt, Optional: true, - Computed: true, + Default: 5, DiffSuppressFunc: suppressConsistencyPolicyStalenessConfiguration, ValidateFunc: validation.IntBetween(5, 86400), // single region values }, + // This value can only change if the 'consistency_level' is set to 'BoundedStaleness' "max_staleness_prefix": { Type: pluginsdk.TypeInt, Optional: true, - Computed: true, + Default: 100, DiffSuppressFunc: suppressConsistencyPolicyStalenessConfiguration, ValidateFunc: validation.IntBetween(10, 2147483647), // single region values }, @@ -1907,28 +1909,37 @@ func expandCosmosdbAccountBackup(input []interface{}, backupHasChange bool, crea switch attr["type"].(string) { case string(cosmosdb.BackupPolicyTypeContinuous): if v := attr["interval_in_minutes"].(int); v != 0 && !backupHasChange { - return nil, fmt.Errorf("`interval_in_minutes` can not be set when `type` in `backup` is `Continuous`") + return nil, fmt.Errorf("`interval_in_minutes` cannot be defined when the `backup.type` is set to %q", cosmosdb.BackupPolicyTypeContinuous) } + if v := attr["retention_in_hours"].(int); v != 0 && !backupHasChange { - return nil, fmt.Errorf("`retention_in_hours` can not be set when `type` in `backup` is `Continuous`") + return nil, fmt.Errorf("`retention_in_hours` cannot be defined when the `backup.type` is set to %q", cosmosdb.BackupPolicyTypeContinuous) } + if v := attr["storage_redundancy"].(string); v != "" && !backupHasChange { - return nil, fmt.Errorf("`storage_redundancy` can not be set when `type` in `backup` is `Continuous`") + return nil, fmt.Errorf("`storage_redundancy` cannot be defined when the `backup.type` is set to %q", cosmosdb.BackupPolicyTypeContinuous) } + return cosmosdb.ContinuousModeBackupPolicy{}, nil case string(cosmosdb.BackupPolicyTypePeriodic): if createMode != "" { - return nil, fmt.Errorf("`create_mode` only works when `backup.type` is `Continuous`") + return nil, fmt.Errorf("`create_mode` can only be defined when the `backup.type` is set to %q, got %q", cosmosdb.BackupPolicyTypeContinuous, cosmosdb.BackupPolicyTypePeriodic) } - return cosmosdb.PeriodicModeBackupPolicy{ + // Mirror the behavior of the old SDK... + periodicModeBackupPolicy := cosmosdb.PeriodicModeBackupPolicy{ PeriodicModeProperties: &cosmosdb.PeriodicModeProperties{ BackupIntervalInMinutes: utils.Int64(int64(attr["interval_in_minutes"].(int))), BackupRetentionIntervalInHours: utils.Int64(int64(attr["retention_in_hours"].(int))), - BackupStorageRedundancy: pointer.To(cosmosdb.BackupStorageRedundancy(attr["storage_redundancy"].(string))), }, - }, nil + } + + if v := attr["storage_redundancy"].(string); v != "" { + periodicModeBackupPolicy.PeriodicModeProperties.BackupStorageRedundancy = pointer.To(cosmosdb.BackupStorageRedundancy(attr["storage_redundancy"].(string))) + } + + return periodicModeBackupPolicy, nil default: return nil, fmt.Errorf("unknown `type` in `backup`:%+v", attr["type"].(string)) @@ -1953,13 +1964,16 @@ func flattenCosmosdbAccountBackup(input cosmosdb.BackupPolicy) ([]interface{}, e if v := backupPolicy.PeriodicModeProperties.BackupIntervalInMinutes; v != nil { interval = int(*v) } + if v := backupPolicy.PeriodicModeProperties.BackupRetentionIntervalInHours; v != nil { retention = int(*v) } + var storageRedundancy cosmosdb.BackupStorageRedundancy if backupPolicy.PeriodicModeProperties.BackupStorageRedundancy != nil { storageRedundancy = pointer.From(backupPolicy.PeriodicModeProperties.BackupStorageRedundancy) } + return []interface{}{ map[string]interface{}{ "type": string(cosmosdb.BackupPolicyTypePeriodic), diff --git a/internal/services/cosmos/cosmosdb_account_resource_test.go b/internal/services/cosmos/cosmosdb_account_resource_test.go index 8475cf561b66..07778117d1c4 100644 --- a/internal/services/cosmos/cosmosdb_account_resource_test.go +++ b/internal/services/cosmos/cosmosdb_account_resource_test.go @@ -992,7 +992,30 @@ func TestAccCosmosDBAccount_identity(t *testing.T) { }) } -func TestAccCosmosDBAccount_backup(t *testing.T) { +func TestAccCosmosDBAccount_storageRedundancyUndefined(t *testing.T) { + // Regression test for MSFT IcM where the SDK is supplied a 'nil' pointer for the + // 'storage_redundancy' field, the new transport layer would send an 'empty' string + // instead of omitting the field from the PUT call which would result in the API + // returning an error... + data := acceptance.BuildTestData(t, "azurerm_cosmosdb_account", "test") + r := CosmosDBAccountResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.storageRedundancyUndefined(data, cosmosdb.DatabaseAccountKindGlobalDocumentDB, cosmosdb.DefaultConsistencyLevelEventual), + Check: acceptance.ComposeAggregateTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("backup.0.type").HasValue("Periodic"), + check.That(data.ResourceName).Key("backup.0.interval_in_minutes").HasValue("120"), + check.That(data.ResourceName).Key("backup.0.retention_in_hours").HasValue("10"), + check.That(data.ResourceName).Key("backup.0.storage_redundancy").HasValue("Geo"), + ), + }, + data.ImportStep(), + }) +} + +func TestAccCosmosDBAccount_backupOnly(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_cosmosdb_account", "test") r := CosmosDBAccountResource{} @@ -1012,6 +1035,10 @@ func TestAccCosmosDBAccount_backup(t *testing.T) { Config: r.basicWithBackupPeriodic(data, cosmosdb.DatabaseAccountKindGlobalDocumentDB, cosmosdb.DefaultConsistencyLevelEventual), Check: acceptance.ComposeAggregateTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("backup.0.type").HasValue("Periodic"), + check.That(data.ResourceName).Key("backup.0.interval_in_minutes").HasValue("120"), + check.That(data.ResourceName).Key("backup.0.retention_in_hours").HasValue("10"), + check.That(data.ResourceName).Key("backup.0.storage_redundancy").HasValue("Geo"), ), }, data.ImportStep(), @@ -1020,6 +1047,9 @@ func TestAccCosmosDBAccount_backup(t *testing.T) { Check: acceptance.ComposeAggregateTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("backup.0.type").HasValue("Periodic"), + check.That(data.ResourceName).Key("backup.0.interval_in_minutes").HasValue("60"), + check.That(data.ResourceName).Key("backup.0.retention_in_hours").HasValue("8"), + check.That(data.ResourceName).Key("backup.0.storage_redundancy").HasValue("Local"), ), }, data.ImportStep(), @@ -3380,6 +3410,42 @@ resource "azurerm_cosmosdb_account" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomInteger, string(kind), string(consistency)) } +func (CosmosDBAccountResource) storageRedundancyUndefined(data acceptance.TestData, kind cosmosdb.DatabaseAccountKind, consistency cosmosdb.DefaultConsistencyLevel) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-cosmos-%d" + location = "%s" +} + +resource "azurerm_cosmosdb_account" "test" { + name = "acctest-ca-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + offer_type = "Standard" + kind = "%s" + + consistency_policy { + consistency_level = "%s" + } + + geo_location { + location = azurerm_resource_group.test.location + failover_priority = 0 + } + + backup { + type = "Periodic" + interval_in_minutes = 120 + retention_in_hours = 10 + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, string(kind), string(consistency)) +} + func (CosmosDBAccountResource) basicWithBackupPeriodicUpdate(data acceptance.TestData, kind cosmosdb.DatabaseAccountKind, consistency cosmosdb.DefaultConsistencyLevel) string { return fmt.Sprintf(` provider "azurerm" { diff --git a/website/docs/r/cosmosdb_account.html.markdown b/website/docs/r/cosmosdb_account.html.markdown index 3263fd9e6434..40ec813ffc8f 100644 --- a/website/docs/r/cosmosdb_account.html.markdown +++ b/website/docs/r/cosmosdb_account.html.markdown @@ -122,11 +122,11 @@ The following arguments are supported: * `create_mode` - (Optional) The creation mode for the CosmosDB Account. Possible values are `Default` and `Restore`. Changing this forces a new resource to be created. -~> **NOTE:** `create_mode` only works when `backup.type` is `Continuous`. +~> **Note:** `create_mode` can only be defined when the `backup.type` is set to `Continuous`. * `default_identity_type` - (Optional) The default identity for accessing Key Vault. Possible values are `FirstPartyIdentity`, `SystemAssignedIdentity` or `UserAssignedIdentity`. Defaults to `FirstPartyIdentity`. -~> **NOTE:** When `default_identity_type` is a `UserAssignedIdentity` it must include the User Assigned Identity ID in the following format: `UserAssignedIdentity=/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{userAssignedIdentityName}`. +~> **Note:** When `default_identity_type` is a `UserAssignedIdentity` it must include the User Assigned Identity ID in the following format: `UserAssignedIdentity=/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{userAssignedIdentityName}`. * `kind` - (Optional) Specifies the Kind of CosmosDB to create - possible values are `GlobalDocumentDB`, `MongoDB` and `Parse`. Defaults to `GlobalDocumentDB`. Changing this forces a new resource to be created. @@ -136,9 +136,9 @@ The following arguments are supported: * `ip_range_filter` - (Optional) CosmosDB Firewall Support: This value specifies the set of IP addresses or IP address ranges in CIDR form to be included as the allowed list of client IPs for a given database account. IP addresses/ranges must be comma separated and must not contain any spaces. -~> **NOTE:** To enable the "Allow access from the Azure portal" behavior, you should add the IP addresses provided by the [documentation](https://docs.microsoft.com/azure/cosmos-db/how-to-configure-firewall#allow-requests-from-the-azure-portal) to this list. +~> **Note:** To enable the "Allow access from the Azure portal" behavior, you should add the IP addresses provided by the [documentation](https://docs.microsoft.com/azure/cosmos-db/how-to-configure-firewall#allow-requests-from-the-azure-portal) to this list. -~> **NOTE:** To enable the "Accept connections from within public Azure datacenters" behavior, you should add `0.0.0.0` to the list, see the [documentation](https://docs.microsoft.com/azure/cosmos-db/how-to-configure-firewall#allow-requests-from-global-azure-datacenters-or-other-sources-within-azure) for more details. +~> **Note:** To enable the "Accept connections from within public Azure datacenters" behavior, you should add `0.0.0.0` to the list, see the [documentation](https://docs.microsoft.com/azure/cosmos-db/how-to-configure-firewall#allow-requests-from-global-azure-datacenters-or-other-sources-within-azure) for more details. * `enable_free_tier` - (Optional) Enable the Free Tier pricing option for this Cosmos DB account. Defaults to `false`. Changing this forces a new resource to be created. @@ -156,9 +156,9 @@ The following arguments are supported: * `key_vault_key_id` - (Optional) A versionless Key Vault Key ID for CMK encryption. Changing this forces a new resource to be created. -~> **NOTE:** When referencing an `azurerm_key_vault_key` resource, use `versionless_id` instead of `id` +~> **Note:** When referencing an `azurerm_key_vault_key` resource, use `versionless_id` instead of `id` -~> **NOTE:** In order to use a `Custom Key` from Key Vault for encryption you must grant Azure Cosmos DB Service access to your key vault. For instructions on how to configure your Key Vault correctly please refer to the [product documentation](https://docs.microsoft.com/azure/cosmos-db/how-to-setup-cmk#add-an-access-policy-to-your-azure-key-vault-instance) +~> **Note:** In order to use a `Custom Key` from Key Vault for encryption you must grant Azure Cosmos DB Service access to your key vault. For instructions on how to configure your Key Vault correctly please refer to the [product documentation](https://docs.microsoft.com/azure/cosmos-db/how-to-setup-cmk#add-an-access-policy-to-your-azure-key-vault-instance) * `virtual_network_rule` - (Optional) Specifies a `virtual_network_rule` block as defined below, used to define which subnets are allowed to access this CosmosDB account. @@ -182,7 +182,7 @@ The following arguments are supported: * `restore` - (Optional) A `restore` block as defined below. -~> **NOTE:** `restore` should be set when `create_mode` is `Restore`. +~> **Note:** `restore` should be set when `create_mode` is `Restore`. --- @@ -194,7 +194,7 @@ The `consistency_policy` block Configures the database consistency and supports * `max_staleness_prefix` - (Optional) When used with the Bounded Staleness consistency level, this value represents the number of stale requests tolerated. The accepted range for this value is `10` – `2147483647`. Defaults to `100`. Required when `consistency_level` is set to `BoundedStaleness`. -~> **Note:** `max_interval_in_seconds` and `max_staleness_prefix` can only be set to custom values when `consistency_level` is set to `BoundedStaleness` - otherwise they will return the default values shown above. +~> **Note:** `max_interval_in_seconds` and `max_staleness_prefix` can only be set to values other than default when the `consistency_level` is set to `BoundedStaleness`. --- @@ -212,11 +212,11 @@ A `capabilities` block Configures the capabilities to be enabled for this Cosmos * `name` - (Required) The capability to enable - Possible values are `AllowSelfServeUpgradeToMongo36`, `DisableRateLimitingResponses`, `EnableAggregationPipeline`, `EnableCassandra`, `EnableGremlin`, `EnableMongo`, `EnableMongo16MBDocumentSupport`, `EnableMongoRetryableWrites`, `EnableMongoRoleBasedAccessControl`, `EnablePartialUniqueIndex`, `EnableServerless`, `EnableTable`, `EnableTtlOnCustomPath`, `EnableUniqueCompoundNestedDocs`, `MongoDBv3.4` and `mongoEnableDocLevelTTL`. -~> **NOTE:** Setting `MongoDBv3.4` also requires setting `EnableMongo`. +~> **Note:** Setting `MongoDBv3.4` also requires setting `EnableMongo`. -~> **NOTE:** Only `AllowSelfServeUpgradeToMongo36`, `DisableRateLimitingResponses`, `EnableAggregationPipeline`, `MongoDBv3.4`, `EnableMongoRetryableWrites`, `EnableMongoRoleBasedAccessControl`, `EnableUniqueCompoundNestedDocs`, `EnableMongo16MBDocumentSupport`, `mongoEnableDocLevelTTL`, `EnableTtlOnCustomPath` and `EnablePartialUniqueIndex` can be added to an existing Cosmos DB account. +~> **Note:** Only `AllowSelfServeUpgradeToMongo36`, `DisableRateLimitingResponses`, `EnableAggregationPipeline`, `MongoDBv3.4`, `EnableMongoRetryableWrites`, `EnableMongoRoleBasedAccessControl`, `EnableUniqueCompoundNestedDocs`, `EnableMongo16MBDocumentSupport`, `mongoEnableDocLevelTTL`, `EnableTtlOnCustomPath` and `EnablePartialUniqueIndex` can be added to an existing Cosmos DB account. -~> **NOTE:** Only `DisableRateLimitingResponses` and `EnableMongoRetryableWrites` can be removed from an existing Cosmos DB account. +~> **Note:** Only `DisableRateLimitingResponses` and `EnableMongoRetryableWrites` can be removed from an existing Cosmos DB account. --- @@ -241,13 +241,17 @@ A `capacity` block supports the following: A `backup` block supports the following: -* `type` - (Required) The type of the `backup`. Possible values are `Continuous` and `Periodic`. Migration of `Periodic` to `Continuous` is one-way, changing `Continuous` to `Periodic` forces a new resource to be created. +* `type` - (Required) The type of the `backup`. Possible values are `Continuous` and `Periodic`. -* `interval_in_minutes` - (Optional) The interval in minutes between two backups. This is configurable only when `type` is `Periodic`. Possible values are between 60 and 1440. +~> **Note:** Migration of `Periodic` to `Continuous` is one-way, changing `Continuous` to `Periodic` forces a new resource to be created. -* `retention_in_hours` - (Optional) The time in hours that each backup is retained. This is configurable only when `type` is `Periodic`. Possible values are between 8 and 720. +* `interval_in_minutes` - (Optional) The interval in minutes between two backups. Possible values are between 60 and 1440. Defaults to `240`. -* `storage_redundancy` - (Optional) The storage redundancy is used to indicate the type of backup residency. This is configurable only when `type` is `Periodic`. Possible values are `Geo`, `Local` and `Zone`. +* `retention_in_hours` - (Optional) The time in hours that each backup is retained. Possible values are between 8 and 720. Defaults to `8`. + +* `storage_redundancy` - (Optional) The storage redundancy is used to indicate the type of backup residency. Possible values are `Geo`, `Local` and `Zone`. Defaults to `Geo`. + +~> **Note:** You can only configure `interval_in_minutes`, `retention_in_hours` and `storage_redundancy` when the `type` field is set to `Periodic`. --- @@ -277,7 +281,7 @@ A `restore` block supports the following: * `source_cosmosdb_account_id` - (Required) The resource ID of the restorable database account from which the restore has to be initiated. The example is `/subscriptions/{subscriptionId}/providers/Microsoft.DocumentDB/locations/{location}/restorableDatabaseAccounts/{restorableDatabaseAccountName}`. Changing this forces a new resource to be created. -~> **NOTE:** Any database account with `Continuous` type (live account or accounts deleted in last 30 days) is a restorable database account and there cannot be Create/Update/Delete operations on the restorable database accounts. They can only be read and retrieved by `azurerm_cosmosdb_restorable_database_accounts`. +~> **Note:** Any database account with `Continuous` type (live account or accounts deleted in last 30 days) is a restorable database account and there cannot be Create/Update/Delete operations on the restorable database accounts. They can only be read and retrieved by `azurerm_cosmosdb_restorable_database_accounts`. * `restore_timestamp_in_utc` - (Required) The creation time of the database or the collection (Datetime Format `RFC 3339`). Changing this forces a new resource to be created.