Skip to content

Commit

Permalink
feat: Support pinned_fcv in attribute in cluster resource and data …
Browse files Browse the repository at this point in the history
…sources (#2817)

* add resource and data source implementations

* apply docs changes

* reuse checks from advanced cluster test for fcv pinning

* simplify if
  • Loading branch information
AgustinBettati authored Dec 11, 2024
1 parent 3f45633 commit 8bd5d4e
Show file tree
Hide file tree
Showing 14 changed files with 281 additions and 64 deletions.
23 changes: 23 additions & 0 deletions .changelog/2817.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
```release-note:enhancement
resource/mongodbatlas_cluster: Adds `pinned_fcv` attribute
```

```release-note:enhancement
data-source/mongodbatlas_cluster: Adds `pinned_fcv` attribute
```

```release-note:enhancement
data-source/mongodbatlas_clusters: Adds `pinned_fcv` attribute
```

```release-note:bug
resource/mongodbatlas_cluster: `mongo_db_major_version` attribute is populated with binary version when FCV pin is active
```

```release-note:bug
data-source/mongodbatlas_cluster: `mongo_db_major_version` attribute is populated with binary version when FCV pin is active
```

```release-note:bug
data-source/mongodbatlas_clusters: `mongo_db_major_version` attribute is populated with binary version when FCV pin is active
```
6 changes: 6 additions & 0 deletions docs/data-sources/cluster.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ In addition to all arguments above, the following attributes are exported:
* `tags` - Set that contains key-value pairs between 1 to 255 characters in length for tagging and categorizing the cluster. See [below](#tags).
* `labels` - Set that contains key-value pairs between 1 to 255 characters in length for tagging and categorizing the cluster. See [below](#labels). **DEPRECATED** Use `tags` instead.
* `mongo_db_major_version` - Indicates the version of the cluster to deploy.
* `pinned_fcv` - The pinned Feature Compatibility Version (FCV) with its associated expiration date. See [below](#pinned-fcv).
* `num_shards` - Indicates whether the cluster is a replica set or a sharded cluster.
* `cloud_backup` - Flag indicating if the cluster uses Cloud Backup Snapshots for backups.
* `termination_protection_enabled` - Flag that indicates whether termination protection is enabled on the cluster. If set to true, MongoDB Cloud won't delete the cluster. If set to false, MongoDB Cloud will delete the cluster.
Expand Down Expand Up @@ -233,4 +234,9 @@ Contains a key-value pair that tags that the cluster was created by a Terraform
* `transaction_lifetime_limit_seconds` - Lifetime, in seconds, of multi-document transactions. Defaults to 60 seconds.
* `change_stream_options_pre_and_post_images_expire_after_seconds` - (Optional) The minimum pre- and post-image retention time in seconds. This parameter is only supported for MongoDB version 6.0 and above. Defaults to `-1`(off).

### Pinned FCV

* `expiration_date` - Expiration date of the fixed FCV. This value is in the ISO 8601 timestamp format (e.g. "2024-12-04T16:25:00Z").
* `version` - Feature compatibility version of the cluster.

See detailed information for arguments and attributes: [MongoDB API Clusters](https://docs.atlas.mongodb.com/reference/api/clusters-create-one/)
5 changes: 5 additions & 0 deletions docs/data-sources/clusters.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ In addition to all arguments above, the following attributes are exported:
* `tags` - Set that contains key-value pairs between 1 to 255 characters in length for tagging and categorizing the cluster. See [below](#tags).
* `labels` - Set that contains key-value pairs between 1 to 255 characters in length for tagging and categorizing the cluster. See [below](#labels). **DEPRECATED** Use `tags` instead.
* `mongo_db_major_version` - Indicates the version of the cluster to deploy.
* `pinned_fcv` - The pinned Feature Compatibility Version (FCV) with its associated expiration date. See [below](#pinned-fcv).
* `num_shards` - Indicates whether the cluster is a replica set or a sharded cluster.
* `provider_backup_enabled` - Flag indicating if the cluster uses Cloud Backup Snapshots for backups. **DEPRECATED** Use `cloud_backup` instead.
* `cloud_backup` - Flag indicating if the cluster uses Cloud Backup Snapshots for backups.
Expand Down Expand Up @@ -220,5 +221,9 @@ Contains a key-value pair that tags that the cluster was created by a Terraform
* `sample_refresh_interval_bi_connector` - Interval in seconds at which the mongosqld process re-samples data to create its relational schema. The default value is 300. The specified value must be a positive integer. Available only for Atlas deployments in which BI Connector for Atlas is enabled.
* `change_stream_options_pre_and_post_images_expire_after_seconds` - (Optional) The minimum pre- and post-image retention time in seconds. This parameter is only supported for MongoDB version 6.0 and above. Defaults to `-1`(off).

### Pinned FCV

* `expiration_date` - Expiration date of the fixed FCV. This value is in the ISO 8601 timestamp format (e.g. "2024-12-04T16:25:00Z").
* `version` - Feature compatibility version of the cluster.

See detailed information for arguments and attributes: [MongoDB API Clusters](https://docs.atlas.mongodb.com/reference/api/clusters-create-one/)
6 changes: 6 additions & 0 deletions docs/resources/cluster.md
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ But in order to explicitly change `provider_instance_size_name` comment the `lif
* `tags` - (Optional) Set that contains key-value pairs between 1 to 255 characters in length for tagging and categorizing the cluster. See [below](#tags).
* `labels` - (Optional) Set that contains key-value pairs between 1 to 255 characters in length for tagging and categorizing the cluster. See [below](#labels). **DEPRECATED** Use `tags` instead.
* `mongo_db_major_version` - (Optional) Version of the cluster to deploy. Atlas supports all the MongoDB versions that have **not** reached [End of Live](https://www.mongodb.com/legal/support-policy/lifecycles) for M10+ clusters. If omitted, Atlas deploys the cluster with the default version. For more details, see [documentation](https://www.mongodb.com/docs/atlas/reference/faq/database/#which-versions-of-mongodb-do-service-clusters-use-). Atlas always deploys the cluster with the latest stable release of the specified version. See [Release Notes](https://www.mongodb.com/docs/upcoming/release-notes/) for latest Current Stable Release.
* `pinned_fcv` - (Optional) Pins the Feature Compatibility Version (FCV) to the current MongoDB version with a provided expiration date. To unpin the FCV the `pinned_fcv` attribute must be removed. This operation can take several minutes as the request processes through the MongoDB data plane. Once FCV is unpinned it will not be possible to downgrade the `mongo_db_major_version`. It is advised that updates to `pinned_fcv` are done isolated from other cluster changes. If a plan contains multiple changes, the FCV change will be applied first. If FCV is unpinned past the expiration date the `pinned_fcv` attribute must be removed. The following [knowledge hub article](https://kb.corp.mongodb.com/article/000021785/) and [FCV documentation](https://www.mongodb.com/docs/atlas/tutorial/major-version-change/#manage-feature-compatibility--fcv--during-upgrades) can be referenced for more details. See [below](#pinned-fcv).
* `num_shards` - (Optional) Selects whether the cluster is a replica set or a sharded cluster. If you use the replicationSpecs parameter, you must set num_shards.
* `pit_enabled` - (Optional) - Flag that indicates if the cluster uses Continuous Cloud Backup. If set to true, cloud_backup must also be set to true.
* `cloud_backup` - (Optional) Flag indicating if the cluster uses Cloud Backup for backups.
Expand Down Expand Up @@ -526,6 +527,11 @@ To learn more, see [Resource Tags](https://dochub.mongodb.org/core/add-cluster-t

-> **NOTE:** MongoDB Atlas doesn't display your labels.

### Pinned FCV

* `expiration_date` - (Required) Expiration date of the fixed FCV. This value is in the ISO 8601 timestamp format (e.g. "2024-12-04T16:25:00Z"). Note that this field cannot exceed 4 weeks from the pinned date.
* `version` - Feature compatibility version of the cluster.

## Attributes Reference

In addition to all arguments above, the following attributes are exported:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,7 @@ func flattenAdvancedClusters(ctx context.Context, connV220240530 *admin20240530.
"redact_client_log_data": cluster.GetRedactClientLogData(),
"config_server_management_mode": cluster.GetConfigServerManagementMode(),
"config_server_type": cluster.GetConfigServerType(),
"pinned_fcv": flattenPinnedFCV(cluster),
"pinned_fcv": FlattenPinnedFCV(cluster),
}
results = append(results, result)
}
Expand Down Expand Up @@ -451,7 +451,7 @@ func flattenAdvancedClustersOldSDK(ctx context.Context, connV20240530 *admin2024
"redact_client_log_data": clusterDescNew.GetRedactClientLogData(),
"config_server_management_mode": clusterDescNew.GetConfigServerManagementMode(),
"config_server_type": clusterDescNew.GetConfigServerType(),
"pinned_fcv": flattenPinnedFCV(clusterDescNew),
"pinned_fcv": FlattenPinnedFCV(clusterDescNew),
}
results = append(results, result)
}
Expand Down
2 changes: 1 addition & 1 deletion internal/service/advancedcluster/model_advanced_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ func CheckRegionConfigsPriorityOrderOld(regionConfigs []admin20240530.Replicatio
return nil
}

func flattenPinnedFCV(cluster *admin.ClusterDescription20240805) []map[string]string {
func FlattenPinnedFCV(cluster *admin.ClusterDescription20240805) []map[string]string {
if cluster.FeatureCompatibilityVersionExpirationDate == nil { // pinned_fcv is defined in state only if featureCompatibilityVersionExpirationDate is present in cluster response
return nil
}
Expand Down
16 changes: 8 additions & 8 deletions internal/service/advancedcluster/resource_advanced_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,7 @@ func resourceCreate(ctx context.Context, d *schema.ResourceData, meta any) diag.
}

if pinnedFCVBlock, _ := d.Get("pinned_fcv").([]any); len(pinnedFCVBlock) > 0 {
if diags := pinFCV(ctx, connV2, projectID, cluster.GetName(), pinnedFCVBlock[0]); diags.HasError() {
if diags := PinFCV(ctx, connV2, projectID, cluster.GetName(), pinnedFCVBlock[0]); diags.HasError() {
return diags
}
waitForChanges = true
Expand Down Expand Up @@ -648,7 +648,7 @@ func resourceRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Di
clusterResp = cluster
}

warning := warningIfFCVExpiredOrUnpinnedExternally(d, clusterResp) // has to be called before pinned_fcv value is updated in ResourceData to know prior state value
warning := WarningIfFCVExpiredOrUnpinnedExternally(d, clusterResp) // has to be called before pinned_fcv value is updated in ResourceData to know prior state value
diags := setRootFields(d, clusterResp, true)
if diags.HasError() {
return diags
Expand Down Expand Up @@ -806,14 +806,14 @@ func setRootFields(d *schema.ResourceData, cluster *admin.ClusterDescription2024
return diag.FromErr(fmt.Errorf(ErrorClusterAdvancedSetting, "config_server_management_mode", clusterName, err))
}

if err := d.Set("pinned_fcv", flattenPinnedFCV(cluster)); err != nil {
if err := d.Set("pinned_fcv", FlattenPinnedFCV(cluster)); err != nil {
return diag.FromErr(fmt.Errorf(ErrorClusterAdvancedSetting, "pinned_fcv", clusterName, err))
}

return nil
}

func warningIfFCVExpiredOrUnpinnedExternally(d *schema.ResourceData, cluster *admin.ClusterDescription20240805) diag.Diagnostics {
func WarningIfFCVExpiredOrUnpinnedExternally(d *schema.ResourceData, cluster *admin.ClusterDescription20240805) diag.Diagnostics {
pinnedFCVBlock, _ := d.Get("pinned_fcv").([]any)
presentInState := len(pinnedFCVBlock) > 0
pinIsActive := cluster.FeatureCompatibilityVersionExpirationDate != nil
Expand Down Expand Up @@ -900,7 +900,7 @@ func resourceUpdate(ctx context.Context, d *schema.ResourceData, meta any) diag.
timeout := d.Timeout(schema.TimeoutUpdate)

// FCV update is intentionally handled before other cluster updates, and will wait for cluster to reach IDLE state before continuing
if diags := handlePinnedFCVUpdate(ctx, connV2, projectID, clusterName, d, timeout); diags != nil {
if diags := HandlePinnedFCVUpdate(ctx, connV2, projectID, clusterName, d, timeout); diags != nil {
return diags
}

Expand Down Expand Up @@ -1002,13 +1002,13 @@ func resourceUpdate(ctx context.Context, d *schema.ResourceData, meta any) diag.
return resourceRead(ctx, d, meta)
}

func handlePinnedFCVUpdate(ctx context.Context, connV2 *admin.APIClient, projectID, clusterName string, d *schema.ResourceData, timeout time.Duration) diag.Diagnostics {
func HandlePinnedFCVUpdate(ctx context.Context, connV2 *admin.APIClient, projectID, clusterName string, d *schema.ResourceData, timeout time.Duration) diag.Diagnostics {
if d.HasChange("pinned_fcv") {
pinnedFCVBlock, _ := d.Get("pinned_fcv").([]any)
isFCVPresentInConfig := len(pinnedFCVBlock) > 0
if isFCVPresentInConfig {
// pinned_fcv has been defined or updated expiration date
if diags := pinFCV(ctx, connV2, projectID, clusterName, pinnedFCVBlock[0]); diags.HasError() {
if diags := PinFCV(ctx, connV2, projectID, clusterName, pinnedFCVBlock[0]); diags.HasError() {
return diags
}
} else {
Expand All @@ -1025,7 +1025,7 @@ func handlePinnedFCVUpdate(ctx context.Context, connV2 *admin.APIClient, project
return nil
}

func pinFCV(ctx context.Context, connV2 *admin.APIClient, projectID, clusterName string, fcvBlock any) diag.Diagnostics {
func PinFCV(ctx context.Context, connV2 *admin.APIClient, projectID, clusterName string, fcvBlock any) diag.Diagnostics {
req := admin.PinFCV{}
if nestedObj, ok := fcvBlock.(map[string]any); ok {
expDateStrPtr := conversion.StringPtr(cast.ToString(nestedObj["expiration_date"]))
Expand Down
32 changes: 6 additions & 26 deletions internal/service/advancedcluster/resource_advanced_cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -992,31 +992,31 @@ func TestAccClusterAdvancedCluster_pinnedFCVWithVersionUpgradeAndDowngrade(t *te
Steps: []resource.TestStep{
{
Config: configFCVPinning(orgID, projectName, clusterName, nil, "7.0"),
Check: checkFCVPinningConfig(7, nil, nil),
Check: acc.CheckFCVPinningConfig(resourceName, dataSourceName, dataSourcePluralName, 7, nil, nil),
},
{ // pins fcv
Config: configFCVPinning(orgID, projectName, clusterName, &firstExpirationDate, "7.0"),
Check: checkFCVPinningConfig(7, admin.PtrString(firstExpirationDate), admin.PtrInt(7)),
Check: acc.CheckFCVPinningConfig(resourceName, dataSourceName, dataSourcePluralName, 7, admin.PtrString(firstExpirationDate), admin.PtrInt(7)),
},
{ // using incorrect format
Config: configFCVPinning(orgID, projectName, clusterName, &invalidDateFormat, "7.0"),
ExpectError: regexp.MustCompile("expiration_date format is incorrect: " + invalidDateFormat),
},
{ // updates expiration date of fcv
Config: configFCVPinning(orgID, projectName, clusterName, &updatedExpirationDate, "7.0"),
Check: checkFCVPinningConfig(7, admin.PtrString(updatedExpirationDate), admin.PtrInt(7)),
Check: acc.CheckFCVPinningConfig(resourceName, dataSourceName, dataSourcePluralName, 7, admin.PtrString(updatedExpirationDate), admin.PtrInt(7)),
},
{ // upgrade mongodb version with fcv pinned
Config: configFCVPinning(orgID, projectName, clusterName, &updatedExpirationDate, "8.0"),
Check: checkFCVPinningConfig(8, admin.PtrString(updatedExpirationDate), admin.PtrInt(7)),
Check: acc.CheckFCVPinningConfig(resourceName, dataSourceName, dataSourcePluralName, 8, admin.PtrString(updatedExpirationDate), admin.PtrInt(7)),
},
{ // downgrade mongodb version with fcv pinned
Config: configFCVPinning(orgID, projectName, clusterName, &updatedExpirationDate, "7.0"),
Check: checkFCVPinningConfig(7, admin.PtrString(updatedExpirationDate), admin.PtrInt(7)),
Check: acc.CheckFCVPinningConfig(resourceName, dataSourceName, dataSourcePluralName, 7, admin.PtrString(updatedExpirationDate), admin.PtrInt(7)),
},
{ // unpins fcv
Config: configFCVPinning(orgID, projectName, clusterName, nil, "7.0"),
Check: checkFCVPinningConfig(7, nil, nil),
Check: acc.CheckFCVPinningConfig(resourceName, dataSourceName, dataSourcePluralName, 7, nil, nil),
},
},
})
Expand Down Expand Up @@ -2608,23 +2608,3 @@ func configFCVPinning(orgID, projectName, clusterName string, pinningExpirationD
}
`, orgID, projectName, clusterName, mongoDBMajorVersion, pinnedFCVAttr)
}

func checkFCVPinningConfig(mongoDBMajorVersion int, pinningExpirationDate *string, fcvVersion *int) resource.TestCheckFunc {
mapChecks := map[string]string{
"mongo_db_major_version": fmt.Sprintf("%d.0", mongoDBMajorVersion),
}

if pinningExpirationDate != nil {
mapChecks["pinned_fcv.0.expiration_date"] = *pinningExpirationDate
} else {
mapChecks["pinned_fcv.#"] = "0"
}

if fcvVersion != nil {
mapChecks["pinned_fcv.0.version"] = fmt.Sprintf("%d.0", *fcvVersion)
}

additionalCheck := resource.TestCheckResourceAttrWith(resourceName, "mongo_db_version", acc.MatchesExpression(fmt.Sprintf("%d..*", mongoDBMajorVersion)))

return acc.CheckRSAndDS(resourceName, admin.PtrString(dataSourceName), admin.PtrString(dataSourcePluralName), []string{}, mapChecks, additionalCheck)
}
33 changes: 27 additions & 6 deletions internal/service/cluster/data_source_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,22 @@ func DataSource() *schema.Resource {
Type: schema.TypeBool,
Computed: true,
},
"pinned_fcv": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"version": {
Type: schema.TypeString,
Computed: true,
},
"expiration_date": {
Type: schema.TypeString,
Computed: true,
},
},
},
},
},
}
}
Expand Down Expand Up @@ -386,10 +402,6 @@ func dataSourceRead(ctx context.Context, d *schema.ResourceData, meta any) diag.
return diag.FromErr(fmt.Errorf(advancedcluster.ErrorClusterSetting, "encryption_at_rest_provider", clusterName, err))
}

if err := d.Set("mongo_db_major_version", cluster.MongoDBMajorVersion); err != nil {
return diag.FromErr(fmt.Errorf(advancedcluster.ErrorClusterSetting, "mongo_db_major_version", clusterName, err))
}

// Avoid Global Cluster issues. (NumShards is not present in Global Clusters)
if cluster.NumShards != nil {
if err := d.Set("num_shards", cluster.NumShards); err != nil {
Expand Down Expand Up @@ -491,14 +503,23 @@ func dataSourceRead(ctx context.Context, d *schema.ResourceData, meta any) diag.
return diag.FromErr(err)
}

redactClientLogData, err := newAtlasGet(ctx, connV2, projectID, clusterName)
latestClusterModel, err := newAtlasGet(ctx, connV2, projectID, clusterName)
if err != nil {
return diag.FromErr(fmt.Errorf(errorClusterRead, clusterName, err))
}
if err := d.Set("redact_client_log_data", redactClientLogData); err != nil {

if err := d.Set("mongo_db_major_version", latestClusterModel.MongoDBMajorVersion); err != nil { // uses 2024-08-05 or above as it has fix for correct value when FCV is active
return diag.FromErr(fmt.Errorf(advancedcluster.ErrorClusterSetting, "mongo_db_major_version", clusterName, err))
}

if err := d.Set("redact_client_log_data", latestClusterModel.GetRedactClientLogData()); err != nil {
return diag.FromErr(fmt.Errorf(advancedcluster.ErrorClusterSetting, "redact_client_log_data", clusterName, err))
}

if err := d.Set("pinned_fcv", advancedcluster.FlattenPinnedFCV(latestClusterModel)); err != nil {
return diag.FromErr(fmt.Errorf(advancedcluster.ErrorClusterSetting, "pinned_fcv", clusterName, err))
}

d.SetId(cluster.ID)

return nil
Expand Down
Loading

0 comments on commit 8bd5d4e

Please sign in to comment.