anthos-bm-gcp-terraform/main.tf (392 lines of code) (raw):

/** * Copyright 2021 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. */ locals { init_script_logfile_name = "init.log" vm_name_template = "%s-abm-%s%d" gpu_enabled = var.gpu.type != "" admin_vm_name = [format(local.vm_name_template, var.abm_cluster_id, "ws", 0)] vm_names = concat(local.admin_vm_name, local.controlplane_vm_names, local.worker_vm_names) controlplane_vm_names = [for i in range(var.instance_count.controlplane) : format(local.vm_name_template, var.abm_cluster_id, "cp", i + 1)] worker_vm_names = [for i in range(var.instance_count.worker) : format(local.vm_name_template, var.abm_cluster_id, "w", i + 1)] controlplane_vxlan_ips = [for name in local.controlplane_vm_names : local.vm_vxlan_ip[name]] worker_vxlan_ips = [for name in local.worker_vm_names : local.vm_vxlan_ip[name]] controlplane_internal_ips = [for vm in module.controlplane_vm_hosts.vm_info : vm.internalIp] worker_internal_ips = [for vm in module.worker_vm_hosts.vm_info : vm.internalIp] admin_vm_hostnames = [for vm in module.admin_vm_hosts.vm_info : vm.hostname] controlplane_vm_hostnames = [for vm in module.controlplane_vm_hosts.vm_info : vm.hostname] vm_vxlan_ip = { for idx, vmName in local.vm_names : vmName => format("10.200.0.%d", idx + 2) } vmHostnameToVmName = { for vmName in local.vm_names : "${vmName}-001" => vmName } public_key_file_path_template = "${var.resources_path}/.temp/%s/ssh-key.pub" private_key_file_path_template = "${var.resources_path}/.temp/%s/ssh-key.priv" init_script_vars_file_path_template = "${var.resources_path}/.temp/%s/init.vars" cluster_yaml_file = "${var.resources_path}/.temp/.${var.abm_cluster_id}.yaml" cluster_yaml_template_file = "${var.resources_path}/templates/anthos_gce_cluster.tpl" nfs_yaml_template_file = "${var.resources_path}/templates/nfs-csi.tpl" cluster_yaml_manuallb_template_file = "${var.resources_path}/templates/manuallb_cluster.tpl" init_script_vars_file = "${var.resources_path}/templates/init.vars.tpl" init_script = "${var.resources_path}/init_vm.sh" init_check_script = "${var.resources_path}/run_initialization_checks.sh" install_abm_script = "${var.resources_path}/install_abm.sh" login_script = "${var.resources_path}/login.sh" firewall_rule_name = "${var.abm_cluster_id}-allow-lb-traffic-rule" nfs_yaml_file = "${var.resources_path}/.temp/.nfs-csi.yaml" terraform_sa_path = "/home/${var.username}/terraform-sa.json" firewall_rule_ports = [6444, 443] firewall_rule_port_str = join(",", [for port in local.firewall_rule_ports : "tcp:${port}"]) vm_hostnames_str = join("|", local.vm_hostnames) controlplan_vm_hostnames_str = join("|", local.controlplane_vm_hostnames) admin_vm_public_ip = [for vm in module.admin_vm_hosts.vm_info : vm.externalIp][0] vm_hostnames = concat( local.admin_vm_hostnames, local.controlplane_vm_hostnames, [for vm in module.worker_vm_hosts.vm_info : vm.hostname] ) vm_internal_ips = join("|", concat( [for vm in module.admin_vm_hosts.vm_info : vm.internalIp], local.controlplane_internal_ips, local.worker_internal_ips) ) publicIps = merge( { for vm in module.admin_vm_hosts.vm_info : vm.hostname => vm.externalIp }, { for vm in module.controlplane_vm_hosts.vm_info : vm.hostname => vm.externalIp }, { for vm in module.worker_vm_hosts.vm_info : vm.hostname => vm.externalIp } ) } module "enable_google_apis_primary" { source = "terraform-google-modules/project-factory/google//modules/project_services" version = "~> 18.0" project_id = var.project_id activate_apis = var.primary_apis disable_services_on_destroy = false } module "enable_google_apis_secondary" { source = "terraform-google-modules/project-factory/google//modules/project_services" version = "~> 18.0" # fetched from previous module to explicitely express dependency project_id = module.enable_google_apis_primary.project_id depends_on = [module.enable_google_apis_primary] activate_apis = var.secondary_apis disable_services_on_destroy = false } module "create_service_accounts" { source = "terraform-google-modules/service-accounts/google" version = "~> 4.0" # fetched from previous module to explicitely express dependency project_id = module.enable_google_apis_secondary.project_id depends_on = [ module.enable_google_apis_primary, module.enable_google_apis_secondary ] names = [var.anthos_service_account_name] generate_keys = true project_roles = [ "${var.project_id}=>roles/gkehub.connect", "${var.project_id}=>roles/gkehub.admin", "${var.project_id}=>roles/logging.logWriter", "${var.project_id}=>roles/monitoring.metricWriter", "${var.project_id}=>roles/monitoring.dashboardEditor", "${var.project_id}=>roles/stackdriver.resourceMetadata.writer", "${var.project_id}=>roles/opsconfigmonitoring.resourceMetadata.writer", ] } module "instance_template" { source = "terraform-google-modules/vm/google//modules/instance_template" version = "~> 13.0" depends_on = [ module.enable_google_apis_primary, module.enable_google_apis_secondary ] # fetched from previous module to explicitely express dependency project_id = module.enable_google_apis_secondary.project_id region = var.region # --zone=${ZONE} source_image = var.image # --image=ubuntu-2004-focal-v20210429 source_image_family = var.image_family # --image-family=ubuntu-2004-lts source_image_project = var.image_project # --image-project=ubuntu-os-cloud machine_type = var.machine_type # --machine-type $MACHINE_TYPE disk_size_gb = var.boot_disk_size # --boot-disk-size 200G disk_type = var.boot_disk_type # --boot-disk-type pd-ssd network = var.network # --network default tags = var.tags # --tags http-server,https-server min_cpu_platform = var.min_cpu_platform # --min-cpu-platform "Intel Haswell" can_ip_forward = true # --can-ip-forward # Disable oslogin explicitly since we rely on metadata based ssh-key (issues/70). metadata = { enable-oslogin = "false" } service_account = null create_service_account = false } module "instance_template_worker" { source = "terraform-google-modules/vm/google//modules/instance_template" version = "~> 13.0" depends_on = [ module.enable_google_apis_primary, module.enable_google_apis_secondary ] # fetched from previous module to explicitely express dependency project_id = module.enable_google_apis_secondary.project_id region = var.region # --zone=${ZONE} source_image = var.image # --image=ubuntu-2004-focal-v20210429 source_image_family = var.image_family # --image-family=ubuntu-2004-lts source_image_project = var.image_project # --image-project=ubuntu-os-cloud machine_type = var.machine_type # --machine-type $MACHINE_TYPE disk_size_gb = var.boot_disk_size # --boot-disk-size 200G disk_type = var.boot_disk_type # --boot-disk-type pd-ssd network = var.network # --network default tags = var.tags # --tags http-server,https-server min_cpu_platform = var.min_cpu_platform # --min-cpu-platform "Intel Haswell" can_ip_forward = true # --can-ip-forward enable_nested_virtualization = var.enable_nested_virtualization # --enable-nested-virtualization # Disable oslogin explicitly since we rely on metadata based ssh-key (issues/70). metadata = { enable-oslogin = "false" } service_account = null gpu = !local.gpu_enabled ? null : { type = var.gpu.type count = var.gpu.count } } module "admin_vm_hosts" { source = "./modules/vm" depends_on = [ module.enable_google_apis_primary, module.enable_google_apis_secondary ] region = var.region zone = var.zone network = var.network vm_names = local.admin_vm_name instance_template = module.instance_template.self_link } module "controlplane_vm_hosts" { source = "./modules/vm" depends_on = [ module.enable_google_apis_primary, module.enable_google_apis_secondary ] region = var.region zone = var.zone network = var.network vm_names = local.controlplane_vm_names instance_template = module.instance_template.self_link } module "worker_vm_hosts" { source = "./modules/vm" depends_on = [ module.enable_google_apis_primary, module.enable_google_apis_secondary ] region = var.region zone = var.zone network = var.network vm_names = local.worker_vm_names instance_template = module.instance_template_worker.self_link } module "configure_controlplane_lb" { source = "./modules/loadbalancer" count = var.mode == "manuallb" ? 1 : 0 depends_on = [ module.admin_vm_hosts, module.controlplane_vm_hosts, module.worker_vm_hosts ] type = "controlplanelb" project = var.project_id zone = var.zone name_prefix = "${var.abm_cluster_id}-cp" ip_name = "${var.abm_cluster_id}-cp-public-ip" health_check_path = "/readyz" health_check_port = 6444 backend_protocol = "TCP" forwarding_rule_ports = [443] lb_endpoint_instances = [ for vm in module.controlplane_vm_hosts.vm_info : { name = vm.hostname ip = vm.internalIp port = 6444 } ] } module "configure_ingress_lb" { source = "./modules/loadbalancer" count = var.mode == "manuallb" ? 1 : 0 depends_on = [ module.admin_vm_hosts, module.controlplane_vm_hosts, module.worker_vm_hosts ] type = "ingresslb" project = var.project_id zone = var.zone name_prefix = "${var.abm_cluster_id}-ing" ip_name = "${var.abm_cluster_id}-ing-public-ip" backend_protocol = "HTTP" forwarding_rule_ports = [80] } resource "google_compute_firewall" "lb-firewall-rule" { name = local.firewall_rule_name depends_on = [ module.enable_google_apis_primary, module.enable_google_apis_secondary ] network = var.network target_tags = var.tags source_ranges = ["0.0.0.0/0"] direction = "INGRESS" allow { protocol = "tcp" ports = local.firewall_rule_ports } } // Generate the Anthos bare metal cluster yaml file using the template for // bundled load balancer setup. This resource is created only if the mode is // NOT 'manuallb' (i.e. setup or install) resource "local_file" "cluster_yaml_bundledlb" { depends_on = [ module.controlplane_vm_hosts, module.worker_vm_hosts ] count = var.mode == "manuallb" ? 0 : 1 filename = local.cluster_yaml_file content = templatefile(local.cluster_yaml_template_file, { clusterId = var.abm_cluster_id, projectId = var.project_id, gcp_accounts = var.gcp_login_accounts, controlPlaneIps = local.controlplane_vxlan_ips, workerNodeIps = local.worker_vxlan_ips abmVersion = var.abm_version }) } // Generate the Anthos bare metal cluster yaml file using the template for // manual load balancer setup. This resource is created only if the mode is // 'manuallb' resource "local_file" "cluster_yaml_manuallb" { depends_on = [ module.controlplane_vm_hosts, module.worker_vm_hosts ] count = var.mode == "manuallb" ? 1 : 0 filename = local.cluster_yaml_file content = templatefile(local.cluster_yaml_manuallb_template_file, { clusterId = var.abm_cluster_id, projectId = var.project_id, gcp_accounts = var.gcp_login_accounts, controlPlaneIps = local.controlplane_internal_ips, workerNodeIps = local.worker_internal_ips, controlPlaneVIP = module.configure_controlplane_lb[0].public_ip, ingressVIP = module.configure_ingress_lb[0].public_ip abmVersion = var.abm_version }) } resource "google_filestore_instance" "cluster-abm-nfs" { count = var.nfs_server ? 1 : 0 depends_on = [ module.enable_google_apis_primary, module.enable_google_apis_secondary ] name = "${substr(var.abm_cluster_id, 0, min(12, length(var.abm_cluster_id)))}-nfs" location = var.zone tier = "STANDARD" file_shares { capacity_gb = 1024 name = "${var.abm_cluster_id}_fs" } networks { network = var.network modes = ["MODE_IPV4"] } } // Generate the Anthos bare metal nfs yaml file using the template. // This file is only used when the `nfs_server` variable is set to true. resource "local_file" "nfs_yaml" { depends_on = [ module.controlplane_vm_hosts, module.worker_vm_hosts ] filename = local.nfs_yaml_file content = templatefile(local.nfs_yaml_template_file, { nfs_server = var.nfs_server ? google_filestore_instance.cluster-abm-nfs[0].networks[0].ip_addresses[0] : "" nfs_share = var.nfs_server ? google_filestore_instance.cluster-abm-nfs[0].file_shares[0].name : "" }) } resource "local_file" "init_args_file" { depends_on = [ module.controlplane_vm_hosts, module.worker_vm_hosts ] for_each = toset(local.vm_hostnames) filename = format(local.init_script_vars_file_path_template, each.value) content = templatefile(local.init_script_vars_file, { zone = var.zone, clusterId = var.abm_cluster_id, isAdminVm = contains(local.admin_vm_hostnames, each.value), vxLanIp = local.vm_vxlan_ip[local.vmHostnameToVmName[each.value]], serviceAccount = var.anthos_service_account_name, terraformSAccount = local.terraform_sa_path, hostnames = local.vm_hostnames_str, controlplaneVms = local.controlplan_vm_hostnames_str, vmInternalIps = local.vm_internal_ips, logFile = local.init_script_logfile_name firewallRuleName = local.firewall_rule_name firewallPorts = local.firewall_rule_port_str installMode = var.mode ingressNeg = var.mode == "manuallb" ? module.configure_ingress_lb[0].neg_name : "" ingressLbIp = var.mode == "manuallb" ? module.configure_ingress_lb[0].public_ip : "" nfsServer = var.nfs_server abmVersion = var.abm_version }) } module "init_hosts" { source = "./modules/init" module_depends_on = [ local_file.init_args_file[each.key], local_file.nfs_yaml ] for_each = toset(local.vm_hostnames) project_id = var.project_id zone = var.zone hostname = each.value username = var.username credentials_file = var.credentials_file resources_path = var.resources_path publicIp = local.publicIps[each.value] init_script = local.init_script init_check_script = local.init_check_script install_abm_script = local.install_abm_script login_script = local.login_script init_logs = local.init_script_logfile_name pub_key_path_template = local.public_key_file_path_template priv_key_path_template = local.private_key_file_path_template init_vars_file = format(local.init_script_vars_file_path_template, each.value) cluster_yaml_path = local.cluster_yaml_file nfs_yaml_path = local.nfs_yaml_file terraform_sa_path = local.terraform_sa_path } module "install_abm" { source = "./modules/install" depends_on = [ module.init_hosts, module.configure_ingress_lb, module.configure_controlplane_lb ] count = var.mode == "install" || var.mode == "manuallb" ? 1 : 0 username = var.username publicIp = local.publicIps[local.admin_vm_hostnames[0]] ssh_private_key_file = format(local.private_key_file_path_template, local.admin_vm_hostnames[0]) } module "gke_hub_membership" { source = "terraform-google-modules/gcloud/google" version = "~> 3.1" platform = "linux" create_cmd_entrypoint = "echo" create_cmd_body = "GKE hub membership is created by bmctl create cluster" destroy_cmd_body = "container hub memberships delete --quiet --project ${var.project_id} ${var.abm_cluster_id} --verbosity=none || true" }