diff --git a/examples/cluster/azure/README.md b/examples/cluster/azure/README.md index 8f73c4c9..81ce75a9 100644 --- a/examples/cluster/azure/README.md +++ b/examples/cluster/azure/README.md @@ -1,6 +1,14 @@ -# Azure Cluster Example +# Important notes: -This example shows how to create a Cluster on Azure, with autoexpansion enabled. +1. Please rename the `terraform.premium.tfvars` and `terraform.ultra.tfvars` files to `terraform.template.tfvars` to use the configurations for Premium and Ultra type disks respectively. + +2. To run `terraform apply` directly - please check the `terraform.template.tfvars` file, make sure you copy the file to `terraform.tfvars` and update the values of the variables as per the correct organization access. + +# Azure Premium type Cluster Example + +This example shows how to create a Cluster on Azure, with Premium type disks and autoexpansion enabled. + +Note that you cannot provide the iops and storage value for Premium type disks. For autoexpansion it requires this disk variable in `terraform.template.tfvars`: @@ -31,6 +39,8 @@ disk = { } ``` +Create - + Sample Output: ``` @@ -235,4 +245,579 @@ new_cluster = { "timezone" = "PT" } } +``` + +Update example- + +Updating Timezone for an existing Azure cluster on developer pro plan - +``` +Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # couchbase-capella_cluster.new_cluster will be updated in-place + ~ resource "couchbase-capella_cluster" "new_cluster" { + + app_service_id = (known after apply) + ~ audit = { + ~ created_at = "2024-11-13 00:42:55.42572338 +0000 UTC" -> (known after apply) + ~ created_by = "CCaCemREbxLhyNfRg61d4ukDW75nWn1I" -> (known after apply) + ~ modified_at = "2024-11-13 01:14:26.968616922 +0000 UTC" -> (known after apply) + ~ modified_by = "apikey-CCaCemREbxLhyNfRg61d4ukDW75nWn1I" -> (known after apply) + ~ version = 7 -> (known after apply) + } -> (known after apply) + ~ connection_string = "cb.epdsnt2cthimgrpn.aws-guardians.nonprod-project-avengers.com" -> (known after apply) + ~ current_state = "healthy" -> (known after apply) + ~ etag = "Version: 7" -> (known after apply) + id = "0952a9c5-fb5c-425c-8e1e-ca1ba4e7781f" + name = "TF Azure P6 iops" + ~ support = { + ~ timezone = "GMT" -> "PT" + # (1 unchanged attribute hidden) + } + # (9 unchanged attributes hidden) + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +Changes to Outputs: + ~ new_cluster = { + + app_service_id = (known after apply) + ~ audit = { + - created_at = "2024-11-13 00:42:55.42572338 +0000 UTC" + - created_by = "CCaCemREbxLhyNfRg61d4ukDW75nWn1I" + - modified_at = "2024-11-13 00:48:04.283065342 +0000 UTC" + - modified_by = "apikey-CCaCemREbxLhyNfRg61d4ukDW75nWn1I" + - version = 3 + } -> (known after apply) + ~ connection_string = "cb.epdsnt2cthimgrpn.aws-guardians.nonprod-project-avengers.com" -> (known after apply) + ~ current_state = "healthy" -> (known after apply) + ~ etag = "Version: 3" -> (known after apply) + id = "0952a9c5-fb5c-425c-8e1e-ca1ba4e7781f" + name = "TF Azure P6 iops" + # (11 unchanged attributes hidden) + } + +Do you want to perform these actions? + Terraform will perform the actions described above. + Only 'yes' will be accepted to approve. + + Enter a value: yes + +couchbase-capella_cluster.new_cluster: Modifying... [id=0952a9c5-fb5c-425c-8e1e-ca1ba4e7781f] +couchbase-capella_cluster.new_cluster: Still modifying... [id=0952a9c5-fb5c-425c-8e1e-ca1ba4e7781f, 10s elapsed] +couchbase-capella_cluster.new_cluster: Still modifying... [id=0952a9c5-fb5c-425c-8e1e-ca1ba4e7781f, 20s elapsed] +couchbase-capella_cluster.new_cluster: Still modifying... [id=0952a9c5-fb5c-425c-8e1e-ca1ba4e7781f, 30s elapsed] +couchbase-capella_cluster.new_cluster: Still modifying... [id=0952a9c5-fb5c-425c-8e1e-ca1ba4e7781f, 40s elapsed] +couchbase-capella_cluster.new_cluster: Still modifying... [id=0952a9c5-fb5c-425c-8e1e-ca1ba4e7781f, 50s elapsed] +couchbase-capella_cluster.new_cluster: Still modifying... [id=0952a9c5-fb5c-425c-8e1e-ca1ba4e7781f, 1m0s elapsed] +couchbase-capella_cluster.new_cluster: Still modifying... [id=0952a9c5-fb5c-425c-8e1e-ca1ba4e7781f, 1m10s elapsed] +couchbase-capella_cluster.new_cluster: Still modifying... [id=0952a9c5-fb5c-425c-8e1e-ca1ba4e7781f, 1m20s elapsed] +couchbase-capella_cluster.new_cluster: Still modifying... [id=0952a9c5-fb5c-425c-8e1e-ca1ba4e7781f, 1m30s elapsed] +couchbase-capella_cluster.new_cluster: Still modifying... [id=0952a9c5-fb5c-425c-8e1e-ca1ba4e7781f, 1m40s elapsed] +couchbase-capella_cluster.new_cluster: Still modifying... [id=0952a9c5-fb5c-425c-8e1e-ca1ba4e7781f, 1m50s elapsed] +couchbase-capella_cluster.new_cluster: Still modifying... [id=0952a9c5-fb5c-425c-8e1e-ca1ba4e7781f, 2m0s elapsed] +couchbase-capella_cluster.new_cluster: Modifications complete after 2m2s [id=0952a9c5-fb5c-425c-8e1e-ca1ba4e7781f] + +Apply complete! Resources: 0 added, 1 changed, 0 destroyed. + +Outputs: + +cluster_id = "0952a9c5-fb5c-425c-8e1e-ca1ba4e7781f" +new_cluster = { + "app_service_id" = tostring(null) + "audit" = { + "created_at" = "2024-11-13 00:42:55.42572338 +0000 UTC" + "created_by" = "CCaCemREbxLhyNfRg61d4ukDW75nWn1I" + "modified_at" = "2024-11-13 01:17:36.317030551 +0000 UTC" + "modified_by" = "apikey-CCaCemREbxLhyNfRg61d4ukDW75nWn1I" + "version" = 11 + } + "availability" = { + "type" = "single" + } + "cloud_provider" = { + "cidr" = "10.0.0.0/23" + "region" = "eastus" + "type" = "azure" + } + "configuration_type" = "multiNode" + "connection_string" = "cb.epdsnt2cthimgrpn.aws-guardians.nonprod-project-avengers.com" + "couchbase_server" = { + "version" = "7.6" + } + "current_state" = "healthy" + "description" = "My first test cluster for multiple services." + "enable_private_dns_resolution" = false + "etag" = "Version: 11" + "id" = "0952a9c5-fb5c-425c-8e1e-ca1ba4e7781f" + "if_match" = tostring(null) + "name" = "TF Azure P6 iops" + "organization_id" = "adb4fb4c-1d98-4287-ac33-230742d2cc76" + "project_id" = "c09035e8-3971-4b79-a6ec-bccd9056041f" + "service_groups" = toset([ + { + "node" = { + "compute" = { + "cpu" = 4 + "ram" = 16 + } + "disk" = { + "autoexpansion" = true + "iops" = 240 + "storage" = 64 + "type" = "P6" + } + } + "num_of_nodes" = 3 + "services" = toset([ + "data", + ]) + }, + ]) + "support" = { + "plan" = "developer pro" + "timezone" = "PT" + } +} + +``` + + + +# Azure Ultra type cluster example- + +This example shows how to create and update a Premium type Cluster on Azure. + +For adding the storage, iops and autoexpansion, make sure you have these disk variable in `terraform.template.tfvars`: + +``` +disk = { + type = "Ultra" + size = 128 + iops = 4000 + autoexpansion = true +} +``` + +It should be added to `variables.tf`: + +``` +type = object({ + size = optional(number) + type = string + iops = optional(number) + autoexpansion = optional(bool) + }) +``` + +and referenced in `create_cluster.tf`: + +``` +disk = { + storage = var.disk.size + type = var.disk.type + iops = var.disk.iops + autoexpansion = var.disk.autoexpansion + } +``` + +Create- + +Sample Output: + +``` +terraform apply + + +Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + + create + +Terraform will perform the following actions: + + # couchbase-capella_cluster.new_cluster will be created + + resource "couchbase-capella_cluster" "new_cluster" { + + app_service_id = (known after apply) + + audit = (known after apply) + + availability = { + + type = "single" + } + + cloud_provider = { + + cidr = "10.0.0.0/23" + + region = "eastus" + + type = "azure" + } + + configuration_type = (known after apply) + + connection_string = (known after apply) + + couchbase_server = (known after apply) + + current_state = (known after apply) + + description = "My first test cluster for multiple services." + + enable_private_dns_resolution = false + + etag = (known after apply) + + id = (known after apply) + + name = "TF Azure Ultra" + + organization_id = "adb4fb4c-1d98-4287-ac33-230742d2cc76" + + project_id = "c09035e8-3971-4b79-a6ec-bccd9056041f" + + service_groups = [ + + { + + node = { + + compute = { + + cpu = 4 + + ram = 16 + } + + disk = { + + autoexpansion = true + + iops = 3000 + + storage = 64 + + type = "Ultra" + } + } + + num_of_nodes = 3 + + services = [ + + "data", + ] + }, + ] + + support = { + + plan = "developer pro" + + timezone = "PT" + } + } + +Plan: 1 to add, 0 to change, 0 to destroy. + +Changes to Outputs: + + cluster_id = (known after apply) + + new_cluster = { + + app_service_id = (known after apply) + + audit = (known after apply) + + availability = { + + type = "single" + } + + cloud_provider = { + + cidr = "10.0.0.0/23" + + region = "eastus" + + type = "azure" + } + + configuration_type = (known after apply) + + connection_string = (known after apply) + + couchbase_server = (known after apply) + + current_state = (known after apply) + + description = "My first test cluster for multiple services." + + enable_private_dns_resolution = false + + etag = (known after apply) + + id = (known after apply) + + if_match = null + + name = "TF Azure Ultra" + + organization_id = "adb4fb4c-1d98-4287-ac33-230742d2cc76" + + project_id = "c09035e8-3971-4b79-a6ec-bccd9056041f" + + service_groups = [ + + { + + node = { + + compute = { + + cpu = 4 + + ram = 16 + } + + disk = { + + autoexpansion = true + + iops = 3000 + + storage = 64 + + type = "Ultra" + } + } + + num_of_nodes = 3 + + services = [ + + "data", + ] + }, + ] + + support = { + + plan = "developer pro" + + timezone = "PT" + } + } +2024-11-13T10:49:30.808-0800 [DEBUG] command: asking for input: "\nDo you want to perform these actions?" + +Do you want to perform these actions? + Terraform will perform the actions described above. + Only 'yes' will be accepted to approve. + + Enter a value: yes + +couchbase-capella_cluster.new_cluster: Creating... +2024-11-13T10:49:32.348-0800 [INFO] Starting apply for couchbase-capella_cluster.new_cluster +2024-11-13T10:49:32.348-0800 [DEBUG] skipping FixUpBlockAttrs +2024-11-13T10:49:32.348-0800 [DEBUG] couchbase-capella_cluster.new_cluster: applying the planned Create change +couchbase-capella_cluster.new_cluster: Still creating... [10s elapsed] +couchbase-capella_cluster.new_cluster: Still creating... [20s elapsed] +couchbase-capella_cluster.new_cluster: Still creating... [30s elapsed] +couchbase-capella_cluster.new_cluster: Still creating... [40s elapsed] +couchbase-capella_cluster.new_cluster: Still creating... [50s elapsed] +couchbase-capella_cluster.new_cluster: Still creating... [1m0s elapsed] +couchbase-capella_cluster.new_cluster: Still creating... [1m10s elapsed] +couchbase-capella_cluster.new_cluster: Still creating... [1m20s elapsed] +couchbase-capella_cluster.new_cluster: Still creating... [1m30s elapsed] +couchbase-capella_cluster.new_cluster: Still creating... [1m40s elapsed] +couchbase-capella_cluster.new_cluster: Still creating... [1m50s elapsed] +couchbase-capella_cluster.new_cluster: Still creating... [2m0s elapsed] + + +Apply complete! Resources: 1 added, 0 changed, 0 destroyed. + +Outputs: + +cluster_id = "c142f957-edac-4bc1-aff4-90d7cde7b007" +new_cluster = { + "app_service_id" = tostring(null) + "audit" = { + "created_at" = "2024-11-13 18:49:32.43634667 +0000 UTC" + "created_by" = "CCaCemREbxLhyNfRg61d4ukDW75nWn1I" + "modified_at" = "2024-11-13 18:55:26.536605251 +0000 UTC" + "modified_by" = "apikey-CCaCemREbxLhyNfRg61d4ukDW75nWn1I" + "version" = 3 + } + "availability" = { + "type" = "single" + } + "cloud_provider" = { + "cidr" = "10.0.0.0/23" + "region" = "eastus" + "type" = "azure" + } + "configuration_type" = "multiNode" + "connection_string" = "cb.sstd0a1ssoq-qz.aws-guardians.nonprod-project-avengers.com" + "couchbase_server" = { + "version" = "7.6" + } + "current_state" = "healthy" + "description" = "My first test cluster for multiple services." + "enable_private_dns_resolution" = false + "etag" = "Version: 3" + "id" = "c142f957-edac-4bc1-aff4-90d7cde7b007" + "if_match" = tostring(null) + "name" = "TF Azure Ultra" + "organization_id" = "adb4fb4c-1d98-4287-ac33-230742d2cc76" + "project_id" = "c09035e8-3971-4b79-a6ec-bccd9056041f" + "service_groups" = toset([ + { + "node" = { + "compute" = { + "cpu" = 4 + "ram" = 16 + } + "disk" = { + "autoexpansion" = true + "iops" = 3000 + "storage" = 64 + "type" = "Ultra" + } + } + "num_of_nodes" = 3 + "services" = toset([ + "data", + ]) + }, + ]) + "support" = { + "plan" = "developer pro" + "timezone" = "PT" + } +} + +``` + +Update example- + +``` +terraform apply- + +Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + ~ update in-place + +Terraform will perform the following actions: + + # couchbase-capella_cluster.new_cluster will be updated in-place + ~ resource "couchbase-capella_cluster" "new_cluster" { + + app_service_id = (known after apply) + ~ audit = { + ~ created_at = "2024-11-13 18:49:32.43634667 +0000 UTC" -> (known after apply) + ~ created_by = "CCaCemREbxLhyNfRg61d4ukDW75nWn1I" -> (known after apply) + ~ modified_at = "2024-11-13 18:55:26.536605251 +0000 UTC" -> (known after apply) + ~ modified_by = "apikey-CCaCemREbxLhyNfRg61d4ukDW75nWn1I" -> (known after apply) + ~ version = 3 -> (known after apply) + } -> (known after apply) + ~ connection_string = "cb.sstd0a1ssoq-qz.aws-guardians.nonprod-project-avengers.com" -> (known after apply) + ~ current_state = "healthy" -> (known after apply) + ~ etag = "Version: 3" -> (known after apply) + id = "c142f957-edac-4bc1-aff4-90d7cde7b007" + name = "TF Azure Ultra" + ~ service_groups = [ + - { + - node = { + - compute = { + - cpu = 4 -> null + - ram = 16 -> null + } -> null + - disk = { + - autoexpansion = true -> null + - iops = 3000 -> null + - storage = 64 -> null + - type = "Ultra" -> null + } -> null + } -> null + - num_of_nodes = 3 -> null + - services = [ + - "data", + ] -> null + }, + + { + + node = { + + compute = { + + cpu = 4 + + ram = 16 + } + + disk = { + + autoexpansion = true + + iops = 4000 + + storage = 128 + + type = "Ultra" + } + } + + num_of_nodes = 3 + + services = [ + + "data", + ] + }, + ] + ~ support = { + ~ timezone = "PT" -> "GMT" + # (1 unchanged attribute hidden) + } + # (8 unchanged attributes hidden) + } + +Plan: 0 to add, 1 to change, 0 to destroy. + +Changes to Outputs: + ~ new_cluster = { + + app_service_id = (known after apply) + ~ audit = { + - created_at = "2024-11-13 18:49:32.43634667 +0000 UTC" + - created_by = "CCaCemREbxLhyNfRg61d4ukDW75nWn1I" + - modified_at = "2024-11-13 18:55:26.536605251 +0000 UTC" + - modified_by = "apikey-CCaCemREbxLhyNfRg61d4ukDW75nWn1I" + - version = 3 + } -> (known after apply) + ~ connection_string = "cb.sstd0a1ssoq-qz.aws-guardians.nonprod-project-avengers.com" -> (known after apply) + ~ current_state = "healthy" -> (known after apply) + ~ etag = "Version: 3" -> (known after apply) + id = "c142f957-edac-4bc1-aff4-90d7cde7b007" + name = "TF Azure Ultra" + ~ service_groups = [ + ~ { + ~ node = { + ~ disk = { + ~ iops = 3000 -> 4000 + ~ storage = 64 -> 128 + # (2 unchanged attributes hidden) + } + # (1 unchanged attribute hidden) + } + # (2 unchanged attributes hidden) + }, + ] + ~ support = { + ~ timezone = "PT" -> "GMT" + # (1 unchanged attribute hidden) + } + # (9 unchanged attributes hidden) + } +2024-11-13T11:06:35.775-0800 [DEBUG] command: asking for input: "\nDo you want to perform these actions?" + +Do you want to perform these actions? + Terraform will perform the actions described above. + Only 'yes' will be accepted to approve. + + Enter a value: yes + +couchbase-capella_cluster.new_cluster: Modifying... [id=c142f957-edac-4bc1-aff4-90d7cde7b007] +2024-11-13T11:06:37.595-0800 [INFO] Starting apply for couchbase-capella_cluster.new_cluster +2024-11-13T11:06:37.595-0800 [DEBUG] skipping FixUpBlockAttrs +2024-11-13T11:06:37.595-0800 [DEBUG] couchbase-capella_cluster.new_cluster: applying the planned Update change +couchbase-capella_cluster.new_cluster: Still modifying... [id=c142f957-edac-4bc1-aff4-90d7cde7b007, 10s elapsed] +couchbase-capella_cluster.new_cluster: Still modifying... [id=c142f957-edac-4bc1-aff4-90d7cde7b007, 20s elapsed] +couchbase-capella_cluster.new_cluster: Still modifying... [id=c142f957-edac-4bc1-aff4-90d7cde7b007, 30s elapsed] +couchbase-capella_cluster.new_cluster: Still modifying... [id=c142f957-edac-4bc1-aff4-90d7cde7b007, 40s elapsed] +couchbase-capella_cluster.new_cluster: Still modifying... [id=c142f957-edac-4bc1-aff4-90d7cde7b007, 50s elapsed] +couchbase-capella_cluster.new_cluster: Still modifying... [id=c142f957-edac-4bc1-aff4-90d7cde7b007, 1m0s elapsed] +couchbase-capella_cluster.new_cluster: Still modifying... [id=c142f957-edac-4bc1-aff4-90d7cde7b007, 1m10s elapsed] +couchbase-capella_cluster.new_cluster: Still modifying... [id=c142f957-edac-4bc1-aff4-90d7cde7b007, 1m20s elapsed] +couchbase-capella_cluster.new_cluster: Still modifying... [id=c142f957-edac-4bc1-aff4-90d7cde7b007, 1m30s elapsed] +couchbase-capella_cluster.new_cluster: Still modifying... [id=c142f957-edac-4bc1-aff4-90d7cde7b007, 1m40s elapsed] +couchbase-capella_cluster.new_cluster: Still modifying... [id=c142f957-edac-4bc1-aff4-90d7cde7b007, 1m50s elapsed] +couchbase-capella_cluster.new_cluster: Still modifying... [id=c142f957-edac-4bc1-aff4-90d7cde7b007, 2m0s elapsed] + +Apply complete! Resources: 0 added, 1 changed, 0 destroyed. + +Outputs: + +cluster_id = "c142f957-edac-4bc1-aff4-90d7cde7b007" +new_cluster = { + "app_service_id" = tostring(null) + "audit" = { + "created_at" = "2024-11-13 18:49:32.43634667 +0000 UTC" + "created_by" = "CCaCemREbxLhyNfRg61d4ukDW75nWn1I" + "modified_at" = "2024-11-13 19:20:06.276514922 +0000 UTC" + "modified_by" = "apikey-Bbmh0bvuZJWriP7lUIafeVh4RS7i8HHz" + "version" = 7 + } + "availability" = { + "type" = "single" + } + "cloud_provider" = { + "cidr" = "10.0.0.0/23" + "region" = "eastus" + "type" = "azure" + } + "configuration_type" = "multiNode" + "connection_string" = "cb.sstd0a1ssoq-qz.aws-guardians.nonprod-project-avengers.com" + "couchbase_server" = { + "version" = "7.6" + } + "current_state" = "healthy" + "description" = "My first test cluster for multiple services." + "enable_private_dns_resolution" = false + "etag" = "Version: 7" + "id" = "c142f957-edac-4bc1-aff4-90d7cde7b007" + "if_match" = tostring(null) + "name" = "TF Azure Ultra" + "organization_id" = "adb4fb4c-1d98-4287-ac33-230742d2cc76" + "project_id" = "c09035e8-3971-4b79-a6ec-bccd9056041f" + "service_groups" = toset([ + { + "node" = { + "compute" = { + "cpu" = 4 + "ram" = 16 + } + "disk" = { + "autoexpansion" = true + "iops" = 4000 + "storage" = 128 + "type" = "Ultra" + } + } + "num_of_nodes" = 3 + "services" = toset([ + "data", + ]) + }, + ]) + "support" = { + "plan" = "developer pro" + "timezone" = "GMT" + } +} + + ``` \ No newline at end of file diff --git a/examples/cluster/azure/terraform.template.tfvars b/examples/cluster/azure/terraform.premium.tfvars similarity index 99% rename from examples/cluster/azure/terraform.template.tfvars rename to examples/cluster/azure/terraform.premium.tfvars index e762aeb5..b4ffd094 100644 --- a/examples/cluster/azure/terraform.template.tfvars +++ b/examples/cluster/azure/terraform.premium.tfvars @@ -28,4 +28,4 @@ disk = { support = { plan = "basic" timezone = "PT" -} +} \ No newline at end of file diff --git a/examples/cluster/azure/terraform.ultra.tfvars b/examples/cluster/azure/terraform.ultra.tfvars new file mode 100644 index 00000000..4ca2a501 --- /dev/null +++ b/examples/cluster/azure/terraform.ultra.tfvars @@ -0,0 +1,32 @@ +auth_token = "" +organization_id = "" +project_id = "" + +cloud_provider = { + name = "azure", + region = "eastus" +} +cluster = { + name = "TF Azure Ultra" + cidr = "10.10.0.0/23" + node_count = 3 + couchbase_services = ["data", "index", "query", "search"] + availability_zone = "single" +} + +compute = { + cpu = 4 + ram = 16 +} + +disk = { + type = "Ultra" + size = 128 + iops = 5000 + autoexpansion = true +} + +support = { + plan = "developer pro" + timezone = "PT" +} diff --git a/internal/api/cluster/cluster.go b/internal/api/cluster/cluster.go index 2f37bc6c..62733807 100644 --- a/internal/api/cluster/cluster.go +++ b/internal/api/cluster/cluster.go @@ -6,6 +6,34 @@ import ( "github.com/google/uuid" ) +const ( + DefaultP6Storage = 64 + DefaultP6IOPS = 240 + DefaultP10Storage = 128 + DefaultP10IOPS = 500 + DefaultP15Storage = 256 + DefaultP15IOPS = 1100 + DefaultP20Storage = 512 + DefaultP20IOPS = 2300 + DefaultP30Storage = 1024 + DefaultP30IOPS = 5000 + DefaultP40Storage = 2048 + DefaultP40IOPS = 7500 + DefaultP50Storage = 4096 + DefaultP50IOPS = 7500 + DefaultP60Storage = 8192 + DefaultP60IOPS = 16000 + Ultra DiskAzureType = "Ultra" + P6 DiskAzureType = "P6" + P10 DiskAzureType = "P10" + P15 DiskAzureType = "P15" + P20 DiskAzureType = "P20" + P30 DiskAzureType = "P30" + P40 DiskAzureType = "P40" + P50 DiskAzureType = "P50" + P60 DiskAzureType = "P60" +) + // Availability defines if the cluster nodes will be deployed in multiple or single availability zones in the cloud. type Availability struct { // Type is availability zone type, either 'single' or 'multi'. diff --git a/internal/resources/acceptance_tests/cluster_acceptance_test.go b/internal/resources/acceptance_tests/cluster_acceptance_test.go index d7011ad3..72898bab 100644 --- a/internal/resources/acceptance_tests/cluster_acceptance_test.go +++ b/internal/resources/acceptance_tests/cluster_acceptance_test.go @@ -253,9 +253,8 @@ func TestAccClusterResourceAzure(t *testing.T) { resource.TestCheckResourceAttr(resourceReference, "cloud_provider.cidr", cidr), resource.TestCheckResourceAttr(resourceReference, "service_groups.0.node.compute.cpu", "4"), resource.TestCheckResourceAttr(resourceReference, "service_groups.0.node.compute.ram", "16"), - resource.TestCheckResourceAttr(resourceReference, "service_groups.0.node.disk.storage", "1024"), - resource.TestCheckResourceAttr(resourceReference, "service_groups.0.node.disk.type", "Ultra"), - resource.TestCheckResourceAttr(resourceReference, "service_groups.0.node.disk.iops", "17000"), + resource.TestCheckResourceAttr(resourceReference, "service_groups.0.node.disk.type", "P6"), + resource.TestCheckResourceAttr(resourceReference, "service_groups.0.node.disk.iops", "240"), resource.TestCheckResourceAttr(resourceReference, "service_groups.0.num_of_nodes", "3"), resource.TestCheckResourceAttr(resourceReference, "service_groups.0.services.#", "3"), resource.TestCheckResourceAttr(resourceReference, "service_groups.0.services.0", "data"), @@ -263,7 +262,7 @@ func TestAccClusterResourceAzure(t *testing.T) { resource.TestCheckResourceAttr(resourceReference, "service_groups.0.services.2", "query"), resource.TestCheckResourceAttr(resourceReference, "availability.type", "multi"), resource.TestCheckResourceAttr(resourceReference, "support.plan", "developer pro"), - resource.TestCheckResourceAttr(resourceReference, "support.timezone", "PT"), + resource.TestCheckResourceAttr(resourceReference, "support.timezone", "GMT"), resource.TestCheckResourceAttr(resourceReference, "enable_private_dns_resolution", "false"), resource.TestCheckResourceAttr(resourceReference, "enable_public_ip", "false"), ), @@ -277,7 +276,7 @@ func TestAccClusterResourceAzure(t *testing.T) { }, { - Config: testAccClusterResourceConfigAzureUpdateToDiskTypeP6(acctest.Cfg, resourceName, projectResourceName, projectResourceReference, cidr), + Config: testAccClusterResourceConfigAzureUpdateTimezone(acctest.Cfg, resourceName, projectResourceName, projectResourceReference, cidr), Check: resource.ComposeAggregateTestCheckFunc( testAccExistsClusterResource(resourceReference), resource.TestCheckResourceAttr(resourceReference, "name", "Terraform Acceptance Test Cluster"), @@ -287,9 +286,32 @@ func TestAccClusterResourceAzure(t *testing.T) { resource.TestCheckResourceAttr(resourceReference, "cloud_provider.cidr", cidr), resource.TestCheckResourceAttr(resourceReference, "service_groups.0.node.compute.cpu", "4"), resource.TestCheckResourceAttr(resourceReference, "service_groups.0.node.compute.ram", "32"), - resource.TestCheckResourceAttr(resourceReference, "service_groups.0.node.disk.storage", "64"), + resource.TestCheckResourceAttr(resourceReference, "service_groups.0.num_of_nodes", "3"), + resource.TestCheckResourceAttr(resourceReference, "service_groups.0.services.#", "3"), + resource.TestCheckResourceAttr(resourceReference, "service_groups.0.services.0", "data"), + resource.TestCheckResourceAttr(resourceReference, "service_groups.0.services.1", "index"), + resource.TestCheckResourceAttr(resourceReference, "service_groups.0.services.2", "query"), + resource.TestCheckResourceAttr(resourceReference, "availability.type", "multi"), + resource.TestCheckResourceAttr(resourceReference, "support.plan", "developer pro"), + resource.TestCheckResourceAttr(resourceReference, "support.timezone", "PT"), resource.TestCheckResourceAttr(resourceReference, "service_groups.0.node.disk.type", "P6"), resource.TestCheckResourceAttr(resourceReference, "service_groups.0.node.disk.iops", "240"), + ), + }, + { + Config: testAccClusterResourceConfigAzureUpdateToUltraDisk(acctest.Cfg, resourceName, projectResourceName, projectResourceReference, cidr), + Check: resource.ComposeAggregateTestCheckFunc( + testAccExistsClusterResource(resourceReference), + resource.TestCheckResourceAttr(resourceReference, "name", "Terraform Acceptance Test Cluster"), + resource.TestCheckResourceAttr(resourceReference, "description", "My first test cluster for multiple services."), + resource.TestCheckResourceAttr(resourceReference, "cloud_provider.type", "azure"), + resource.TestCheckResourceAttr(resourceReference, "cloud_provider.region", "eastus"), + resource.TestCheckResourceAttr(resourceReference, "cloud_provider.cidr", cidr), + resource.TestCheckResourceAttr(resourceReference, "service_groups.0.node.compute.cpu", "4"), + resource.TestCheckResourceAttr(resourceReference, "service_groups.0.node.compute.ram", "32"), + resource.TestCheckResourceAttr(resourceReference, "service_groups.0.node.disk.storage", "1024"), + resource.TestCheckResourceAttr(resourceReference, "service_groups.0.node.disk.type", "Ultra"), + resource.TestCheckResourceAttr(resourceReference, "service_groups.0.node.disk.iops", "17000"), resource.TestCheckResourceAttr(resourceReference, "service_groups.0.num_of_nodes", "3"), resource.TestCheckResourceAttr(resourceReference, "service_groups.0.services.#", "3"), resource.TestCheckResourceAttr(resourceReference, "service_groups.0.services.0", "data"), @@ -339,7 +361,7 @@ func TestAccClusterResourceAzure(t *testing.T) { resource.TestCheckResourceAttr(resourceReference, "service_groups.0.node.compute.ram", "16"), resource.TestCheckResourceAttr(resourceReference, "service_groups.0.node.disk.storage", "1024"), resource.TestCheckResourceAttr(resourceReference, "service_groups.0.node.disk.type", "Ultra"), - resource.TestCheckResourceAttr(resourceReference, "service_groups.0.node.disk.iops", "2000"), + resource.TestCheckResourceAttr(resourceReference, "service_groups.0.node.disk.iops", "17000"), resource.TestCheckResourceAttr(resourceReference, "service_groups.0.num_of_nodes", "3"), resource.TestCheckResourceAttr(resourceReference, "service_groups.0.services.#", "1"), resource.TestCheckResourceAttr(resourceReference, "service_groups.0.services.0", "data"), @@ -1008,9 +1030,9 @@ resource "couchbase-capella_cluster" "%[2]s" { ram = 16 } disk = { - storage = 1024, - type = "Ultra" - iops = 17000 + type = "P6" + autoexpansion = true + } } num_of_nodes = 3 @@ -1022,7 +1044,7 @@ resource "couchbase-capella_cluster" "%[2]s" { } support = { plan = "developer pro" - timezone = "PT" + timezone = "GMT" } } `, cfg, resourceName, projectResourceName, projectResourceReference, cidr) @@ -1128,9 +1150,7 @@ resource "couchbase-capella_cluster" "%[2]s" { `, cfg, resourceName, projectResourceName, projectResourceReference, cidr) } -// testAccClusterResourceConfigAzureUpdateToDiskTypeP6 generates a Terraform configuration string for testing an acceptance test scenario -// where a cluster resource is updated to change the disk type to "P6". -func testAccClusterResourceConfigAzureUpdateToDiskTypeP6(cfg, resourceName, projectResourceName, projectResourceReference, cidr string) string { +func testAccClusterResourceConfigAzureUpdateToUltraDisk(cfg, resourceName, projectResourceName, projectResourceReference, cidr string) string { return fmt.Sprintf(` %[1]s @@ -1158,8 +1178,57 @@ resource "couchbase-capella_cluster" "%[2]s" { cpu = 4 ram = 32 } - disk = { + disk = { + storage = 1024, + type = "Ultra" + iops = 17000 + } + } + num_of_nodes = 3 + services = ["data", "index", "query"] + } + ] + availability = { + "type" : "multi" + } + support = { + plan = "developer pro" + timezone = "PT" + } +} +`, cfg, resourceName, projectResourceName, projectResourceReference, cidr) +} +func testAccClusterResourceConfigAzureUpdateTimezone(cfg, resourceName, projectResourceName, projectResourceReference, cidr string) string { + return fmt.Sprintf(` +%[1]s + +resource "couchbase-capella_project" "%[3]s" { + organization_id = var.organization_id + name = "acc_test_project_name" + description = "description" +} + +resource "couchbase-capella_cluster" "%[2]s" { + organization_id = var.organization_id + project_id = %[4]s.id + name = "Terraform Acceptance Test Cluster" + description = "My first test cluster for multiple services." + cloud_provider = { + type = "azure" + region = "eastus" + cidr = "%[5]s" + } + + service_groups = [ + { + node = { + compute = { + cpu = 4 + ram = 32 + } + disk = { type = "P6" + autoexpansion = true } } num_of_nodes = 3 @@ -1263,7 +1332,7 @@ resource "couchbase-capella_cluster" "%[2]s" { disk = { storage = 1024, type = "Ultra" - iops = 2000 + iops = 17000 } } num_of_nodes = 3 diff --git a/internal/resources/cluster.go b/internal/resources/cluster.go index ca0bc4db..517d9fcc 100644 --- a/internal/resources/cluster.go +++ b/internal/resources/cluster.go @@ -138,6 +138,18 @@ func (c *Cluster) Create(ctx context.Context, req resource.CreateRequest, resp * clusterRequest.ConfigurationType = clusterapi.ConfigurationType(plan.ConfigurationType.ValueString()) } + //check disk values provided for Azure, if Premium type disks, then do not allow setting storage, iops. + if plan.CloudProvider.Type.ValueString() == string(clusterapi.Azure) { + err := c.checkDisk(plan) + if err != nil { + resp.Diagnostics.AddError( + "Error creating cluster", + "Could not create cluster, unexpected error: "+err.Error(), + ) + return + } + } + serviceGroups, err := c.morphToApiServiceGroups(plan) if err != nil { resp.Diagnostics.AddError( @@ -377,6 +389,19 @@ func (c *Cluster) Update(ctx context.Context, req resource.UpdateRequest, resp * } } + //Check disk values provided for Azure, if Premium type disks, then do not allow setting storage, iops. + //And if the values in plan are set as default values, then ignore as that is correct configuration. + if plan.CloudProvider.Type.ValueString() == string(clusterapi.Azure) { + err := c.checkDiskUpdate(plan) + if err != nil { + resp.Diagnostics.AddError( + "Error creating cluster", + "Could not create cluster, unexpected error: "+err.Error(), + ) + return + } + } + serviceGroups, err := c.morphToApiServiceGroups(plan) if err != nil { resp.Diagnostics.AddError( @@ -679,14 +704,17 @@ func (c *Cluster) morphToApiServiceGroups(plan providerschema.Cluster) ([]cluste Type: clusterapi.DiskAzureType(serviceGroup.Node.Disk.Type.ValueString()), } - if serviceGroup.Node != nil && !serviceGroup.Node.Disk.Storage.IsNull() && !serviceGroup.Node.Disk.Storage.IsUnknown() { - storage := int(serviceGroup.Node.Disk.Storage.ValueInt64()) - diskAzure.Storage = &storage - } + //only set values for Ultra type disk, not for Premium type + if diskAzure.Type == clusterapi.Ultra { + if serviceGroup.Node != nil && !serviceGroup.Node.Disk.Storage.IsNull() && !serviceGroup.Node.Disk.Storage.IsUnknown() { + storage := int(serviceGroup.Node.Disk.Storage.ValueInt64()) + diskAzure.Storage = &storage + } - if serviceGroup.Node != nil && !serviceGroup.Node.Disk.IOPS.IsNull() && !serviceGroup.Node.Disk.Storage.IsUnknown() { - iops := int(serviceGroup.Node.Disk.IOPS.ValueInt64()) - diskAzure.Iops = &iops + if serviceGroup.Node != nil && !serviceGroup.Node.Disk.IOPS.IsNull() && !serviceGroup.Node.Disk.Storage.IsUnknown() { + iops := int(serviceGroup.Node.Disk.IOPS.ValueInt64()) + diskAzure.Iops = &iops + } } if serviceGroup.Node != nil && !serviceGroup.Node.Disk.Autoexpansion.IsNull() { @@ -869,3 +897,54 @@ func (c *Cluster) convertZones(zones []basetypes.StringValue) []string { } return convertedZones } + +func (c *Cluster) checkDisk(plan providerschema.Cluster) error { + for _, serviceGroup := range plan.ServiceGroups { + // Check if Disk.Type is not "Ultra" but either Storage or IOPS is non-null. + if serviceGroup.Node.Disk.Type.ValueString() != string(clusterapi.Ultra) { + if (!serviceGroup.Node.Disk.Storage.IsNull() && !serviceGroup.Node.Disk.Storage.IsUnknown()) || (!serviceGroup.Node.Disk.IOPS.IsNull() && !serviceGroup.Node.Disk.IOPS.IsUnknown()) { + return fmt.Errorf("invalid configuration: Storage and IOPS cannot be specified when Disk.Type is Premium") + } + } + } + return nil +} + +func (c *Cluster) checkDiskUpdate(plan providerschema.Cluster) error { + for _, serviceGroup := range plan.ServiceGroups { + // Check if Disk.Type is not "Ultra" but either Storage or IOPS is non-null. + if serviceGroup.Node.Disk.Type.ValueString() != string(clusterapi.Ultra) { + if (!serviceGroup.Node.Disk.Storage.IsNull() && !serviceGroup.Node.Disk.Storage.IsUnknown()) || (!serviceGroup.Node.Disk.IOPS.IsNull() && !serviceGroup.Node.Disk.IOPS.IsUnknown()) { + // Check what is coming from the existing plan, throw error, if non-default values. + if !isDefaultStorageAndIOPS(serviceGroup.Node.Disk.Type, serviceGroup.Node.Disk.Storage, serviceGroup.Node.Disk.IOPS) { + return fmt.Errorf("invalid configuration: Storage and IOPS cannot be specified when Disk.Type is Premium") + } + } + } + } + return nil +} + +// Helper function to check if Storage and IOPS are set to default values. +func isDefaultStorageAndIOPS(diskType types.String, storage types.Int64, iops types.Int64) bool { + switch diskType.ValueString() { + case string(clusterapi.P6): + return storage.ValueInt64() == clusterapi.DefaultP6Storage && iops.ValueInt64() == clusterapi.DefaultP6IOPS + case string(clusterapi.P10): + return storage.ValueInt64() == clusterapi.DefaultP10Storage && iops.ValueInt64() == clusterapi.DefaultP10IOPS + case string(clusterapi.P15): + return storage.ValueInt64() == clusterapi.DefaultP15Storage && iops.ValueInt64() == clusterapi.DefaultP15IOPS + case string(clusterapi.P20): + return storage.ValueInt64() == clusterapi.DefaultP20Storage && iops.ValueInt64() == clusterapi.DefaultP20IOPS + case string(clusterapi.P30): + return storage.ValueInt64() == clusterapi.DefaultP30Storage && iops.ValueInt64() == clusterapi.DefaultP30IOPS + case string(clusterapi.P40): + return storage.ValueInt64() == clusterapi.DefaultP40Storage && iops.ValueInt64() == clusterapi.DefaultP40IOPS + case string(clusterapi.P50): + return storage.ValueInt64() == clusterapi.DefaultP50Storage && iops.ValueInt64() == clusterapi.DefaultP50IOPS + case string(clusterapi.P60): + return storage.ValueInt64() == clusterapi.DefaultP60Storage && iops.ValueInt64() == clusterapi.DefaultP60IOPS + } + + return false +} diff --git a/internal/resources/cluster_schema.go b/internal/resources/cluster_schema.go index 6c0eb775..d4818509 100644 --- a/internal/resources/cluster_schema.go +++ b/internal/resources/cluster_schema.go @@ -61,7 +61,7 @@ func ClusterSchema() schema.Schema { }, "disk": schema.SingleNestedAttribute{ Description: "The 'storage' and 'IOPS' fields are required for AWS. " + - "For Azure, only the 'disktype' field is required, and for Ultra, you can provide all three fields. " + + "For Azure, only the 'disktype' field is required, and for Ultra disk type, you can provide all 3 - storage, iops and autoexpansion fields. For Premium type, you can only provide the autoexpansion field, others can't be set." + "In the case of GCP, only 'pd ssd' disk type is available, and you cannot set the 'IOPS' field.", Required: true, Attributes: map[string]schema.Attribute{