-
Notifications
You must be signed in to change notification settings - Fork 209
doc: Implements v2 and v3 MongoDB Atlas cluster migration examples #3050
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 18 commits
4c15b16
b57ba7f
6bda23f
746389b
95cea88
d9976bf
46014a3
fd587fd
51e1b37
e73b91f
2f52072
12265f3
dee594f
d5a9be7
e199021
12c71e7
bc06455
02e08a8
dcbfbd6
720fc59
c12fbdd
58de2de
ccbaa97
f2d1520
fef8a10
9b1e065
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,137 @@ | ||
| # Module Maintainer - `mongodbatlas_cluster` to `mongodbatlas_advanced_cluster` | ||
|
|
||
| The purpose of this example is to demonstrate how a Terraform module can help in the migration from `mongodbatlas_cluster` to `mongodbatlas_advanced_cluster`. | ||
| The example contains three module versions: | ||
| The example contains three module versions which represent the three steps of the migration: | ||
|
|
||
| <!-- Update Variable count --> | ||
| Version | Purpose | Variables | Resources | ||
| Step | Purpose | Resources | ||
| --- | --- | --- | --- | ||
| [v1](./v1) | Baseline | 5 | `mongodbatlas_cluster` | ||
| [v2](./v2) | Migrate to advanced_cluster with no change in variables or plan | 5 | `mongodbatlas_advanced_cluster` | ||
lantoli marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| [v3](./v3) | Use the latest features of advanced_cluster | 10 | `mongodbatlas_advanced_cluster` | ||
| [Step 1](./v1) | Baseline | `mongodbatlas_cluster` | ||
| [Step 2](./v2) | Migrate to advanced_cluster with no change in variables or plan | `mongodbatlas_advanced_cluster` | ||
| [Step 3](./v3) | Use the latest features of advanced_cluster | `mongodbatlas_advanced_cluster` | ||
|
|
||
EspenAlbert marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| <!-- TODO: Add highlights of module implementations --> | ||
| The rest of this document summarizes the different implementations: | ||
|
|
||
| - [Step 1: Module `v1` Implementation Summary](#step-1-module-v1-implementation-summary) | ||
| - [`variables.tf`](#variablestf) | ||
| - [`main.tf`](#maintf) | ||
| - [`outputs.tf`](#outputstf) | ||
| - [Step 2: Module `v2` Implementation Changes and Highlights](#step-2-module-v2-implementation-changes-and-highlights) | ||
| - [`variables.tf` unchanged from `v1`](#variablestf-unchanged-from-v1) | ||
| - [`versions.tf`](#versionstf) | ||
| - [`main.tf`](#maintf-1) | ||
| - [`outputs.tf`](#outputstf-1) | ||
| - [Step 3: Module `v3` Implementation Changes and Highlights](#step-3-module-v3-implementation-changes-and-highlights) | ||
| - [`variables.tf`](#variablestf-1) | ||
| - [`main.tf`](#maintf-2) | ||
| - [`outputs.tf`](#outputstf-2) | ||
|
|
||
|
|
||
| ## Step 1: Module `v1` Implementation Summary | ||
| This module creates a `mongodbatlas_cluster` | ||
|
|
||
| ### [`variables.tf`](v1/variables.tf) | ||
| An abstraction for the `mongodbatlas_cluster` resource: | ||
| - Not all arguments are exposed, but the arguments follow the schema closely | ||
| - `disk_size` and `auto_scaling_disk_gb_enabled` are mutually exclusive and validated in the `precondition` in `main.tf` | ||
|
|
||
| ### [`main.tf`](v1/main.tf) | ||
| It uses `dynamic` blocks to represent: | ||
| - `tags` | ||
| - `replication_specs` | ||
| - `regions_config` (nested inside replication_specs) | ||
|
|
||
| ### [`outputs.tf`](v1/outputs.tf) | ||
| - Expose some attributes of `mongodbatlas_cluster` but also the full resource with `mongodbatlas_cluster` output variable: | ||
| ```terraform | ||
| output "mongodbatlas_cluster" { | ||
| value = mongodbatlas_cluster.this | ||
| description = "Full cluster configuration for mongodbatlas_cluster resource" | ||
| } | ||
| ``` | ||
|
|
||
| ## Step 2: Module `v2` Implementation Changes and Highlights | ||
| This module uses HCL code to create a `mongodbatlas_advanced_cluster` resource that is compatible with the input variables of `v1`. | ||
|
|
||
| ### [`variables.tf`](v2/variables.tf) unchanged from `v1` | ||
| ### [`versions.tf`](v2/versions.tf) | ||
| - `required_version` of Terraform CLI bumped to `# todo: minimum moved block supported version` for `moved` block support | ||
| - `mongodbatlas.version` bumped to `# todo: PLACEHOLDER_TPF_RELEASE` for new `mongodbatlas_advanced_cluster` v2 schema support | ||
|
|
||
| ### [`main.tf`](v2/main.tf) | ||
| <!-- TODO: Update link to (schema v2) docs page --> | ||
| - `locals.replication_specs` an intermediate variable transforming the `variables` to a compatible [replication_specs](https://registry.terraform.io/providers/mongodb/mongodbatlas/latest/docs/resources/advanced_cluster#replication_specs-1) for `mongodbatlas_advanced_cluster` | ||
EspenAlbert marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| - We use the Terraform builtin [range](https://developer.hashicorp.com/terraform/language/functions/range) function (`range(old_spec.num_shards)`) to flatten `num_shards` | ||
| - We expand `read_only_specs` and `electable_specs` into nested attributes | ||
| - We use the `var.provider_name` in the `region_configs.*.instance_size` | ||
| - `moved` block: | ||
| ```terraform | ||
| moved { | ||
| from = mongodbatlas_cluster.this | ||
| to = mongodbatlas_advanced_cluster.this | ||
| } | ||
| ``` | ||
| - `resource "mongodbatlas_advanced_cluster" "this"` | ||
| - We reference the `local.replication_specs` as input to `replication_specs` (`replication_specs = local.replication_specs`) | ||
| - Tags can be passed directly instead of the dynamic block (`tags = var.tags`) | ||
| - Adds `data "mongodbatlas_cluster" "this"` to avoid breaking changes in `outputs.tf` (see below) | ||
|
|
||
| ### [`outputs.tf`](v2/outputs.tf) | ||
| - All outputs can use `mongodbatlas_advanced_cluster` except for | ||
| - `replication_specs`, we use the `data.mongodbatlas_cluster.this.replication_specs` to keep the same format | ||
| - `mongodbatlas_cluster`, we use the `data.mongodbatlas_cluster.this` to keep the same format | ||
|
|
||
|
|
||
| ## Step 3: Module `v3` Implementation Changes and Highlights | ||
| This module adds variables to support the latest `mongodbatlas_advanced_cluster` features while staying compatible with the old input variables. | ||
|
|
||
| ### [`variables.tf`](v3/variables.tf) | ||
| - Add `replication_specs_new`, this is almost a full mirror of the `replication_specs` of the latest `mongodbatlas_advanced_cluster` schema | ||
| - Use a `[]` for default to allow continued usage of the old `replication_specs` | ||
| - Usage of `optional` to simplify the caller | ||
| - Add `default` to `instance_size` and `provider_name` as these are not required when `replication_specs_new` is used | ||
| - Change `[]` default to `replication_specs` to allow usage of `replication_specs_new` | ||
|
|
||
| ### [`main.tf`](v3/main.tf) | ||
| - Add *defaults* to old variables in `locals`: | ||
| - `old_disk_size` | ||
| - `old_instance_size` | ||
| - `old_provider_name` | ||
| - Add `_old` suffix to `locals.replication_specs` to make conditional code (see below) more readable | ||
| - Add `precondition` for `replication_specs` to validate only `var.replication_specs_new` or `replication_specs` is used | ||
| ```terraform | ||
| precondition { | ||
| condition = !((local.use_new_replication_specs && length(var.replication_specs) > 0) || (!local.use_new_replication_specs && length(var.replication_specs) == 0)) | ||
| error_message = "Must use either replication_specs_new or replication_specs, not both." | ||
| } | ||
| ``` | ||
| - Use a conditional for`replication_specs` in `resource "mongodbatlas_advanced_cluster" "this"`: | ||
| ```terraform | ||
| # other attributes... | ||
| replication_specs = local.use_new_replication_specs ? var.replication_specs_new : local.replication_specs_old | ||
| tags = var.tags | ||
| } | ||
| ``` | ||
| - Use `count` for data source to avoid error when Asymmetric Shards are used: | ||
| ```terraform | ||
| data "mongodbatlas_cluster" "this" { | ||
| count = local.use_new_replication_specs ? 0 : 1 # Not safe when Asymmetric Shards are used | ||
| name = mongodbatlas_advanced_cluster.this.name | ||
| project_id = mongodbatlas_advanced_cluster.this.project_id | ||
|
|
||
| depends_on = [mongodbatlas_advanced_cluster.this] | ||
| } | ||
| ``` | ||
|
|
||
| ### [`outputs.tf`](v3/outputs.tf) | ||
| - Update `replication_specs` and `mongodbatlas_cluster` to handle the case when the new schema is used: | ||
| ```terraform | ||
| output "replication_specs" { | ||
| value = local.use_new_replication_specs ? [] : data.mongodbatlas_cluster.this[0].replication_specs # updated | ||
| description = "Replication Specs for cluster, will be empty if var.replication_specs_new is set" | ||
| } | ||
|
|
||
| output "mongodbatlas_cluster" { | ||
| value = local.use_new_replication_specs ? null : data.mongodbatlas_cluster.this[0] # updated | ||
| description = "Full cluster configuration for mongodbatlas_cluster resource, will be null if var.replication_specs_new is set" | ||
| } | ||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| locals { | ||
| disk_size = var.auto_scaling_disk_gb_enabled ? null : var.disk_size | ||
| replication_specs = flatten([ | ||
| for old_spec in var.replication_specs : [ | ||
| for shard in range(old_spec.num_shards) : [ | ||
| { | ||
| zone_name = old_spec.zone_name | ||
| region_configs = tolist([ | ||
| for region in old_spec.regions_config : { | ||
| region_name = region.region_name | ||
| provider_name = var.provider_name | ||
| electable_specs = { | ||
| instance_size = var.instance_size | ||
| node_count = region.electable_nodes | ||
| disk_size_gb = local.disk_size | ||
EspenAlbert marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| priority = region.priority | ||
| read_only_specs = region.read_only_nodes == 0 ? null : { | ||
| instance_size = var.instance_size | ||
| node_count = region.read_only_nodes | ||
| disk_size_gb = local.disk_size | ||
| } | ||
| auto_scaling = var.auto_scaling_disk_gb_enabled ? { | ||
| disk_gb_enabled = true | ||
| } : null | ||
| } | ||
| ]) | ||
| } | ||
| ] | ||
| ] | ||
| ] | ||
| ) | ||
| } | ||
|
|
||
| moved { | ||
| from = mongodbatlas_cluster.this | ||
| to = mongodbatlas_advanced_cluster.this | ||
| } | ||
|
|
||
|
|
||
| resource "mongodbatlas_advanced_cluster" "this" { | ||
EspenAlbert marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| lifecycle { | ||
| precondition { | ||
| condition = !(var.auto_scaling_disk_gb_enabled && var.disk_size > 0) | ||
| error_message = "Must use either auto_scaling_disk_gb_enabled or disk_size, not both." | ||
| } | ||
| } | ||
|
|
||
| project_id = var.project_id | ||
| name = var.cluster_name | ||
| cluster_type = var.cluster_type | ||
| mongo_db_major_version = var.mongo_db_major_version | ||
| replication_specs = local.replication_specs | ||
| tags = var.tags | ||
| } | ||
|
|
||
| data "mongodbatlas_cluster" "this" { # note the usage of `cluster` not `advanced_cluster`, this is to have outputs stay compatible with the v1 module | ||
| name = mongodbatlas_advanced_cluster.this.name | ||
| project_id = mongodbatlas_advanced_cluster.this.project_id | ||
|
|
||
| depends_on = [mongodbatlas_advanced_cluster.this] | ||
| } | ||
|
|
||
| # OLD cluster configuration: | ||
| # resource "mongodbatlas_cluster" "this" { | ||
| # lifecycle { | ||
| # precondition { | ||
| # condition = !(var.auto_scaling_disk_gb_enabled && var.disk_size > 0) | ||
| # error_message = "Must use either auto_scaling_disk_gb_enabled or disk_size, not both." | ||
| # } | ||
| # } | ||
|
|
||
| # project_id = var.project_id | ||
| # name = var.cluster_name | ||
|
|
||
| # auto_scaling_disk_gb_enabled = var.auto_scaling_disk_gb_enabled | ||
| # cluster_type = var.cluster_type | ||
| # disk_size_gb = var.disk_size | ||
| # mongo_db_major_version = var.mongo_db_major_version | ||
| # provider_instance_size_name = var.instance_size | ||
| # provider_name = var.provider_name | ||
|
|
||
| # dynamic "tags" { | ||
| # for_each = var.tags | ||
| # content { | ||
| # key = tags.key | ||
| # value = tags.value | ||
| # } | ||
| # } | ||
|
|
||
| # dynamic "replication_specs" { | ||
| # for_each = var.replication_specs | ||
| # content { | ||
| # num_shards = replication_specs.value.num_shards | ||
| # zone_name = replication_specs.value.zone_name | ||
|
|
||
| # dynamic "regions_config" { | ||
| # for_each = replication_specs.value.regions_config | ||
| # content { | ||
| # electable_nodes = regions_config.value.electable_nodes | ||
| # priority = regions_config.value.priority | ||
| # read_only_nodes = regions_config.value.read_only_nodes | ||
| # region_name = regions_config.value.region_name | ||
| # } | ||
| # } | ||
| # } | ||
| # } | ||
| # } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| output "mongodb_connection_strings" { | ||
| value = mongodbatlas_advanced_cluster.this.connection_strings | ||
| description = "This is the MongoDB Atlas connection strings. Note, these do not show the connection mechanism of the database details" | ||
| } | ||
|
|
||
| output "cluster_name" { | ||
| value = mongodbatlas_advanced_cluster.this.name | ||
| description = "MongoDB Atlas cluster name" | ||
| } | ||
|
|
||
| output "project_id" { | ||
| value = mongodbatlas_advanced_cluster.this.project_id | ||
| description = "MongoDB Atlas project id" | ||
| } | ||
|
|
||
| output "replication_specs" { | ||
| value = data.mongodbatlas_cluster.this.replication_specs | ||
| description = "Replication Specs for cluster" | ||
| } | ||
|
|
||
| output "mongodbatlas_cluster" { | ||
EspenAlbert marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| value = data.mongodbatlas_cluster.this | ||
| description = "Full cluster configuration for mongodbatlas_cluster resource" | ||
| } | ||
|
|
||
| output "mongodbatlas_advanced_cluster" { | ||
| value = data.mongodbatlas_cluster.this | ||
| description = "Full cluster configuration for mongodbatlas_advanced_cluster resource" | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| variable "project_id" { | ||
| type = string | ||
| } | ||
|
|
||
| variable "cluster_name" { | ||
| type = string | ||
| } | ||
EspenAlbert marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| variable "cluster_type" { | ||
| type = string | ||
| } | ||
|
|
||
| variable "instance_size" { | ||
| type = string | ||
| } | ||
|
|
||
| variable "mongo_db_major_version" { | ||
| type = string | ||
| } | ||
|
|
||
| variable "provider_name" { | ||
| type = string | ||
| } | ||
|
|
||
| # OPTIONAL VARIABLES | ||
|
|
||
| variable "disk_size" { | ||
| type = number | ||
| default = 0 | ||
| } | ||
|
|
||
| variable "auto_scaling_disk_gb_enabled" { | ||
| type = bool | ||
| default = false | ||
| } | ||
|
|
||
| variable "tags" { | ||
| type = map(string) | ||
| default = {} | ||
| } | ||
|
|
||
| variable "replication_specs" { | ||
| type = list(object({ | ||
| num_shards = number | ||
| zone_name = string | ||
| regions_config = set(object({ | ||
| region_name = string | ||
| electable_nodes = number | ||
| priority = number | ||
| read_only_nodes = optional(number, 0) | ||
| })) | ||
| })) | ||
| default = [{ | ||
| num_shards = 1 | ||
| zone_name = "Zone 1" | ||
| regions_config = [{ | ||
| region_name = "US_EAST_1" | ||
| electable_nodes = 3 | ||
| priority = 7 | ||
| }] | ||
| }] | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,9 @@ | ||
| terraform { | ||
| required_version = ">= 1.0" | ||
| required_providers { | ||
| mongodbatlas = { | ||
| source = "mongodb/mongodbatlas" | ||
| version = "~> 1.26" # todo: PLACEHOLDER_TPF_RELEASE | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what about using 1.27 as in the other examples I did, so we can search/replace easily if needed?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, but I think it is better to change it later, otherwise CI will fail:
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sounds good
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. created a ticket so we don't forget: CLOUDP-300537 |
||
| } | ||
| } | ||
| required_version = ">= 1.8" | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.