From 2ce836381204e7158e269ef9e1a2ccd4bbcd5f23 Mon Sep 17 00:00:00 2001 From: Viliam Pucik Date: Fri, 9 May 2025 13:03:58 +0200 Subject: [PATCH] AlloyDB read poll support and various usability fixes (#3061) * AlloyDB read poll support and various usability fixes * Added support for read poll instances. * Added support for public (outbound) IPs. * Added new arguments: `machine_type`, `skip_await_major_version_upgrade`, and `subscription_type`. * Added outputs for key cluster attributes, including ID and name. * Improved input variable validations and simplified configurations. * Fixed bug where AlloyDB user accounts always had null passwords. * Resolved Terraform state drift issues for `network_config` and `psc_config`. * Fixed `continuous_backup_config` always being implicitly or explicitly enabled. * Fixed `automated_backup_policy` and `maintenance_update_policy`, which do not support non-zero minutes, seconds and nanos. * Fixed various typos. * Aligned the format of test examples. * Fixed invalid condition: authorized external networks require enabled public IP, however enabled public IP can have empty authorized external networks * Formatting * Fix PSA block --------- Co-authored-by: Julio Castillo --- modules/alloydb/README.md | 123 ++++-- modules/alloydb/main.tf | 356 +++++++++++------- modules/alloydb/outputs.tf | 60 ++- modules/alloydb/variables.tf | 208 +++++----- tests/modules/alloydb/examples/cmek.yaml | 22 +- .../examples/cross_region_replication.yaml | 55 +-- tests/modules/alloydb/examples/custom.yaml | 33 +- tests/modules/alloydb/examples/psc.yaml | 27 +- tests/modules/alloydb/examples/read_pool.yaml | 94 +++++ tests/modules/alloydb/examples/simple.yaml | 10 +- 10 files changed, 641 insertions(+), 347 deletions(-) create mode 100644 tests/modules/alloydb/examples/read_pool.yaml diff --git a/modules/alloydb/README.md b/modules/alloydb/README.md index 2bbd26676..b2aa131bf 100644 --- a/modules/alloydb/README.md +++ b/modules/alloydb/README.md @@ -12,6 +12,7 @@ Note that this module assumes that some options are the same for both the primar * [AlloyDB module](#alloydb-module) * [Examples](#examples) * [Simple example](#simple-example) + * [Read pool](#read-pool) * [Cross region replication](#cross-region-replication) * [PSC instance](#psc-instance) * [Custom flags and users definition](#custom-flags-and-users-definition) @@ -71,6 +72,33 @@ module "alloydb" { # tftest modules=3 resources=17 inventory=simple.yaml e2e ``` +### Read pool + +One node read pool instance is always zonal, two or more nodes make the instance always regional. By default a read pool instance has one node. + +```hcl +module "alloydb" { + source = "./fabric/modules/alloydb" + project_id = var.project_id + project_number = var.project_number + cluster_name = "db" + location = var.region + instance_name = "db" + network_config = { + psa_config = { + network = var.vpc.id + } + } + read_pool = { + "zonal-read-pool" = {} + "regional-read-pool" = { + node_count = 2 + } + } +} +# tftest modules=1 resources=4 inventory=read_pool.yaml e2e +``` + ### Cross region replication ```hcl @@ -98,7 +126,7 @@ In a cross-region replication scenario (like in the previous example) this modul * [promoting the secondary instance](https://cloud.google.com/alloydb/docs/cross-region-replication/work-with-cross-region-replication#promote-secondary-cluster) to become a primary instance via the `var.cross_region_replication.promote_secondary` flag. -* [promoting the secondary instance](https://cloud.google.com/alloydb/docs/cross-region-replication/work-with-cross-region-replication#promote-secondary-cluster) to become a primary instance via the `var.cross_region_replication.promote_secondary` flag. +* aligning an existing cluster after switchover via the `var.cross_region_replication.switchover_mode` flag. ### PSC instance @@ -266,50 +294,63 @@ module "alloydb" { | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [cluster_name](variables.tf#L99) | Name of the primary cluster. | string | ✓ | | -| [instance_name](variables.tf#L192) | Name of primary instance. | string | ✓ | | -| [location](variables.tf#L203) | Region or zone of the cluster and instance. | string | ✓ | | -| [network_config](variables.tf#L259) | Network configuration for cluster and instance. Only one between psa_config and psc_config can be used. | object({…}) | ✓ | | -| [project_id](variables.tf#L302) | The ID of the project where this instances will be created. | string | ✓ | | +| [cluster_name](variables.tf#L81) | Name of the primary cluster. | string | ✓ | | +| [instance_name](variables.tf#L177) | Name of primary instance. | string | ✓ | | +| [location](variables.tf#L189) | Region or zone of the cluster and instance. | string | ✓ | | +| [network_config](variables.tf#L234) | Network configuration for cluster and instance. Only one between psa_config and psc_config can be used. | object({…}) | ✓ | | +| [project_id](variables.tf#L269) | The ID of the project where this instances will be created. | string | ✓ | | | [annotations](variables.tf#L17) | Map FLAG_NAME=>VALUE for annotations which allow client tools to store small amount of arbitrary data. | map(string) | | null | -| [automated_backup_configuration](variables.tf#L23) | Automated backup settings for cluster. | object({…}) | | {…} | -| [availability_type](variables.tf#L76) | Availability type for the primary replica. Either `ZONAL` or `REGIONAL`. | string | | "REGIONAL" | -| [client_connection_config](variables.tf#L82) | Client connection config. | object({…}) | | null | -| [cluster_display_name](variables.tf#L93) | Display name of the primary cluster. | string | | null | -| [continuous_backup_configuration](variables.tf#L104) | Continuous backup settings for cluster. | object({…}) | | {…} | -| [cross_region_replication](variables.tf#L117) | Cross region replication config. | object({…}) | | {} | -| [database_version](variables.tf#L143) | Database type and version to create. | string | | "POSTGRES_15" | -| [deletion_policy](variables.tf#L149) | AlloyDB cluster and instance deletion policy. | string | | null | -| [display_name](variables.tf#L155) | AlloyDB instance display name. | string | | null | -| [encryption_config](variables.tf#L161) | Set encryption configuration. KMS name format: 'projects/[PROJECT]/locations/[REGION]/keyRings/[RING]/cryptoKeys/[KEY_NAME]'. | object({…}) | | null | -| [flags](variables.tf#L171) | Map FLAG_NAME=>VALUE for database-specific tuning. | map(string) | | null | -| [gce_zone](variables.tf#L177) | The GCE zone that the instance should serve from. This can ONLY be specified for ZONAL instances. If present for a REGIONAL instance, an error will be thrown. | string | | null | -| [initial_user](variables.tf#L183) | AlloyDB cluster initial user credentials. | object({…}) | | null | -| [labels](variables.tf#L197) | Labels to be attached to all instances. | map(string) | | null | -| [machine_config](variables.tf#L208) | AlloyDB machine config. | object({…}) | | {…} | -| [maintenance_config](variables.tf#L219) | Set maintenance window configuration. | object({…}) | | {…} | -| [prefix](variables.tf#L292) | Optional prefix used to generate instance names. | string | | null | -| [project_number](variables.tf#L307) | The project number of the project where this instances will be created. Only used for testing purposes. | string | | null | -| [query_insights_config](variables.tf#L313) | Query insights config. | object({…}) | | {…} | -| [tag_bindings](variables.tf#L329) | Tag bindings for this service, in key => tag value id format. | map(string) | | {} | -| [users](variables.tf#L336) | Map of users to create in the primary instance (and replicated to other replicas). Set PASSWORD to null if you want to get an autogenerated password. The user types available are: 'ALLOYDB_BUILT_IN' or 'ALLOYDB_IAM_USER'. | map(object({…})) | | null | +| [automated_backup_configuration](variables.tf#L23) | Automated backup settings for cluster. | object({…}) | | {} | +| [availability_type](variables.tf#L58) | Availability type for the primary replica. Either `ZONAL` or `REGIONAL`. | string | | "REGIONAL" | +| [client_connection_config](variables.tf#L64) | Client connection config. | object({…}) | | null | +| [cluster_display_name](variables.tf#L75) | Display name of the primary cluster. | string | | null | +| [continuous_backup_configuration](variables.tf#L87) | Continuous backup settings for cluster. | object({…}) | | {} | +| [cross_region_replication](variables.tf#L97) | Cross region replication config. | object({…}) | | {} | +| [database_version](variables.tf#L129) | Database type and version to create. | string | | "POSTGRES_15" | +| [deletion_policy](variables.tf#L135) | AlloyDB cluster and instance deletion policy. | string | | null | +| [display_name](variables.tf#L141) | AlloyDB instance display name. | string | | null | +| [encryption_config](variables.tf#L147) | Set encryption configuration. KMS name format: 'projects/[PROJECT]/locations/[REGION]/keyRings/[RING]/cryptoKeys/[KEY_NAME]'. | object({…}) | | null | +| [flags](variables.tf#L156) | Map FLAG_NAME=>VALUE for database-specific tuning. | map(string) | | null | +| [gce_zone](variables.tf#L162) | The GCE zone that the instance should serve from. This can ONLY be specified for ZONAL instances. If present for a REGIONAL instance, an error will be thrown. | string | | null | +| [initial_user](variables.tf#L168) | AlloyDB cluster initial user credentials. | object({…}) | | null | +| [labels](variables.tf#L183) | Labels to be attached to all instances. | map(string) | | null | +| [machine_config](variables.tf#L195) | AlloyDB machine config. | object({…}) | | {} | +| [maintenance_config](variables.tf#L209) | Set maintenance window configuration. | object({…}) | | {} | +| [prefix](variables.tf#L259) | Optional prefix used to generate instance names. | string | | null | +| [project_number](variables.tf#L274) | The project number of the project where this instances will be created. Only used for testing purposes. | string | | null | +| [query_insights_config](variables.tf#L280) | Query insights config. | object({…}) | | {} | +| [read_pool](variables.tf#L291) | Map of read pool instances to create in the primary cluster. | map(object({…})) | | {} | +| [skip_await_major_version_upgrade](variables.tf#L336) | Set to true to skip awaiting on the major version upgrade of the cluster. | bool | | true | +| [subscription_type](variables.tf#L342) | The subscription type of cluster. Possible values are: 'STANDARD' or 'TRIAL'. | string | | "STANDARD" | +| [tag_bindings](variables.tf#L348) | Tag bindings for this service, in key => tag value id format. | map(string) | | {} | +| [users](variables.tf#L355) | Map of users to create in the primary instance (and replicated to other replicas). Set PASSWORD to null if you want to get an autogenerated password. The user types available are: 'ALLOYDB_BUILT_IN' or 'ALLOYDB_IAM_USER'. | map(object({…})) | | {} | ## Outputs | name | description | sensitive | |---|---|:---:| -| [id](outputs.tf#L24) | Fully qualified primary instance id. | | -| [ids](outputs.tf#L29) | Fully qualified ids of all instances. | | -| [instances](outputs.tf#L37) | AlloyDB instance resources. | ✓ | -| [ip](outputs.tf#L43) | IP address of the primary instance. | | -| [ips](outputs.tf#L48) | IP addresses of all instances. | | -| [name](outputs.tf#L55) | Name of the primary instance. | | -| [names](outputs.tf#L60) | Names of all instances. | | -| [psc_dns_name](outputs.tf#L68) | AlloyDB Primary instance PSC DNS name. | | -| [psc_dns_names](outputs.tf#L73) | AlloyDB instances PSC DNS names. | | -| [secondary_id](outputs.tf#L80) | Fully qualified primary instance id. | | -| [secondary_ip](outputs.tf#L85) | IP address of the primary instance. | | -| [service_attachment](outputs.tf#L90) | AlloyDB Primary instance service attachment. | | -| [service_attachments](outputs.tf#L95) | AlloyDB instances service attachment. | | -| [user_passwords](outputs.tf#L102) | Map of containing the password of all users created through terraform. | ✓ | +| [cluster_id](outputs.tf#L24) | Fully qualified primary cluster id. | | +| [cluster_name](outputs.tf#L29) | Name of the primary cluster. | | +| [id](outputs.tf#L34) | Fully qualified primary instance id. | | +| [ids](outputs.tf#L39) | Fully qualified ids of all instances. | | +| [instances](outputs.tf#L47) | AlloyDB instance resources. | ✓ | +| [ip](outputs.tf#L53) | IP address of the primary instance. | | +| [ips](outputs.tf#L58) | IP addresses of all instances. | | +| [name](outputs.tf#L65) | Name of the primary instance. | | +| [names](outputs.tf#L70) | Names of all instances. | | +| [outbound_public_ips](outputs.tf#L78) | Public IP addresses of the primary instance. | | +| [psc_dns_name](outputs.tf#L83) | AlloyDB Primary instance PSC DNS name. | | +| [psc_dns_names](outputs.tf#L88) | AlloyDB instances PSC DNS names. | | +| [public_ip](outputs.tf#L95) | Public IP address of the primary instance. | | +| [read_pool_ids](outputs.tf#L100) | Fully qualified ids of all read poll instances. | | +| [read_pool_ips](outputs.tf#L108) | IP addresses of all read poll instances. | | +| [secondary_cluster_id](outputs.tf#L116) | Fully qualified secondary cluster id. | | +| [secondary_cluster_name](outputs.tf#L121) | Name of the secondary cluster. | | +| [secondary_id](outputs.tf#L126) | Fully qualified secondary instance id. | | +| [secondary_ip](outputs.tf#L131) | IP address of the secondary instance. | | +| [secondary_outbound_public_ips](outputs.tf#L136) | Public IP addresses of the primary instance. | | +| [secondary_public_ip](outputs.tf#L141) | Public IP address of the secondary instance. | | +| [service_attachment](outputs.tf#L146) | AlloyDB Primary instance service attachment. | | +| [service_attachments](outputs.tf#L151) | AlloyDB instances service attachment. | | +| [user_passwords](outputs.tf#L158) | Map of containing the password of all users created through terraform. | ✓ | diff --git a/modules/alloydb/main.tf b/modules/alloydb/main.tf index ac3073b75..f55c4c610 100644 --- a/modules/alloydb/main.tf +++ b/modules/alloydb/main.tf @@ -17,42 +17,63 @@ locals { prefix = var.prefix == null ? "" : "${var.prefix}-" is_regional = var.availability_type == "REGIONAL" - # secondary instance type is aligned with cluster type unless apply is targeting a promotion, in that - # case cluster will be 'primary' while instance still 'secondary'. + + require_connectors = try(var.client_connection_config.require_connectors, false) ? true : null + ssl_mode = try(var.client_connection_config.ssl_config.ssl_mode, null) + + has_public_ip = try(var.network_config.psa_config.enable_public_ip, false) || try(var.network_config.psa_config.enable_outbound_public_ip, false) + authorized_external_networks = toset(try(var.network_config.psa_config.authorized_external_networks, [])) + enable_public_ip = try(var.network_config.psa_config.enable_public_ip, false) ? true : null + enable_outbound_public_ip = try(var.network_config.psa_config.enable_outbound_public_ip, false) ? true : null + allowed_consumer_projects = try(var.network_config.psc_config.allowed_consumer_projects, []) + primary_cluster_name = "${local.prefix}${var.cluster_name}" primary_instance_name = "${local.prefix}${var.instance_name}" + primary_kms_key_name = try(var.encryption_config.primary_kms_key_name, null) secondary_cluster_name = coalesce(var.cross_region_replication.secondary_cluster_name, "${var.cluster_name}-sec") secondary_instance_name = coalesce(var.cross_region_replication.secondary_instance_name, "${var.instance_name}-sec") + secondary_kms_key_name = try(var.encryption_config.secondary_kms_key_name, null) + # secondary instance type is aligned with cluster type unless apply is targeting a promotion, in that + # case cluster will be 'primary' while instance still 'secondary'. secondary_instance_type = try( var.cross_region_replication.promote_secondary && google_alloydb_cluster.secondary[0].cluster_type == "SECONDARY" ? "SECONDARY" : google_alloydb_cluster.secondary[0].cluster_type, null ) - users = { - for k, v in coalesce(var.users, {}) : - k => { - name = k - password = try(v.type, "ALLOYDB_BUILT_IN") == "ALLOYDB_BUILT_IN" ? try(random_password.passwords[k].result, v.password) : null - roles = v.roles - type = coalesce(v.type, "ALLOYDB_BUILT_IN") - } + secondary_machine_type = ( + try(var.cross_region_replication.secondary_machine_config.machine_type, null) != null + ? var.cross_region_replication.secondary_machine_config.machine_type + : var.machine_config.machine_type + ) + + read_pool = { + for name, instance in var.read_pool : name => merge(instance, { + require_connectors = try(instance.client_connection_config.require_connectors, false) ? true : null + ssl_mode = try(instance.client_connection_config.ssl_config.ssl_mode, null) + }) } } resource "google_alloydb_cluster" "primary" { - project = var.project_id - annotations = var.annotations - cluster_id = local.primary_cluster_name - cluster_type = var.cross_region_replication.switchover_mode ? "SECONDARY" : "PRIMARY" - database_version = var.database_version - deletion_policy = var.deletion_policy - display_name = coalesce(var.cluster_display_name, local.primary_cluster_name) - labels = var.labels - location = var.location + project = var.project_id + annotations = var.annotations + cluster_id = local.primary_cluster_name + cluster_type = var.cross_region_replication.switchover_mode ? "SECONDARY" : "PRIMARY" + database_version = var.database_version + deletion_policy = var.deletion_policy + display_name = coalesce(var.cluster_display_name, local.primary_cluster_name) + labels = var.labels + location = var.location + skip_await_major_version_upgrade = var.skip_await_major_version_upgrade + subscription_type = var.subscription_type - network_config { - network = try(var.network_config.psa_config.network, null) - allocated_ip_range = try(var.network_config.psa_config.allocated_ip_range, null) + # network_config block should exist only when PSA VPC resource link is present to prevent Terraform state drift + dynamic "network_config" { + for_each = var.network_config.psa_config != null ? [""] : [] + content { + network = var.network_config.psa_config.network + allocated_ip_range = var.network_config.psa_config.allocated_ip_range + } } dynamic "automated_backup_policy" { @@ -64,9 +85,9 @@ resource "google_alloydb_cluster" "primary" { labels = var.labels dynamic "encryption_config" { - for_each = var.encryption_config != null ? [""] : [] + for_each = local.primary_kms_key_name != null ? [""] : [] content { - kms_key_name = var.encryption_config.primary_kms_key_name + kms_key_name = local.primary_kms_key_name } } @@ -74,9 +95,9 @@ resource "google_alloydb_cluster" "primary" { days_of_week = var.automated_backup_configuration.weekly_schedule.days_of_week start_times { hours = var.automated_backup_configuration.weekly_schedule.start_times.hours - minutes = var.automated_backup_configuration.weekly_schedule.start_times.minutes - seconds = var.automated_backup_configuration.weekly_schedule.start_times.seconds - nanos = var.automated_backup_configuration.weekly_schedule.start_times.nanos + minutes = 0 + seconds = 0 + nanos = 0 } } @@ -96,24 +117,21 @@ resource "google_alloydb_cluster" "primary" { } } - dynamic "continuous_backup_config" { - for_each = var.continuous_backup_configuration.enabled ? [""] : [] - content { - enabled = true - recovery_window_days = var.continuous_backup_configuration.recovery_window_days - dynamic "encryption_config" { - for_each = var.encryption_config != null ? [""] : [] - content { - kms_key_name = var.encryption_config.primary_kms_key_name - } + continuous_backup_config { + enabled = var.continuous_backup_configuration.enabled + recovery_window_days = var.continuous_backup_configuration.recovery_window_days + dynamic "encryption_config" { + for_each = local.primary_kms_key_name != null ? [""] : [] + content { + kms_key_name = local.primary_kms_key_name } } } dynamic "encryption_config" { - for_each = var.encryption_config != null ? [""] : [] + for_each = local.primary_kms_key_name != null ? [""] : [] content { - kms_key_name = var.encryption_config.primary_kms_key_name + kms_key_name = local.primary_kms_key_name } } @@ -132,16 +150,20 @@ resource "google_alloydb_cluster" "primary" { day = var.maintenance_config.day start_time { hours = var.maintenance_config.start_time.hours - minutes = var.maintenance_config.start_time.minutes - seconds = var.maintenance_config.start_time.seconds - nanos = var.maintenance_config.start_time.nanos + minutes = 0 + seconds = 0 + nanos = 0 } } } } - psc_config { - psc_enabled = var.network_config.psc_config != null ? true : null + # psc_config block should exist only when PSC is enabled to prevent Terraform state drift + dynamic "psc_config" { + for_each = length(local.allowed_consumer_projects) > 0 ? [""] : [] + content { + psc_enabled = true + } } dynamic "secondary_config" { @@ -155,8 +177,6 @@ resource "google_alloydb_cluster" "primary" { lifecycle { ignore_changes = [ display_name, - network_config, - psc_config ] } } @@ -173,42 +193,43 @@ resource "google_alloydb_instance" "primary" { labels = var.labels dynamic "client_connection_config" { - for_each = var.client_connection_config != null ? [""] : [] + for_each = local.require_connectors != null || local.ssl_mode != null ? [""] : [] content { - require_connectors = var.client_connection_config.require_connectors + require_connectors = local.require_connectors dynamic "ssl_config" { - for_each = var.client_connection_config.ssl_config != null ? [""] : [] + for_each = local.ssl_mode != null ? [""] : [] content { - ssl_mode = var.client_connection_config.ssl_config.ssl_mode + ssl_mode = local.ssl_mode } } } } - dynamic "machine_config" { - for_each = var.machine_config != null ? [""] : [] - content { - cpu_count = var.machine_config.cpu_count - } + machine_config { + cpu_count = var.machine_config.cpu_count + machine_type = var.machine_config.machine_type } + # network_config block should exist only when (outbound) public IP is enabled to prevent Terraform state drift dynamic "network_config" { - for_each = var.network_config.psa_config != null ? [""] : [] + for_each = local.has_public_ip ? [""] : [] content { dynamic "authorized_external_networks" { - for_each = coalesce(var.network_config.psa_config.authorized_external_networks, []) + for_each = local.authorized_external_networks content { cidr_range = authorized_external_networks.value } } - enable_public_ip = var.network_config.psa_config.enable_public_ip + enable_public_ip = local.enable_public_ip + enable_outbound_public_ip = local.enable_outbound_public_ip } } + # psc_instance_config block should exist only when there are PSC allowed consumer projects to prevent Terraform state drift dynamic "psc_instance_config" { - for_each = var.network_config.psc_config != null ? [""] : [] + for_each = length(local.allowed_consumer_projects) > 0 ? [""] : [] content { - allowed_consumer_projects = var.network_config.psc_config.allowed_consumer_projects + allowed_consumer_projects = local.allowed_consumer_projects } } @@ -221,30 +242,29 @@ resource "google_alloydb_instance" "primary" { query_plans_per_minute = var.query_insights_config.query_plans_per_minute } } - - # waiting to fix this issue https://github.com/hashicorp/terraform-provider-google/issues/14944 - lifecycle { - ignore_changes = [ - network_config - ] - } } resource "google_alloydb_cluster" "secondary" { - count = var.cross_region_replication.enabled ? 1 : 0 - project = var.project_id - annotations = var.annotations - cluster_id = local.secondary_cluster_name - cluster_type = var.cross_region_replication.promote_secondary || var.cross_region_replication.switchover_mode ? "PRIMARY" : "SECONDARY" - database_version = var.database_version - deletion_policy = "FORCE" - display_name = coalesce(var.cross_region_replication.secondary_cluster_display_name, local.secondary_cluster_name) - labels = var.labels - location = var.cross_region_replication.region + count = var.cross_region_replication.enabled ? 1 : 0 + project = var.project_id + annotations = var.annotations + cluster_id = local.secondary_cluster_name + cluster_type = var.cross_region_replication.promote_secondary || var.cross_region_replication.switchover_mode ? "PRIMARY" : "SECONDARY" + database_version = var.database_version + deletion_policy = "FORCE" + display_name = coalesce(var.cross_region_replication.secondary_cluster_display_name, local.secondary_cluster_name) + labels = var.labels + location = var.cross_region_replication.region + skip_await_major_version_upgrade = var.skip_await_major_version_upgrade + subscription_type = var.subscription_type - network_config { - network = try(var.network_config.psa_config.network, null) - allocated_ip_range = try(var.network_config.psa_config.allocated_ip_range, null) + # network_config block should exist only when PSA VPC resource link is present to prevent Terraform state drift + dynamic "network_config" { + for_each = var.network_config.psa_config != null ? [""] : [] + content { + network = var.network_config.psa_config.network + allocated_ip_range = var.network_config.psa_config.allocated_ip_range + } } dynamic "automated_backup_policy" { @@ -256,9 +276,9 @@ resource "google_alloydb_cluster" "secondary" { labels = var.labels dynamic "encryption_config" { - for_each = var.encryption_config != null ? [""] : [] + for_each = local.secondary_kms_key_name != null ? [""] : [] content { - kms_key_name = var.encryption_config.secondary_kms_key_name + kms_key_name = local.secondary_kms_key_name } } @@ -266,9 +286,9 @@ resource "google_alloydb_cluster" "secondary" { days_of_week = var.automated_backup_configuration.weekly_schedule.days_of_week start_times { hours = var.automated_backup_configuration.weekly_schedule.start_times.hours - minutes = var.automated_backup_configuration.weekly_schedule.start_times.minutes - seconds = var.automated_backup_configuration.weekly_schedule.start_times.seconds - nanos = var.automated_backup_configuration.weekly_schedule.start_times.nanos + minutes = 0 + seconds = 0 + nanos = 0 } } @@ -288,24 +308,21 @@ resource "google_alloydb_cluster" "secondary" { } } - dynamic "continuous_backup_config" { - for_each = var.continuous_backup_configuration.enabled ? [""] : [] - content { - enabled = true - recovery_window_days = var.continuous_backup_configuration.recovery_window_days - dynamic "encryption_config" { - for_each = var.encryption_config != null ? [""] : [] - content { - kms_key_name = var.encryption_config.secondary_kms_key_name - } + continuous_backup_config { + enabled = var.continuous_backup_configuration.enabled + recovery_window_days = var.continuous_backup_configuration.recovery_window_days + dynamic "encryption_config" { + for_each = local.secondary_kms_key_name != null ? [""] : [] + content { + kms_key_name = local.secondary_kms_key_name } } } dynamic "encryption_config" { - for_each = var.encryption_config != null ? [""] : [] + for_each = local.secondary_kms_key_name != null ? [""] : [] content { - kms_key_name = var.encryption_config.secondary_kms_key_name + kms_key_name = local.secondary_kms_key_name } } @@ -316,16 +333,20 @@ resource "google_alloydb_cluster" "secondary" { day = var.maintenance_config.day start_time { hours = var.maintenance_config.start_time.hours - minutes = var.maintenance_config.start_time.minutes - seconds = var.maintenance_config.start_time.seconds - nanos = var.maintenance_config.start_time.nanos + minutes = 0 + seconds = 0 + nanos = 0 } } } } - psc_config { - psc_enabled = var.network_config.psc_config != null ? true : null + # psc_config block should exist only when PSC is enabled to prevent Terraform state drift + dynamic "psc_config" { + for_each = length(local.allowed_consumer_projects) > 0 ? [""] : [] + content { + psc_enabled = true + } } dynamic "secondary_config" { @@ -336,12 +357,11 @@ resource "google_alloydb_cluster" "secondary" { } depends_on = [google_alloydb_instance.primary] + # waiting to fix this issue https://github.com/hashicorp/terraform-provider-google/issues/14944 lifecycle { ignore_changes = [ display_name, - network_config, - psc_config ] } } @@ -359,42 +379,43 @@ resource "google_alloydb_instance" "secondary" { labels = var.labels dynamic "client_connection_config" { - for_each = var.client_connection_config != null ? [""] : [] + for_each = local.require_connectors != null || local.ssl_mode != null ? [""] : [] content { - require_connectors = var.client_connection_config.require_connectors + require_connectors = local.require_connectors dynamic "ssl_config" { - for_each = var.client_connection_config.ssl_config != null ? [""] : [] + for_each = local.ssl_mode != null ? [""] : [] content { - ssl_mode = var.client_connection_config.ssl_config.ssl_mode + ssl_mode = local.ssl_mode } } } } - dynamic "machine_config" { - for_each = var.machine_config != null || var.cross_region_replication.secondary_machine_config != null ? [""] : [] - content { - cpu_count = coalesce(try(var.cross_region_replication.secondary_machine_config.cpu_count, null), try(var.machine_config.cpu_count, null)) - } + machine_config { + cpu_count = coalesce(try(var.cross_region_replication.secondary_machine_config.cpu_count, null), var.machine_config.cpu_count) + machine_type = local.secondary_machine_type } + # network_config block should exist only when (outbound) public IP is enabled to prevent Terraform state drift dynamic "network_config" { - for_each = var.network_config.psa_config != null ? [""] : [] + for_each = local.has_public_ip ? [""] : [] content { dynamic "authorized_external_networks" { - for_each = coalesce(var.network_config.psa_config.authorized_external_networks, []) + for_each = local.authorized_external_networks content { cidr_range = authorized_external_networks.value } } - enable_public_ip = var.network_config.psa_config.enable_public_ip + enable_public_ip = local.enable_public_ip + enable_outbound_public_ip = local.enable_outbound_public_ip } } + # psc_instance_config block should exist only when there are PSC allowed consumer projects to prevent Terraform state drift dynamic "psc_instance_config" { - for_each = var.network_config.psc_config != null ? [""] : [] + for_each = length(local.allowed_consumer_projects) > 0 ? [""] : [] content { - allowed_consumer_projects = var.network_config.psc_config.allowed_consumer_projects + allowed_consumer_projects = local.allowed_consumer_projects } } @@ -407,31 +428,100 @@ resource "google_alloydb_instance" "secondary" { query_plans_per_minute = var.query_insights_config.query_plans_per_minute } } +} - # waiting to fix this issue https://github.com/hashicorp/terraform-provider-google/issues/14944 - lifecycle { - ignore_changes = [ - network_config - ] +# Read pool (instance_type = "READ_POOL") cannot be created for secondary cluster +# and does not support the following attributes: +# * availability_type: Because 1 node pool (read_pool_config.node_count) is always zonal, two or more is always regional. +# * gce_zone +# * network_config.enable_outbound_public_ip +resource "google_alloydb_instance" "read_pool" { + for_each = local.read_pool + annotations = var.annotations + cluster = google_alloydb_cluster.primary.id + database_flags = each.value.flags + display_name = coalesce(each.value.display_name, "${local.prefix}${each.key}") + instance_id = "${local.prefix}${each.key}" + instance_type = "READ_POOL" + labels = var.labels + + dynamic "client_connection_config" { + for_each = each.value.require_connectors != null || each.value.ssl_mode != null ? [""] : [] + content { + require_connectors = each.value.require_connectors + dynamic "ssl_config" { + for_each = each.value.ssl_mode != null ? [""] : [] + content { + ssl_mode = each.value.ssl_mode + } + } + } } + + machine_config { + cpu_count = each.value.machine_config.cpu_count + machine_type = each.value.machine_config.machine_type + } + + # network_config block should exist only when (outbound) public IP is enabled to prevent Terraform state drift + dynamic "network_config" { + for_each = each.value.network_config.enable_public_ip ? [""] : [] + content { + dynamic "authorized_external_networks" { + for_each = toset(each.value.network_config.authorized_external_networks) + content { + cidr_range = authorized_external_networks.value + } + } + enable_public_ip = true + } + } + + # psc_instance_config block should exist only when there are PSC allowed consumer projects to prevent Terraform state drift + dynamic "psc_instance_config" { + for_each = length(local.allowed_consumer_projects) > 0 ? [""] : [] + content { + allowed_consumer_projects = local.allowed_consumer_projects + } + } + + read_pool_config { + node_count = each.value.node_count + } + + dynamic "query_insights_config" { + for_each = each.value.query_insights_config != null ? [""] : [] + content { + query_string_length = each.value.query_insights_config.query_string_length + record_application_tags = each.value.query_insights_config.record_application_tags + record_client_address = each.value.query_insights_config.record_client_address + query_plans_per_minute = each.value.query_insights_config.query_plans_per_minute + } + } + + depends_on = [google_alloydb_instance.primary] } resource "random_password" "passwords" { - for_each = toset([ - for k, v in coalesce(var.users, {}) : - k - if v.password == null - ]) + for_each = { + for k, v in var.users : + k => v + if v.type == "ALLOYDB_BUILT_IN" && v.password == null + } length = 16 special = true } resource "google_alloydb_user" "users" { - for_each = local.users - cluster = google_alloydb_cluster.primary.id - user_id = each.value.name - user_type = each.value.type - password = each.value.password + for_each = var.users + cluster = google_alloydb_cluster.primary.id + user_id = each.key + user_type = each.value.type + password = ( + each.value.type == "ALLOYDB_BUILT_IN" && each.value.password == null ? + random_password.passwords[each.key].result + : each.value.password + ) database_roles = each.value.roles depends_on = [google_alloydb_instance.primary] } diff --git a/modules/alloydb/outputs.tf b/modules/alloydb/outputs.tf index a7066d0a7..5918ef340 100644 --- a/modules/alloydb/outputs.tf +++ b/modules/alloydb/outputs.tf @@ -21,6 +21,16 @@ locals { } } +output "cluster_id" { + description = "Fully qualified primary cluster id." + value = google_alloydb_cluster.primary.id +} + +output "cluster_name" { + description = "Name of the primary cluster." + value = google_alloydb_cluster.primary.name +} + output "id" { description = "Fully qualified primary instance id." value = google_alloydb_instance.primary.id @@ -65,6 +75,11 @@ output "names" { } } +output "outbound_public_ips" { + description = "Public IP addresses of the primary instance." + value = google_alloydb_instance.primary.outbound_public_ip_addresses +} + output "psc_dns_name" { description = "AlloyDB Primary instance PSC DNS name." value = try(google_alloydb_instance.primary.psc_instance_config[0].psc_dns_name, null) @@ -77,16 +92,57 @@ output "psc_dns_names" { } } +output "public_ip" { + description = "Public IP address of the primary instance." + value = google_alloydb_instance.primary.public_ip_address +} + +output "read_pool_ids" { + description = "Fully qualified ids of all read poll instances." + value = { + for name, instance in google_alloydb_instance.read_pool : + name => instance.id + } +} + +output "read_pool_ips" { + description = "IP addresses of all read poll instances." + value = { + for name, instance in google_alloydb_instance.read_pool : + name => instance.ip_address + } +} + +output "secondary_cluster_id" { + description = "Fully qualified secondary cluster id." + value = var.cross_region_replication.enabled ? google_alloydb_cluster.secondary[0].id : null +} + +output "secondary_cluster_name" { + description = "Name of the secondary cluster." + value = var.cross_region_replication.enabled ? google_alloydb_cluster.secondary[0].name : null +} + output "secondary_id" { - description = "Fully qualified primary instance id." + description = "Fully qualified secondary instance id." value = var.cross_region_replication.enabled ? google_alloydb_instance.secondary[0].id : null } output "secondary_ip" { - description = "IP address of the primary instance." + description = "IP address of the secondary instance." value = var.cross_region_replication.enabled ? google_alloydb_instance.secondary[0].ip_address : null } +output "secondary_outbound_public_ips" { + description = "Public IP addresses of the primary instance." + value = var.cross_region_replication.enabled ? google_alloydb_instance.secondary[0].outbound_public_ip_addresses : null +} + +output "secondary_public_ip" { + description = "Public IP address of the secondary instance." + value = var.cross_region_replication.enabled ? google_alloydb_instance.secondary[0].public_ip_address : null +} + output "service_attachment" { description = "AlloyDB Primary instance service attachment." value = try(google_alloydb_instance.primary.psc_instance_config[0].service_attachment_link, null) diff --git a/modules/alloydb/variables.tf b/modules/alloydb/variables.tf index 299dd8e2f..5c7e73021 100644 --- a/modules/alloydb/variables.tf +++ b/modules/alloydb/variables.tf @@ -22,7 +22,6 @@ variable "annotations" { variable "automated_backup_configuration" { description = "Automated backup settings for cluster." - nullable = false type = object({ enabled = optional(bool, false) backup_window = optional(string, "1800s") @@ -32,44 +31,27 @@ variable "automated_backup_configuration" { "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY" ]) start_times = optional(object({ - hours = optional(number, 23) - minutes = optional(number, 0) - seconds = optional(number, 0) - nanos = optional(number, 0) + hours = optional(number, 23) }), {}) }), {}) retention_count = optional(number, 7) - retention_period = optional(string, null) + retention_period = optional(string) }) - default = { - enabled = false - backup_window = "1800s" - location = null - weekly_schedule = { - days_of_week = ["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY"] - start_times = { - hours = 23 - minutes = 0 - seconds = 0 - nanos = 0 - } - } - retention_count = 7 - retention_period = null - } + default = {} + nullable = false validation { condition = ( var.automated_backup_configuration.enabled ? ( - # Maintenance window validation below + # Backup window validation below !(var.automated_backup_configuration.retention_count != null && var.automated_backup_configuration.retention_period != null) && - # Maintenance window day validation - length([ - for day in var.automated_backup_configuration.weekly_schedule.days_of_week : true - if contains(["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY"], day) - ]) == length(var.automated_backup_configuration.weekly_schedule.days_of_week) + # Backup window hours below + var.automated_backup_configuration.weekly_schedule.start_times.hours >= 0 && + var.automated_backup_configuration.weekly_schedule.start_times.hours <= 23 && + # Backup window day validation + setintersection(["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY"], var.automated_backup_configuration.weekly_schedule.days_of_week) == toset(var.automated_backup_configuration.weekly_schedule.days_of_week) ) : true ) - error_message = "Days of week must contains one or more days with the following format 'MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY', 'SUNDAY'. You can only specify retention_count or retention_period." + error_message = "Days of week must contains one or more days with the following format 'MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY', 'SUNDAY'. Backup window hour must be between 0 and 23. You can only specify retention_count or retention_period." } } @@ -85,7 +67,7 @@ variable "client_connection_config" { require_connectors = optional(bool, false) ssl_config = optional(object({ ssl_mode = string - }), null) + })) }) default = null } @@ -99,19 +81,17 @@ variable "cluster_display_name" { variable "cluster_name" { description = "Name of the primary cluster." type = string + nullable = false } variable "continuous_backup_configuration" { description = "Continuous backup settings for cluster." - nullable = true type = object({ - enabled = optional(bool, false) + enabled = optional(bool, true) recovery_window_days = optional(number, 14) }) - default = { - enabled = true - recovery_window_days = 14 - } + nullable = false + default = {} } variable "cross_region_replication" { @@ -120,23 +100,29 @@ variable "cross_region_replication" { enabled = optional(bool, false) promote_secondary = optional(bool, false) switchover_mode = optional(bool, false) - region = optional(string, null) - secondary_cluster_display_name = optional(string, null) - secondary_cluster_name = optional(string, null) - secondary_instance_display_name = optional(string, null) - secondary_instance_name = optional(string, null) + region = optional(string) + secondary_cluster_display_name = optional(string) + secondary_cluster_name = optional(string) + secondary_instance_display_name = optional(string) + secondary_instance_name = optional(string) secondary_machine_config = optional(object({ - cpu_count = number - }), null) + cpu_count = number + machine_type = optional(string) + })) }) - default = {} + default = {} + nullable = false validation { condition = !var.cross_region_replication.enabled || var.cross_region_replication.enabled && var.cross_region_replication.region != null error_message = "Region must be available when cross region replication is enabled." } validation { condition = !(var.cross_region_replication.switchover_mode && var.cross_region_replication.promote_secondary) - error_message = "Please choose to either promote secondary cluster or align an existing cluster after swtichover." + error_message = "Please choose to either promote secondary cluster or align an existing cluster after switchover." + } + validation { + condition = contains([2, 4, 8, 16, 32, 64, 96, 128], try(var.cross_region_replication.secondary_machine_config.cpu_count, 2)) + error_message = "The number of CPU's in the VM instance must be one of [2, 4, 8, 16, 32, 64, 96, 128]" } } @@ -162,10 +148,9 @@ variable "encryption_config" { description = "Set encryption configuration. KMS name format: 'projects/[PROJECT]/locations/[REGION]/keyRings/[RING]/cryptoKeys/[KEY_NAME]'." type = object({ primary_kms_key_name = string - secondary_kms_key_name = optional(string, null) + secondary_kms_key_name = optional(string) }) - default = null - nullable = true + default = null } variable "flags" { @@ -183,7 +168,7 @@ variable "gce_zone" { variable "initial_user" { description = "AlloyDB cluster initial user credentials." type = object({ - user = optional(string, "root") + user = optional(string, "postgres") password = string }) default = null @@ -192,6 +177,7 @@ variable "initial_user" { variable "instance_name" { description = "Name of primary instance." type = string + nullable = false } variable "labels" { @@ -203,16 +189,20 @@ variable "labels" { variable "location" { description = "Region or zone of the cluster and instance." type = string + nullable = false } variable "machine_config" { description = "AlloyDB machine config." type = object({ - cpu_count = optional(number, 2) + cpu_count = optional(number, 2) + machine_type = optional(string) }) nullable = false - default = { - cpu_count = 2 + default = {} + validation { + condition = contains([2, 4, 8, 16, 32, 64, 96, 128], var.machine_config.cpu_count) + error_message = "The number of CPU's in the VM instance must be one of [2, 4, 8, 16, 32, 64, 96, 128]" } } @@ -222,37 +212,22 @@ variable "maintenance_config" { enabled = optional(bool, false) day = optional(string, "SUNDAY") start_time = optional(object({ - hours = optional(number, 23) - minutes = optional(number, 0) - seconds = optional(number, 0) - nanos = optional(number, 0) + hours = optional(number, 23) }), {}) }) - default = { - enabled = false - day = "SUNDAY" - start_time = { - hours = 23 - minutes = 0 - seconds = 0 - nanos = 0 - } - } + default = {} validation { condition = ( var.maintenance_config.enabled ? ( # Maintenance window validation below var.maintenance_config.start_time.hours >= 0 && var.maintenance_config.start_time.hours <= 23 && - var.maintenance_config.start_time.minutes == 0 && - var.maintenance_config.start_time.seconds == 0 && - var.maintenance_config.start_time.nanos == 0 && # Maintenance window day validation contains([ "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY" ], var.maintenance_config.day)) : true ) - error_message = "Maintenance window day must one of 'MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY', 'SUNDAY'. Maintenance window hour must be between 0 and 23 and maintenance window minutes, seconds and nanos should be 0." + error_message = "Maintenance window day must one of 'MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY', 'SUNDAY'. Maintenance window hour must be between 0 and 23." } } @@ -260,28 +235,20 @@ variable "network_config" { description = "Network configuration for cluster and instance. Only one between psa_config and psc_config can be used." type = object({ psa_config = optional(object({ - network = optional(string) + network = string allocated_ip_range = optional(string) authorized_external_networks = optional(list(string), []) enable_public_ip = optional(bool, false) + enable_outbound_public_ip = optional(bool, false) })) psc_config = optional(object({ - allowed_consumer_projects = optional(list(string), []) - }), null) + allowed_consumer_projects = list(string) + })) }) nullable = false validation { - condition = ( - var.network_config.psa_config == null || ( - ( - try(var.network_config.psa_config.enable_public_ip, false) && - try(length(var.network_config.psa_config.authorized_external_networks), 0) > 0 - ) || ( - try(length(var.network_config.psa_config.authorized_external_networks), 0) == 0 - ) - ) - ) - error_message = "A list of external network authorized to access this instance is required only in case public ip is enabled for the instance." + condition = try(length(var.network_config.psa_config.authorized_external_networks) > 0, false) ? try(var.network_config.psa_config.enable_public_ip, false) : true + error_message = "A list of external network authorized to access this instance is required only in case public IP is enabled for the instance." } validation { condition = (var.network_config.psc_config == null) != (var.network_config.psa_config == null) @@ -318,12 +285,64 @@ variable "query_insights_config" { record_client_address = optional(bool, true) query_plans_per_minute = optional(number, 5) }) - default = { - query_string_length = 1024 - record_application_tags = true - record_client_address = true - query_plans_per_minute = 5 + default = {} +} + +variable "read_pool" { + description = "Map of read pool instances to create in the primary cluster." + type = map(object({ + display_name = optional(string) + node_count = optional(number, 1) + flags = optional(map(string)) + client_connection_config = optional(object({ + require_connectors = optional(bool, false) + ssl_config = optional(object({ + ssl_mode = string + })) + })) + machine_config = optional(object({ + cpu_count = optional(number, 2) + machine_type = optional(string) + }), {}) + network_config = optional(object({ + authorized_external_networks = optional(list(string), []) + enable_public_ip = optional(bool, false) + }), {}) + query_insights_config = optional(object({ + query_string_length = optional(number, 1024) + record_application_tags = optional(bool, true) + record_client_address = optional(bool, true) + query_plans_per_minute = optional(number, 5) + })) + })) + nullable = false + default = {} + validation { + condition = alltrue([ + for k, v in var.read_pool : + contains([2, 4, 8, 16, 32, 64, 96, 128], v.machine_config.cpu_count) + ]) + error_message = "The number of CPU's in the VM instance must be one of [2, 4, 8, 16, 32, 64, 96, 128]" } + validation { + condition = alltrue([ + for k, v in var.read_pool : + try(length(v.network_config.psa_config.authorized_external_networks) > 0, false) ? try(v.network_config.psa_config.enable_public_ip, false) : true + ]) + error_message = "A list of external network authorized to access this replica pool instance is required only in case public IP is enabled for the replica pool instance." + } +} + +variable "skip_await_major_version_upgrade" { + description = "Set to true to skip awaiting on the major version upgrade of the cluster." + type = bool + default = true +} + +variable "subscription_type" { + description = "The subscription type of cluster. Possible values are: 'STANDARD' or 'TRIAL'." + type = string + default = "STANDARD" } variable "tag_bindings" { @@ -338,14 +357,15 @@ variable "users" { type = map(object({ password = optional(string) roles = optional(list(string), ["alloydbsuperuser"]) - type = optional(string) + type = optional(string, "ALLOYDB_BUILT_IN") })) - default = null + nullable = false + default = {} validation { condition = alltrue([ - for user in coalesce(var.users, {}) : - try(contains(["ALLOYDB_BUILT_IN", "ALLOYDB_IAM_USER"], user.type), true) + for user in var.users : + contains(["ALLOYDB_BUILT_IN", "ALLOYDB_IAM_USER"], user.type) ]) - error_message = "User type must one of 'ALLOYDB_BUILT_IN', 'ALLOYDB_IAM_USER'" + error_message = "User type must one of 'ALLOYDB_BUILT_IN', 'ALLOYDB_IAM_USER'." } } diff --git a/tests/modules/alloydb/examples/cmek.yaml b/tests/modules/alloydb/examples/cmek.yaml index a893a6b70..9df488d9e 100644 --- a/tests/modules/alloydb/examples/cmek.yaml +++ b/tests/modules/alloydb/examples/cmek.yaml @@ -18,8 +18,8 @@ values: cluster_id: primary cluster_type: PRIMARY continuous_backup_config: - - enabled: true - recovery_window_days: 14 + - enabled: true + recovery_window_days: 14 database_version: POSTGRES_15 deletion_policy: DEFAULT display_name: primary @@ -28,12 +28,13 @@ values: labels: null location: europe-west8 maintenance_update_policy: [] - network_config: - - allocated_ip_range: null project: test-alloycmek + psc_config: [] restore_backup_source: [] restore_continuous_backup_source: [] secondary_config: [] + skip_await_major_version_upgrade: true + subscription_type: STANDARD timeouts: null module.alloydb.google_alloydb_instance.primary: annotations: null @@ -44,15 +45,12 @@ values: instance_type: PRIMARY labels: null machine_config: - - cpu_count: 2 - network_config: - - authorized_external_networks: [] - enable_public_ip: false + - cpu_count: 2 query_insights_config: - - query_plans_per_minute: 5 - query_string_length: 1024 - record_application_tags: true - record_client_address: true + - query_plans_per_minute: 5 + query_string_length: 1024 + record_application_tags: true + record_client_address: true read_pool_config: [] timeouts: null diff --git a/tests/modules/alloydb/examples/cross_region_replication.yaml b/tests/modules/alloydb/examples/cross_region_replication.yaml index b25154176..19aef1f99 100644 --- a/tests/modules/alloydb/examples/cross_region_replication.yaml +++ b/tests/modules/alloydb/examples/cross_region_replication.yaml @@ -18,9 +18,9 @@ values: cluster_id: db cluster_type: PRIMARY continuous_backup_config: - - enabled: true - encryption_config: [] - recovery_window_days: 14 + - enabled: true + encryption_config: [] + recovery_window_days: 14 database_version: POSTGRES_15 deletion_policy: DEFAULT display_name: db @@ -31,21 +31,24 @@ values: location: europe-west8 maintenance_update_policy: [] network_config: - - allocated_ip_range: null - network: projects/xxx/global/networks/aaa + - allocated_ip_range: null + network: projects/xxx/global/networks/aaa project: project-id + psc_config: [] restore_backup_source: [] restore_continuous_backup_source: [] secondary_config: [] + skip_await_major_version_upgrade: true + subscription_type: STANDARD timeouts: null module.alloydb.google_alloydb_cluster.secondary[0]: annotations: null cluster_id: db-sec cluster_type: SECONDARY continuous_backup_config: - - enabled: true - encryption_config: [] - recovery_window_days: 14 + - enabled: true + encryption_config: [] + recovery_window_days: 14 database_version: POSTGRES_15 deletion_policy: FORCE display_name: db-sec @@ -56,12 +59,16 @@ values: location: europe-west12 maintenance_update_policy: [] network_config: - - allocated_ip_range: null - network: projects/xxx/global/networks/aaa + - allocated_ip_range: null + network: projects/xxx/global/networks/aaa project: project-id + psc_config: [] restore_backup_source: [] restore_continuous_backup_source: [] - secondary_config: [{}] + secondary_config: + - {} + skip_await_major_version_upgrade: true + subscription_type: STANDARD timeouts: null module.alloydb.google_alloydb_instance.primary: annotations: null @@ -72,15 +79,12 @@ values: instance_type: PRIMARY labels: null machine_config: - - cpu_count: 2 - network_config: - - authorized_external_networks: [] - enable_public_ip: false + - cpu_count: 2 query_insights_config: - - query_plans_per_minute: 5 - query_string_length: 1024 - record_application_tags: true - record_client_address: true + - query_plans_per_minute: 5 + query_string_length: 1024 + record_application_tags: true + record_client_address: true read_pool_config: [] timeouts: null module.alloydb.google_alloydb_instance.secondary[0]: @@ -92,15 +96,12 @@ values: instance_type: SECONDARY labels: null machine_config: - - cpu_count: 2 - network_config: - - authorized_external_networks: [] - enable_public_ip: false + - cpu_count: 2 query_insights_config: - - query_plans_per_minute: 5 - query_string_length: 1024 - record_application_tags: true - record_client_address: true + - query_plans_per_minute: 5 + query_string_length: 1024 + record_application_tags: true + record_client_address: true read_pool_config: [] timeouts: null diff --git a/tests/modules/alloydb/examples/custom.yaml b/tests/modules/alloydb/examples/custom.yaml index a2bcf0b4e..53ac5f6d6 100644 --- a/tests/modules/alloydb/examples/custom.yaml +++ b/tests/modules/alloydb/examples/custom.yaml @@ -18,9 +18,9 @@ values: cluster_id: primary cluster_type: PRIMARY continuous_backup_config: - - enabled: true - encryption_config: [] - recovery_window_days: 14 + - enabled: true + encryption_config: [] + recovery_window_days: 14 database_version: POSTGRES_15 deletion_policy: DEFAULT display_name: primary @@ -31,12 +31,15 @@ values: location: europe-west8 maintenance_update_policy: [] network_config: - - allocated_ip_range: null - network: projects/xxx/global/networks/aaa + - allocated_ip_range: null + network: projects/xxx/global/networks/aaa project: project-id + psc_config: [] restore_backup_source: [] restore_continuous_backup_source: [] secondary_config: [] + skip_await_major_version_upgrade: true + subscription_type: STANDARD timeouts: null module.alloydb.google_alloydb_instance.primary: annotations: null @@ -52,28 +55,24 @@ values: instance_type: PRIMARY labels: null machine_config: - - cpu_count: 2 - network_config: - - authorized_external_networks: [] - enable_public_ip: false + - cpu_count: 2 query_insights_config: - - query_plans_per_minute: 5 - query_string_length: 1024 - record_application_tags: true - record_client_address: true + - query_plans_per_minute: 5 + query_string_length: 1024 + record_application_tags: true + record_client_address: true read_pool_config: [] timeouts: null module.alloydb.google_alloydb_user.users["user1"]: database_roles: - - alloydbsuperuser - password: null + - alloydbsuperuser timeouts: null user_id: user1 user_type: ALLOYDB_BUILT_IN module.alloydb.google_alloydb_user.users["user2"]: database_roles: - - alloydbsuperuser - password: null + - alloydbsuperuser + password: mypassword timeouts: null user_id: user2 user_type: ALLOYDB_BUILT_IN diff --git a/tests/modules/alloydb/examples/psc.yaml b/tests/modules/alloydb/examples/psc.yaml index 1866d7dc3..3edf96808 100644 --- a/tests/modules/alloydb/examples/psc.yaml +++ b/tests/modules/alloydb/examples/psc.yaml @@ -18,9 +18,9 @@ values: cluster_id: db cluster_type: PRIMARY continuous_backup_config: - - enabled: true - encryption_config: [] - recovery_window_days: 14 + - enabled: true + encryption_config: [] + recovery_window_days: 14 database_version: POSTGRES_15 deletion_policy: DEFAULT display_name: db @@ -30,15 +30,14 @@ values: labels: null location: europe-west8 maintenance_update_policy: [] - network_config: - - allocated_ip_range: null - network: null project: project-id psc_config: - - psc_enabled: true + - psc_enabled: true restore_backup_source: [] restore_continuous_backup_source: [] secondary_config: [] + skip_await_major_version_upgrade: true + subscription_type: STANDARD timeouts: null module.alloydb.google_alloydb_instance.primary: annotations: null @@ -49,15 +48,15 @@ values: instance_type: PRIMARY labels: null machine_config: - - cpu_count: 2 + - cpu_count: 2 psc_instance_config: - - allowed_consumer_projects: - - '123' + - allowed_consumer_projects: + - '123' query_insights_config: - - query_plans_per_minute: 5 - query_string_length: 1024 - record_application_tags: true - record_client_address: true + - query_plans_per_minute: 5 + query_string_length: 1024 + record_application_tags: true + record_client_address: true read_pool_config: [] timeouts: null diff --git a/tests/modules/alloydb/examples/read_pool.yaml b/tests/modules/alloydb/examples/read_pool.yaml new file mode 100644 index 000000000..76b9f8580 --- /dev/null +++ b/tests/modules/alloydb/examples/read_pool.yaml @@ -0,0 +1,94 @@ +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +values: + module.alloydb.google_alloydb_cluster.primary: + annotations: null + cluster_id: db + cluster_type: PRIMARY + continuous_backup_config: + - enabled: true + encryption_config: [] + recovery_window_days: 14 + database_version: POSTGRES_15 + deletion_policy: DEFAULT + display_name: db + encryption_config: [] + etag: null + initial_user: [] + labels: null + location: europe-west8 + maintenance_update_policy: [] + network_config: + - allocated_ip_range: null + network: projects/xxx/global/networks/aaa + project: project-id + psc_config: [] + restore_backup_source: [] + restore_continuous_backup_source: [] + secondary_config: [] + skip_await_major_version_upgrade: true + subscription_type: STANDARD + timeouts: null + module.alloydb.google_alloydb_instance.primary: + annotations: null + availability_type: REGIONAL + display_name: db + gce_zone: null + instance_id: db + instance_type: PRIMARY + labels: null + machine_config: + - cpu_count: 2 + query_insights_config: + - query_plans_per_minute: 5 + query_string_length: 1024 + record_application_tags: true + record_client_address: true + read_pool_config: [] + timeouts: null + module.alloydb.google_alloydb_instance.read_pool["regional-read-pool"]: + annotations: null + display_name: regional-read-pool + gce_zone: null + instance_id: regional-read-pool + instance_type: READ_POOL + labels: null + machine_config: + - cpu_count: 2 + read_pool_config: + - node_count: 2 + timeouts: null + module.alloydb.google_alloydb_instance.read_pool["zonal-read-pool"]: + annotations: null + display_name: zonal-read-pool + effective_labels: + goog-terraform-provisioned: 'true' + gce_zone: null + instance_id: zonal-read-pool + instance_type: READ_POOL + labels: null + machine_config: + - cpu_count: 2 + read_pool_config: + - node_count: 1 + timeouts: null + +counts: + google_alloydb_cluster: 1 + google_alloydb_instance: 3 + modules: 1 + resources: 4 + +outputs: {} diff --git a/tests/modules/alloydb/examples/simple.yaml b/tests/modules/alloydb/examples/simple.yaml index c7dc37a05..3add09656 100644 --- a/tests/modules/alloydb/examples/simple.yaml +++ b/tests/modules/alloydb/examples/simple.yaml @@ -30,14 +30,13 @@ values: labels: null location: europe-west8 maintenance_update_policy: [] - network_config: - - allocated_ip_range: null project: test-alloydb - psc_config: - - psc_enabled: null + psc_config: [] restore_backup_source: [] restore_continuous_backup_source: [] secondary_config: [] + skip_await_major_version_upgrade: true + subscription_type: STANDARD timeouts: null module.alloydb.google_alloydb_instance.primary: annotations: null @@ -49,9 +48,6 @@ values: labels: null machine_config: - cpu_count: 2 - network_config: - - authorized_external_networks: [] - enable_public_ip: false query_insights_config: - query_plans_per_minute: 5 query_string_length: 1024