projects/alloydbomni-ha-patroni-etcd/terraform/main.tf (236 lines of code) (raw):

# # 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 # # https://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. # terraform { required_version = ">= 1.1.3" required_providers { google = { source = "hashicorp/google" version = ">= 4.0.0" } } provider_meta "google" { module_name = "cloud-solutions/alloydbomni-patroni-ha-deploy-v0.1" } } data "google_project" "project" { project_id = var.project_id } provider "google" { billing_project = var.project_id project = var.project_id region = var.region zone = var.zones[0] } locals { etcd_ip_count = var.node_count patroni_ip_count = var.node_count upload_scripts_path = "../src/" machine_type = "n2-highmem-4" image = "ubuntu-os-cloud/ubuntu-2204-lts" etcd_ip_names = [for idx in range(local.etcd_ip_count) : "alloydb-patroni-internal-etcd-ip-${idx + 1}"] patroni_ip_names = [for idx in range(local.patroni_ip_count) : "alloydb-patroni-internal-patroni-ip-${idx + 1}"] effective_replication_user_password = var.replication_user_password != "" ? var.replication_user_password : random_password.replication_user_password[0].result effective_postgres_super_user_password = var.postgres_super_user_password != "" ? var.postgres_super_user_password : random_password.postgres_super_user_password[0].result } resource "random_id" "bucket_suffix" { byte_length = 4 } resource "random_password" "postgres_super_user_password" { count = var.postgres_super_user_password != "" ? 0 : 1 length = 16 special = true } resource "random_password" "replication_user_password" { count = var.replication_user_password != "" ? 0 : 1 length = 16 special = true } resource "google_compute_network" "vpc" { name = "alloydb-patroni-vpc" auto_create_subnetworks = false } resource "google_compute_subnetwork" "subnet" { name = "alloydb-patroni-subnet" region = var.region ip_cidr_range = "10.0.1.0/24" network = google_compute_network.vpc.id } resource "google_compute_address" "internal_ips" { for_each = toset(flatten([ local.etcd_ip_names, local.patroni_ip_names ])) name = each.key address_type = "INTERNAL" region = var.region subnetwork = google_compute_subnetwork.subnet.id } resource "google_compute_firewall" "firewalls" { for_each = tomap({ allow_ssh = { name = "alloydb-patroni-fw-allow-ssh" description = "Firewall rules to allow SSH" ports = ["22"] } allow_postgres = { name = "alloydb-patroni-fw-allow-postgres" description = "Firewall rules to allow Postgres" ports = ["5432"] } allow_etcd = { name = "alloydb-patroni-fw-allow-etcd" description = "Firewall rules to allow etcd" ports = ["2379", "2380"] } allow_patroni = { name = "alloydb-patroni-fw-allow-patroni" description = "Firewall rules to allow patroni" ports = ["8008"] } allow_haproxy = { name = "alloydb-patroni-fw-allow-haproxy" description = "Firewall rules to allow haproxy" ports = ["5000", "5001", "7000"] } }) name = each.value.name network = google_compute_network.vpc.id description = each.value.description allow { protocol = "tcp" ports = each.value.ports } source_ranges = ["0.0.0.0/0"] } resource "google_storage_bucket" "bucket" { name = "alloydbomni-patroni-temporary-bucket-${random_id.bucket_suffix.hex}" location = var.region force_destroy = true } resource "google_storage_bucket_object" "objects" { for_each = fileset(path.module, "${local.upload_scripts_path}**") name = replace("${each.value}", "${local.upload_scripts_path}", "") bucket = google_storage_bucket.bucket.name source = each.value } resource "google_service_account" "service_accounts" { for_each = tomap({ etcd_instance_sa = { account_id = "etcd-instance-sa" display_name = "ETCD Instance Service Account" } alloydb_patroni_instance_sa = { account_id = "alloydb-patroni-instance-sa" display_name = "AlloydbOmni with Patroni Instance Service Account" } haproxy_instance_sa = { account_id = "haproxy-instance-sa" display_name = "HAProxy Instance Service Account" } }) account_id = each.value.account_id display_name = each.value.display_name } resource "google_storage_bucket_iam_member" "bucket_reader" { bucket = google_storage_bucket.bucket.name role = "roles/storage.objectViewer" member = "serviceAccount:${google_service_account.service_accounts["alloydb_patroni_instance_sa"].email}" } resource "local_file" "startup_scripts" { for_each = merge( { for idx in range(var.node_count) : "etcd_startup${idx + 1}" => { content = templatefile("${path.module}/scripts/etcd_startup.tftpl", { instance_index = idx + 1, etcd_current_node_ip = google_compute_address.internal_ips[local.etcd_ip_names[idx]].address, etcd_nodes_ip_list = [for idx in range(local.etcd_ip_count) : tostring(google_compute_address.internal_ips[local.etcd_ip_names[idx]].address)] }) filename = "${path.module}/scripts/etcd_startup${idx + 1}.sh" } }, { for idx in range(var.node_count) : "patroni_startup${idx + 1}" => { content = templatefile("${path.module}/scripts/patroni_startup.tftpl", { download_folder = var.download_folder, bucket_name = google_storage_bucket.bucket.name, cluster_name = var.cluster_name, instance_index = idx + 1, patroni_current_node_ip = google_compute_address.internal_ips[local.patroni_ip_names[idx]].address, patroni_nodes_ip_list = [for idx in range(local.patroni_ip_count) : tostring(google_compute_address.internal_ips[local.patroni_ip_names[idx]].address)] etcd_nodes_ip_list = [for idx in range(local.etcd_ip_count) : tostring(google_compute_address.internal_ips[local.etcd_ip_names[idx]].address)] replication_user_password = local.effective_replication_user_password, postgres_super_user = var.postgres_super_user, postgres_super_user_password = local.effective_postgres_super_user_password }) filename = "${path.module}/scripts/patroni_startup${idx + 1}.sh" } }, { "haproxy" = { content = templatefile("${path.module}/scripts/haproxy_startup.tftpl", { patroni_nodes_ip_list = [for idx in range(local.patroni_ip_count) : tostring(google_compute_address.internal_ips[local.patroni_ip_names[idx]].address)] }) filename = "${path.module}/scripts/haproxy_startup.sh" } } ) content = each.value.content filename = each.value.filename } resource "google_compute_instance" "instances" { for_each = merge( { for idx in range(var.node_count) : "etcd${idx + 1}" => { name = "alloydb-patroni-etcd${idx + 1}" type = "etcd" zone = var.zones[idx % length(var.zones)] ip_name = local.etcd_ip_names[idx] startup_script = local_file.startup_scripts["etcd_startup${idx + 1}"].content sa_email = google_service_account.service_accounts["etcd_instance_sa"].email } }, { for idx in range(var.node_count) : "patroni${idx + 1}" => { name = "alloydb-patroni${idx + 1}" type = "patroni" zone = var.zones[idx % length(var.zones)] ip_name = local.patroni_ip_names[idx] startup_script = local_file.startup_scripts["patroni_startup${idx + 1}"].content sa_email = google_service_account.service_accounts["alloydb_patroni_instance_sa"].email } }, { "haproxy" = { name = "haproxy" type = "haproxy" zone = var.zones[0] ip_name = null startup_script = local_file.startup_scripts["haproxy"].content sa_email = google_service_account.service_accounts["haproxy_instance_sa"].email } } ) name = each.value.name machine_type = local.machine_type zone = each.value.zone boot_disk { initialize_params { size = 20 image = local.image } } network_interface { network = google_compute_network.vpc.id subnetwork = google_compute_subnetwork.subnet.id network_ip = each.value.type == "haproxy" ? null : google_compute_address.internal_ips[each.value.ip_name].address access_config {} } service_account { email = each.value.sa_email scopes = ["https://www.googleapis.com/auth/cloud-platform"] } metadata_startup_script = each.value.startup_script depends_on = [google_storage_bucket.bucket] }