From b0b992588b309c238b39365e833192ac01f9c4e5 Mon Sep 17 00:00:00 2001 From: Mrugesh Mohapatra Date: Sun, 27 Oct 2024 22:29:30 +0530 Subject: [PATCH 1/2] fix(ahoyworld): droplet configs, add VMs --- .../cloud-init--userdata.yml.tftpl | 41 +++++++++++++++ .../stg-cluster-ahoyworld/droplets-01-pxy.tf | 41 +++++++++++++++ .../stg-cluster-ahoyworld/droplets-03-clt.tf | 42 +++++++++++++++ .../stg-cluster-ahoyworld/droplets-04-api.tf | 41 +++++++++++++++ .../stg-cluster-ahoyworld/droplets-05-nws.tf | 42 +++++++++++++++ .../stg-cluster-ahoyworld/droplets-06-jms.tf | 41 +++++++++++++++ terraform/stg-cluster-ahoyworld/main.tf | 52 ++++++++----------- terraform/stg-cluster-ahoyworld/network.tf | 40 ++++++++++++++ terraform/stg-cluster-ahoyworld/project.tf | 15 ++++++ terraform/stg-cluster-ahoyworld/variables.tf | 12 +++++ 10 files changed, 337 insertions(+), 30 deletions(-) create mode 100644 terraform/stg-cluster-ahoyworld/cloud-init--userdata.yml.tftpl create mode 100644 terraform/stg-cluster-ahoyworld/droplets-01-pxy.tf create mode 100644 terraform/stg-cluster-ahoyworld/droplets-03-clt.tf create mode 100644 terraform/stg-cluster-ahoyworld/droplets-04-api.tf create mode 100644 terraform/stg-cluster-ahoyworld/droplets-05-nws.tf create mode 100644 terraform/stg-cluster-ahoyworld/droplets-06-jms.tf create mode 100644 terraform/stg-cluster-ahoyworld/project.tf diff --git a/terraform/stg-cluster-ahoyworld/cloud-init--userdata.yml.tftpl b/terraform/stg-cluster-ahoyworld/cloud-init--userdata.yml.tftpl new file mode 100644 index 00000000..5a892ef3 --- /dev/null +++ b/terraform/stg-cluster-ahoyworld/cloud-init--userdata.yml.tftpl @@ -0,0 +1,41 @@ +#cloud-config +merge_how: + - name: list + settings: [append] + - name: dict + settings: [no_replace, recurse_list] +disable_root: true +ssh_pwauth: false +allow_public_ssh_keys: true + +users: + - name: freecodecamp + groups: + - sudo + - docker + shell: /bin/bash + sudo: "ALL=(ALL) NOPASSWD:ALL" + ssh_import_id: + - gh:camperbot + - gh:raisedadead + +preserve_hostname: true +fqdn: ${tf_fqdn} +hostname: ${tf_hostname} +prefer_fqdn_over_hostname: true +create_hostname_file: true + +network: + version: 2 + ethernets: + eth0: + dhcp4: true + nameservers: + addresses: + - 1.1.1.1 + - 1.0.0.1 + +runcmd: + - shutdown -r +2 'Rebooting to apply hostname change in 2 min.' + +final_message: "Setup complete" diff --git a/terraform/stg-cluster-ahoyworld/droplets-01-pxy.tf b/terraform/stg-cluster-ahoyworld/droplets-01-pxy.tf new file mode 100644 index 00000000..9d389076 --- /dev/null +++ b/terraform/stg-cluster-ahoyworld/droplets-01-pxy.tf @@ -0,0 +1,41 @@ +resource "digitalocean_droplet" "stg_ahoyworld_pxy" { + count = local.pxy_node_count + name = "stg-vm-ahoyworld-pxy-${count.index + 1}" + tags = ["stg", "ahoyworld", "pxy", "stg_ahoyworld_pxy"] + + image = data.hcp_packer_artifact.do_ubuntu.external_identifier + size = "s-2vcpu-4gb" + region = "nyc3" + vpc_uuid = digitalocean_vpc.stg_vpc.id + + ssh_keys = [for ssh_key in data.digitalocean_ssh_key.stg_ssh_keys : ssh_key.id] + + user_data = templatefile("${path.root}/cloud-init--userdata.yml.tftpl", { + tf_fqdn = "pxy-${count.index + 1}.ahoyworld.stg.${local.zone}" + tf_hostname = "pxy-stg-${count.index + 1}" + }) +} + +resource "cloudflare_record" "stg_ahoyworld_pxy_dns__public" { + count = local.pxy_node_count + + zone_id = data.cloudflare_zone.cf_zone.id + type = "A" + proxied = false + ttl = 120 + + name = "pxy-${count.index + 1}.ahoyworld.stg.${var.network_subdomain}" + content = digitalocean_droplet.stg_ahoyworld_pxy[count.index].ipv4_address +} + +resource "cloudflare_record" "stg_ahoyworld_pxy_dns__private" { + count = local.pxy_node_count + + zone_id = data.cloudflare_zone.cf_zone.id + type = "A" + proxied = false + ttl = 120 + + name = "pxy-${count.index + 1}.ahoyworld.stg" + content = digitalocean_droplet.stg_ahoyworld_pxy[count.index].ipv4_address_private +} diff --git a/terraform/stg-cluster-ahoyworld/droplets-03-clt.tf b/terraform/stg-cluster-ahoyworld/droplets-03-clt.tf new file mode 100644 index 00000000..e3d988af --- /dev/null +++ b/terraform/stg-cluster-ahoyworld/droplets-03-clt.tf @@ -0,0 +1,42 @@ +resource "digitalocean_droplet" "stg_ahoyworld_clt" { + for_each = { for i in local.clt_instances : i.instance => i } + + name = "stg-vm-ahoyworld-clt-${each.value.instance}" + tags = ["stg", "ahoyworld", "clt", "stg_ahoyworld_clt", "${each.value.name}"] + + image = data.hcp_packer_artifact.do_ubuntu.external_identifier + size = "s-2vcpu-4gb" + region = "nyc3" + vpc_uuid = digitalocean_vpc.stg_vpc.id + + ssh_keys = [for ssh_key in data.digitalocean_ssh_key.stg_ssh_keys : ssh_key.id] + + user_data = templatefile("${path.root}/cloud-init--userdata.yml.tftpl", { + tf_fqdn = "clt-${each.value.instance}.ahoyworld.stg.${local.zone}" + tf_hostname = "clt-stg-${each.value.instance}" + }) +} + +resource "cloudflare_record" "stg_ahoyworld_clt_dns__public" { + for_each = { for i in local.clt_instances : i.instance => i } + + zone_id = data.cloudflare_zone.cf_zone.id + type = "A" + proxied = false + ttl = 120 + + name = "clt-${each.value.instance}.ahoyworld.stg.${var.network_subdomain}" + content = digitalocean_droplet.stg_ahoyworld_clt[each.key].ipv4_address +} + +resource "cloudflare_record" "stg_ahoyworld_clt_dns__private" { + for_each = { for i in local.clt_instances : i.instance => i } + + zone_id = data.cloudflare_zone.cf_zone.id + type = "A" + proxied = false + ttl = 120 + + name = "clt-${each.value.instance}.ahoyworld.stg" + content = digitalocean_droplet.stg_ahoyworld_clt[each.key].ipv4_address_private +} diff --git a/terraform/stg-cluster-ahoyworld/droplets-04-api.tf b/terraform/stg-cluster-ahoyworld/droplets-04-api.tf new file mode 100644 index 00000000..9222c303 --- /dev/null +++ b/terraform/stg-cluster-ahoyworld/droplets-04-api.tf @@ -0,0 +1,41 @@ +resource "digitalocean_droplet" "stg_ahoyworld_api" { + count = local.api_node_count + name = "stg-vm-ahoyworld-api-${count.index + 1}" + tags = ["stg", "ahoyworld", "api", "stg_ahoyworld_api"] + + image = data.hcp_packer_artifact.do_ubuntu.external_identifier + size = "s-2vcpu-4gb" + region = "nyc3" + vpc_uuid = digitalocean_vpc.stg_vpc.id + + ssh_keys = [for ssh_key in data.digitalocean_ssh_key.stg_ssh_keys : ssh_key.id] + + user_data = templatefile("${path.root}/cloud-init--userdata.yml.tftpl", { + tf_fqdn = "api-${count.index + 1}.ahoyworld.stg.${local.zone}" + tf_hostname = "api-stg-${count.index + 1}" + }) +} + +resource "cloudflare_record" "stg_ahoyworld_api_dns__public" { + count = local.api_node_count + + zone_id = data.cloudflare_zone.cf_zone.id + type = "A" + proxied = false + ttl = 120 + + name = "api-${count.index + 1}.ahoyworld.stg.${var.network_subdomain}" + content = digitalocean_droplet.stg_ahoyworld_api[count.index].ipv4_address +} + +resource "cloudflare_record" "stg_ahoyworld_api_dns__private" { + count = local.api_node_count + + zone_id = data.cloudflare_zone.cf_zone.id + type = "A" + proxied = false + ttl = 120 + + name = "api-${count.index + 1}.ahoyworld.stg" + content = digitalocean_droplet.stg_ahoyworld_api[count.index].ipv4_address_private +} diff --git a/terraform/stg-cluster-ahoyworld/droplets-05-nws.tf b/terraform/stg-cluster-ahoyworld/droplets-05-nws.tf new file mode 100644 index 00000000..1c59d8d3 --- /dev/null +++ b/terraform/stg-cluster-ahoyworld/droplets-05-nws.tf @@ -0,0 +1,42 @@ +resource "digitalocean_droplet" "stg_ahoyworld_nws" { + for_each = { for i in local.nws_instances : i.name => i } + + name = "stg-vm-ahoyworld-nws-${each.value.name}" + tags = ["stg", "ahoyworld", "nws", "stg_ahoyworld_nws", "${each.value.name}"] + + image = data.hcp_packer_artifact.do_ubuntu.external_identifier + size = "s-2vcpu-4gb" + region = "nyc3" + vpc_uuid = digitalocean_vpc.stg_vpc.id + + ssh_keys = [for ssh_key in data.digitalocean_ssh_key.stg_ssh_keys : ssh_key.id] + + user_data = templatefile("${path.root}/cloud-init--userdata.yml.tftpl", { + tf_fqdn = "nws-${each.value.name}.ahoyworld.stg.${local.zone}" + tf_hostname = "nws-stg-${each.value.name}" + }) +} + +resource "cloudflare_record" "stg_ahoyworld_nws_dns__public" { + for_each = { for i in local.nws_instances : i.name => i } + + zone_id = data.cloudflare_zone.cf_zone.id + type = "A" + proxied = false + ttl = 120 + + name = "nws-${each.value.name}.ahoyworld.stg.${var.network_subdomain}" + content = digitalocean_droplet.stg_ahoyworld_nws[each.key].ipv4_address +} + +resource "cloudflare_record" "stg_ahoyworld_nws_dns__private" { + for_each = { for i in local.nws_instances : i.name => i } + + zone_id = data.cloudflare_zone.cf_zone.id + type = "A" + proxied = false + ttl = 120 + + name = "nws-${each.value.name}.ahoyworld.stg" + content = digitalocean_droplet.stg_ahoyworld_nws[each.key].ipv4_address_private +} diff --git a/terraform/stg-cluster-ahoyworld/droplets-06-jms.tf b/terraform/stg-cluster-ahoyworld/droplets-06-jms.tf new file mode 100644 index 00000000..8777c096 --- /dev/null +++ b/terraform/stg-cluster-ahoyworld/droplets-06-jms.tf @@ -0,0 +1,41 @@ +resource "digitalocean_droplet" "stg_ahoyworld_jms" { + count = local.jms_node_count + name = "stg-vm-ahoyworld-jms-${count.index + 1}" + tags = ["stg", "ahoyworld", "jms", "stg_ahoyworld_jms"] + + image = data.hcp_packer_artifact.do_ubuntu.external_identifier + size = "s-2vcpu-4gb" + region = "nyc3" + vpc_uuid = digitalocean_vpc.stg_vpc.id + + ssh_keys = [for ssh_key in data.digitalocean_ssh_key.stg_ssh_keys : ssh_key.id] + + user_data = templatefile("${path.root}/cloud-init--userdata.yml.tftpl", { + tf_fqdn = "jms-${count.index + 1}.ahoyworld.stg.${local.zone}" + tf_hostname = "jms-stg-${count.index + 1}" + }) +} + +resource "cloudflare_record" "stg_ahoyworld_jms_dns__public" { + count = local.jms_node_count + + zone_id = data.cloudflare_zone.cf_zone.id + type = "A" + proxied = false + ttl = 120 + + name = "jms-${count.index + 1}.ahoyworld.stg.${var.network_subdomain}" + content = digitalocean_droplet.stg_ahoyworld_jms[count.index].ipv4_address +} + +resource "cloudflare_record" "stg_ahoyworld_jms_dns__private" { + count = local.jms_node_count + + zone_id = data.cloudflare_zone.cf_zone.id + type = "A" + proxied = false + ttl = 120 + + name = "jms-${count.index + 1}.ahoyworld.stg" + content = digitalocean_droplet.stg_ahoyworld_jms[count.index].ipv4_address_private +} diff --git a/terraform/stg-cluster-ahoyworld/main.tf b/terraform/stg-cluster-ahoyworld/main.tf index 5386b541..44e30b31 100644 --- a/terraform/stg-cluster-ahoyworld/main.tf +++ b/terraform/stg-cluster-ahoyworld/main.tf @@ -22,6 +22,15 @@ data "cloudflare_zone" "cf_zone" { # } # } +locals { + ssh_accounts = ["ssh-service-camperbot-ed25519", "ssh-service-terraform-ed25519"] +} + +data "digitalocean_ssh_key" "stg_ssh_keys" { + for_each = toset(local.ssh_accounts) + name = each.value +} + locals { pxy_node_count = 3 # number of proxy nodes api_node_count = 3 # number of api nodes @@ -29,47 +38,30 @@ locals { jms_node_count = 3 # number of JAMStack nodes } -locals { - ipam_block_pxy = 10 # 10.0.0.11, 10.0.0.12, ... - ipam_block_api = 20 # 10.0.0.21, 10.0.0.22, ... - ipam_block_clt = 40 # 10.0.0.41, 10.0.0.42, ... - ipam_block_nws = 100 # 10.0.0.100, 10.0.0.102, ... - ipam_block_jms = 120 # 10.0.0.120, 10.0.0.121, ... -} - -// When removing an item, DO NOT change the IPAM number. locals { nws_instances = { - # eng = { name = "eng", ipam_id = 0 }, # 10.0.0.100 - chn = { name = "chn", ipam_id = 1 }, # 10.0.0.101 - esp = { name = "esp", ipam_id = 2 }, # ... - ita = { name = "ita", ipam_id = 3 }, - jpn = { name = "jpn", ipam_id = 4 }, - kor = { name = "kor", ipam_id = 5 }, - por = { name = "por", ipam_id = 6 }, - ukr = { name = "ukr", ipam_id = 7 }, - # ger = { name = "ger", ipam_id = 8 } + # eng = { name = "eng" } + # i18n = { name = "i18n" } } clt_config_meta = { - eng = { name = "eng", ipam_id = 0, node_count = local.clt_node_count }, # 10.0.0.40, 10.0.0.41, ... - chn = { name = "chn", ipam_id = 5, node_count = local.clt_node_count }, # 10.0.0.45, 10.0.0.46, ... - esp = { name = "esp", ipam_id = 10, node_count = local.clt_node_count }, # 10.0.0.50, 10.0.0.51, ... - ita = { name = "ita", ipam_id = 15, node_count = local.clt_node_count }, # 10.0.0.55, 10.0.0.56, ... - jpn = { name = "jpn", ipam_id = 20, node_count = local.clt_node_count }, # 10.0.0.60, 10.0.0.61, ... - # kor = { name = "kor", ipam_id = 6, node_count = local.clt_node_count }, - por = { name = "por", ipam_id = 25, node_count = local.clt_node_count }, # 10.0.0.65, 10.0.0.66, ... - ukr = { name = "ukr", ipam_id = 30, node_count = local.clt_node_count }, # 10.0.0.70, 10.0.0.71, ... - ger = { name = "ger", ipam_id = 35, node_count = local.clt_node_count }, # 10.0.0.75, 10.0.0.76, ... - cnt = { name = "cnt", ipam_id = 40, node_count = local.clt_node_count } # 10.0.0.80, 10.0.0.81, ... - swa = { name = "swa", ipam_id = 45, node_count = local.clt_node_count } # 10.0.0.85, 10.0.0.86, ... + eng = { name = "eng", node_count = local.clt_node_count }, + chn = { name = "chn", node_count = local.clt_node_count }, + esp = { name = "esp", node_count = local.clt_node_count }, + ita = { name = "ita", node_count = local.clt_node_count }, + jpn = { name = "jpn", node_count = local.clt_node_count }, + # kor = { name = "kor", node_count = local.clt_node_count }, + por = { name = "por", node_count = local.clt_node_count }, + ukr = { name = "ukr", node_count = local.clt_node_count }, + ger = { name = "ger", node_count = local.clt_node_count }, + cnt = { name = "cnt", node_count = local.clt_node_count } + swa = { name = "swa", node_count = local.clt_node_count } } clt_instances = flatten([ [for k, v in local.clt_config_meta : [ for i in range(v.node_count) : { name = v.name - ipam_id = v.ipam_id + i instance = "${k}-${i}" } ]], diff --git a/terraform/stg-cluster-ahoyworld/network.tf b/terraform/stg-cluster-ahoyworld/network.tf index 2fb4f1f2..bc7708e4 100644 --- a/terraform/stg-cluster-ahoyworld/network.tf +++ b/terraform/stg-cluster-ahoyworld/network.tf @@ -8,6 +8,13 @@ resource "digitalocean_vpc" "stg_vpc" { resource "digitalocean_firewall" "stg_fw_internal" { name = "stg-ahoyworld-fw-internal" + droplet_ids = flatten([ + [for instance in digitalocean_droplet.stg_ahoyworld_clt : instance.id], + digitalocean_droplet.stg_ahoyworld_api[*].id, + [for instance in digitalocean_droplet.stg_ahoyworld_nws : instance.id], + digitalocean_droplet.stg_ahoyworld_jms[*].id, + ]) + inbound_rule { protocol = "tcp" port_range = "22" @@ -24,11 +31,31 @@ resource "digitalocean_firewall" "stg_fw_internal" { digitalocean_vpc.stg_vpc.ip_range ] } + + outbound_rule { + protocol = "tcp" + port_range = "1-65535" + destination_addresses = [ + "0.0.0.0/0", + "::/0", + ] + } + + depends_on = [ + digitalocean_droplet.stg_ahoyworld_clt, + digitalocean_droplet.stg_ahoyworld_api, + digitalocean_droplet.stg_ahoyworld_nws, + digitalocean_droplet.stg_ahoyworld_jms, + ] } resource "digitalocean_firewall" "stg_fw_external" { name = "stg-ahoyworld-fw-external" + droplet_ids = flatten([ + digitalocean_droplet.stg_ahoyworld_pxy[*].id + ]) + inbound_rule { protocol = "tcp" port_range = "22" @@ -55,4 +82,17 @@ resource "digitalocean_firewall" "stg_fw_external" { "::/0", ] } + + outbound_rule { + protocol = "tcp" + port_range = "1-65535" + destination_addresses = [ + "0.0.0.0/0", + "::/0", + ] + } + + depends_on = [ + digitalocean_droplet.stg_ahoyworld_pxy, + ] } diff --git a/terraform/stg-cluster-ahoyworld/project.tf b/terraform/stg-cluster-ahoyworld/project.tf new file mode 100644 index 00000000..7231539b --- /dev/null +++ b/terraform/stg-cluster-ahoyworld/project.tf @@ -0,0 +1,15 @@ +resource "digitalocean_project" "stg_project" { + name = "stg-ahoyworld" + description = "AhoyWorld staging resources." + purpose = "Web Application" + environment = "Staging" +} + +resource "digitalocean_project_resources" "stg_project_resources" { + project = digitalocean_project.stg_project.id + resources = flatten([ + [for droplet in digitalocean_droplet.stg_ahoyworld_pxy : droplet.urn], + [for droplet in digitalocean_droplet.stg_ahoyworld_clt : droplet.urn], + [for droplet in digitalocean_droplet.stg_ahoyworld_api : droplet.urn] + ]) +} diff --git a/terraform/stg-cluster-ahoyworld/variables.tf b/terraform/stg-cluster-ahoyworld/variables.tf index 601a0d22..31f1138b 100644 --- a/terraform/stg-cluster-ahoyworld/variables.tf +++ b/terraform/stg-cluster-ahoyworld/variables.tf @@ -25,3 +25,15 @@ variable "cloudflare_account_id" { type = string description = "Cloudflare Account ID" } + +variable "network_subdomain" { + description = "The subdomain for the network." + type = string + sensitive = true +} + +variable "ssh_terraform_ed25519_private_key" { + type = string + description = "The private key for the terraform account." + sensitive = true +} From 302394987436665619178c048946129b64ef20ef Mon Sep 17 00:00:00 2001 From: Mrugesh Mohapatra Date: Mon, 4 Nov 2024 22:12:29 +0530 Subject: [PATCH 2/2] feat(ansible): add digitalocean inventory --- ansible/ansible.cfg | 2 +- ansible/inventory/digitalocean.yml | 12 ++++++++++++ ansible/requirements/collections.yml | 1 + 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 ansible/inventory/digitalocean.yml diff --git a/ansible/ansible.cfg b/ansible/ansible.cfg index 599ec4a9..5250d4de 100644 --- a/ansible/ansible.cfg +++ b/ansible/ansible.cfg @@ -7,7 +7,7 @@ remote_user=freecodecamp inventory = ./inventory [inventory] -enable_plugins = yaml, ini, toml, community.general.linode +enable_plugins = yaml, ini, toml, community.general.linode, community.digitalocean.digitalocean # enable_plugins = yaml, ini, toml, community.general.linode, amazon.aws.aws_ec2 cache = true cache_connection = ~/.ansible/.cache diff --git a/ansible/inventory/digitalocean.yml b/ansible/inventory/digitalocean.yml new file mode 100644 index 00000000..20bbd2e9 --- /dev/null +++ b/ansible/inventory/digitalocean.yml @@ -0,0 +1,12 @@ +--- +plugin: community.digitalocean.digitalocean +api_token: "{{ lookup('ansible.builtin.env', 'DO_API_TOKEN') }}" +attributes: + - name + - tags + - networks +keyed_groups: + - key: do_tags +leading_separator: no +compose: + ansible_host: do_networks.v4 | selectattr('type','eq','public') | map(attribute='ip_address') | first diff --git a/ansible/requirements/collections.yml b/ansible/requirements/collections.yml index d4e3ebdb..78b52051 100644 --- a/ansible/requirements/collections.yml +++ b/ansible/requirements/collections.yml @@ -3,5 +3,6 @@ collections: - community.general - community.docker # - community.aws + - community.digitalocean - name: grafana.grafana version: 5.3.0