diff --git a/modules/compute/vultr/1.0/facets.yaml b/modules/compute/vultr/1.0/facets.yaml new file mode 100644 index 00000000..41d9f329 --- /dev/null +++ b/modules/compute/vultr/1.0/facets.yaml @@ -0,0 +1,174 @@ +intent: compute +flavor: vultr +version: "1.0" +clouds: + - kubernetes +description: Vultr cloud compute instances (count-scaled VMs) with SSH keys, startup script, and optional firewall +intentDetails: + type: Cloud & Infrastructure + description: Vultr cloud compute virtual machines + displayName: Compute + iconUrl: https://raw.githubusercontent.com/Facets-cloud/facets-modules-redesign/main/icons/compute_engine.svg +spec: + title: Vultr Compute Instances + description: One or more identical Vultr cloud compute instances + type: object + x-ui-order: + - region + - sizing + - image + - networking + - access + - firewall + properties: + region: + type: string + title: Region + description: Vultr region; defaults to the cloud account region when empty + x-ui-overrides-only: true + sizing: + type: object + title: Sizing + x-ui-order: + - plan + - count + properties: + plan: + type: string + title: Plan + description: "Vultr compute plan slug (e.g. vc2-1c-1gb, vc2-1c-2gb, vhf-1c-1gb). List via the Vultr API (GET /v2/plans)." + default: vc2-1c-1gb + x-ui-placeholder: vc2-1c-1gb + count: + type: integer + title: Instance Count + description: Number of identical instances to provision (horizontal scaling) + minimum: 1 + maximum: 20 + default: 2 + required: + - plan + - count + image: + type: object + title: Image + properties: + os_id: + type: integer + title: OS ID + description: "Vultr OS ID (e.g. 1743 = Ubuntu 22.04 x64, 2136 = Ubuntu 24.04 x64). List via GET /v2/os." + default: 1743 + required: + - os_id + networking: + type: object + title: Networking + properties: + attach_vpc: + type: boolean + title: Attach to VPC + description: Attach instances to the wired VPC (requires the network input) + default: true + enable_ipv6: + type: boolean + title: Enable IPv6 + default: false + access: + type: object + title: Access + properties: + ssh_public_keys: + type: array + title: SSH Public Keys + description: Public keys installed on the instances + default: [] + items: + type: string + startup_script: + type: string + title: Startup Script + description: Shell script run on first boot + x-ui-widget: textarea + default: "" + firewall: + type: object + title: Firewall + x-ui-toggle: true + properties: + manage: + type: boolean + title: Manage Firewall + description: Create a managed firewall group for these instances + default: false + open_ports: + type: object + title: Open Ports + description: Inbound rules; key is a unique rule id + default: {} + patternProperties: + ".*": + type: object + properties: + port: + type: string + title: Port + protocol: + type: string + title: Protocol + enum: + - tcp + - udp + default: tcp + source: + type: string + title: Source CIDR + default: 0.0.0.0/0 + required: + - port + required: + - sizing + - image +inputs: + vultr_cloud_account: + type: "@facets/vultr_cloud_account" + optional: false + displayName: Vultr Cloud Account + description: Vultr provider configuration and default region + providers: + - vultr + network: + type: "@facets/vultr-vpc-details" + optional: true + displayName: VPC Network + description: Optional VPC to attach instances to + providers: [] +outputs: + default: + type: "@facets/vultr-instance-details" + title: Vultr Compute Instances +sample: + kind: compute + flavor: vultr + version: "1.0" + disabled: true + spec: + sizing: + plan: vc2-1c-1gb + count: 2 + image: + os_id: 1743 + networking: + attach_vpc: true + enable_ipv6: false + access: + ssh_public_keys: [] + startup_script: "" + firewall: + manage: false + open_ports: {} +iac: + validated_files: + - main.tf + - variables.tf + - outputs.tf + - versions.tf diff --git a/modules/compute/vultr/1.0/main.tf b/modules/compute/vultr/1.0/main.tf new file mode 100644 index 00000000..68720ac3 --- /dev/null +++ b/modules/compute/vultr/1.0/main.tf @@ -0,0 +1,59 @@ +# Vultr Compute Instances Module +# Provisions one or more identical Vultr cloud compute instances (count-scaled), +# with optional SSH keys, startup script, VPC attach, and managed firewall. + +module "name" { + source = "github.com/Facets-cloud/facets-utility-modules//name" + environment = var.environment + limit = 32 + resource_name = var.instance_name + resource_type = "compute" +} + +locals { + region = coalesce(try(var.instance.spec.region, null), var.inputs.vultr_cloud_account.attributes.region) + vpc_ids = (var.instance.spec.networking.attach_vpc && var.inputs.network != null) ? [var.inputs.network.attributes.vpc_id] : null + has_script = length(trimspace(var.instance.spec.access.startup_script)) > 0 +} + +resource "vultr_ssh_key" "this" { + for_each = { for idx, k in var.instance.spec.access.ssh_public_keys : tostring(idx) => k } + name = "${module.name.name}-${each.key}" + ssh_key = each.value +} + +resource "vultr_startup_script" "this" { + count = local.has_script ? 1 : 0 + name = module.name.name + script = base64encode(var.instance.spec.access.startup_script) +} + +resource "vultr_firewall_group" "this" { + count = var.instance.spec.firewall.manage ? 1 : 0 + description = module.name.name +} + +resource "vultr_firewall_rule" "this" { + for_each = var.instance.spec.firewall.manage ? var.instance.spec.firewall.open_ports : {} + firewall_group_id = vultr_firewall_group.this[0].id + protocol = each.value.protocol + ip_type = "v4" + subnet = split("/", each.value.source)[0] + subnet_size = tonumber(split("/", each.value.source)[1]) + port = each.value.port +} + +resource "vultr_instance" "this" { + count = var.instance.spec.sizing.count + region = local.region + plan = var.instance.spec.sizing.plan + os_id = var.instance.spec.image.os_id + label = "${module.name.name}-${count.index}" + hostname = "${module.name.name}-${count.index}" + enable_ipv6 = var.instance.spec.networking.enable_ipv6 + vpc_ids = local.vpc_ids + ssh_key_ids = [for k in vultr_ssh_key.this : k.id] + script_id = local.has_script ? vultr_startup_script.this[0].id : null + firewall_group_id = var.instance.spec.firewall.manage ? vultr_firewall_group.this[0].id : null + activation_email = false +} diff --git a/modules/compute/vultr/1.0/outputs.tf b/modules/compute/vultr/1.0/outputs.tf new file mode 100644 index 00000000..f344bd6d --- /dev/null +++ b/modules/compute/vultr/1.0/outputs.tf @@ -0,0 +1,22 @@ +locals { + output_attributes = { + instance_ids = [for i in vultr_instance.this : i.id] + instance_id = length(vultr_instance.this) > 0 ? vultr_instance.this[0].id : "" + main_ips = [for i in vultr_instance.this : i.main_ip] + main_ip = length(vultr_instance.this) > 0 ? vultr_instance.this[0].main_ip : "" + internal_ips = [for i in vultr_instance.this : i.internal_ip] + region = local.region + plan = var.instance.spec.sizing.plan + firewall_group_id = var.instance.spec.firewall.manage ? vultr_firewall_group.this[0].id : "" + } + + output_interfaces = { + ssh = { + host = length(vultr_instance.this) > 0 ? vultr_instance.this[0].main_ip : "" + port = "22" + username = "root" + password = length(vultr_instance.this) > 0 ? vultr_instance.this[0].default_password : "" + secrets = ["password"] + } + } +} diff --git a/modules/compute/vultr/1.0/variables.tf b/modules/compute/vultr/1.0/variables.tf new file mode 100644 index 00000000..ed2c7002 --- /dev/null +++ b/modules/compute/vultr/1.0/variables.tf @@ -0,0 +1,70 @@ +variable "instance" { + description = "Vultr cloud compute instances (count-scaled)" + type = object({ + kind = string + flavor = string + version = string + spec = object({ + region = optional(string) + sizing = object({ + plan = string + count = number + }) + image = object({ + os_id = number + }) + networking = optional(object({ + attach_vpc = optional(bool, true) + enable_ipv6 = optional(bool, false) + }), {}) + access = optional(object({ + ssh_public_keys = optional(list(string), []) + startup_script = optional(string, "") + }), {}) + firewall = optional(object({ + manage = optional(bool, false) + open_ports = optional(map(object({ + port = string + protocol = optional(string, "tcp") + source = optional(string, "0.0.0.0/0") + })), {}) + }), {}) + }) + }) + + validation { + condition = var.instance.spec.sizing.count >= 1 && var.instance.spec.sizing.count <= 20 + error_message = "count must be between 1 and 20." + } +} + +variable "instance_name" { + description = "The architectural name for the resource as added in the Facets blueprint designer." + type = string +} + +variable "environment" { + description = "An object containing details about the environment." + type = object({ + name = string + unique_name = string + cloud_tags = optional(map(string), {}) + }) +} + +variable "inputs" { + description = "A map of inputs requested by the module developer." + type = object({ + vultr_cloud_account = object({ + attributes = object({ + api_key = string + region = string + }) + }) + network = optional(object({ + attributes = object({ + vpc_id = string + }) + })) + }) +} diff --git a/modules/compute/vultr/1.0/versions.tf b/modules/compute/vultr/1.0/versions.tf new file mode 100644 index 00000000..1468f2c6 --- /dev/null +++ b/modules/compute/vultr/1.0/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + vultr = { + source = "vultr/vultr" + version = ">= 2.0.0" + } + } +} diff --git a/modules/datastore/kafka/vultr/1.0/facets.yaml b/modules/datastore/kafka/vultr/1.0/facets.yaml new file mode 100644 index 00000000..d810cff5 --- /dev/null +++ b/modules/datastore/kafka/vultr/1.0/facets.yaml @@ -0,0 +1,129 @@ +intent: kafka +flavor: vultr +version: "1.0" +clouds: + - kubernetes +description: Managed Kafka cluster on Vultr with SASL_SSL auth and trusted-IP allow-listing +intentDetails: + type: Datastores + description: Vultr managed Kafka streaming service + displayName: Kafka + iconUrl: https://raw.githubusercontent.com/Facets-cloud/facets-modules-redesign/main/icons/kafka.svg +spec: + title: Vultr Managed Kafka + description: Managed Kafka cluster on Vultr + type: object + x-ui-order: + - version_config + - sizing + - features + - network_access + properties: + version_config: + type: object + title: Version Configuration + x-ui-order: + - version + properties: + version: + type: string + title: Kafka Version + description: Apache Kafka version + enum: + - "3.8" + - "3.9" + - "4.0" + - "4.1" + default: "4.1" + required: + - version + sizing: + type: object + title: Sizing & Performance + x-ui-order: + - plan + properties: + plan: + type: string + title: Database Plan + description: "Vultr managed Kafka plan slug (encodes vCPU/RAM/node count). List plans via the Vultr API (GET /v2/databases/plans?engine=kafka)." + default: vultr-dbaas-business-3x-occ-so-2-200-4 + x-ui-placeholder: vultr-dbaas-business-3x-occ-so-2-200-4 + required: + - plan + features: + type: object + title: Features + x-ui-toggle: true + properties: + enable_kafka_rest: + type: boolean + title: Kafka REST Proxy + description: Enable the Kafka REST proxy interface + default: false + enable_schema_registry: + type: boolean + title: Schema Registry + description: Enable the Schema Registry service + default: false + enable_kafka_connect: + type: boolean + title: Kafka Connect + description: Enable Kafka Connect + default: false + network_access: + type: object + title: Network Access + properties: + trusted_ips: + type: array + title: Trusted IPs + description: List of CIDRs permitted to connect (empty denies all external access) + default: [] + items: + type: string + pattern: ^([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]{1,2}$ + required: [] + required: + - version_config + - sizing +inputs: + vultr_cloud_account: + type: "@facets/vultr_cloud_account" + optional: false + displayName: Vultr Cloud Account + description: Vultr provider configuration and default region + providers: + - vultr + network: + type: "@facets/vultr-vpc-details" + optional: true + displayName: VPC Network + description: Optional VPC for private connectivity; when wired the cluster is attached via vpc_id + providers: [] +outputs: + default: + type: "@facets/kafka" + title: Vultr Managed Kafka +sample: + kind: kafka + flavor: vultr + version: "1.0" + disabled: true + spec: + version_config: + version: "4.1" + sizing: + plan: vultr-dbaas-business-3x-occ-so-2-200-4 + features: + enable_kafka_rest: false + enable_schema_registry: false + enable_kafka_connect: false + network_access: + trusted_ips: [] +iac: + validated_files: + - main.tf + - variables.tf + - outputs.tf + - versions.tf diff --git a/modules/datastore/kafka/vultr/1.0/main.tf b/modules/datastore/kafka/vultr/1.0/main.tf new file mode 100644 index 00000000..3f2a09ae --- /dev/null +++ b/modules/datastore/kafka/vultr/1.0/main.tf @@ -0,0 +1,34 @@ +# Vultr Managed Kafka Module +# Creates a Vultr managed Kafka cluster. + +module "name" { + source = "github.com/Facets-cloud/facets-utility-modules//name" + environment = var.environment + limit = 32 + resource_name = var.instance_name + resource_type = "kafka" +} + +locals { + region = var.inputs.vultr_cloud_account.attributes.region +} + +resource "vultr_database" "db" { + label = module.name.name + database_engine = "kafka" + database_engine_version = var.instance.spec.version_config.version + region = local.region + plan = var.instance.spec.sizing.plan + trusted_ips = var.instance.spec.network_access.trusted_ips + + enable_kafka_rest = var.instance.spec.features.enable_kafka_rest + enable_schema_registry = var.instance.spec.features.enable_schema_registry + enable_kafka_connect = var.instance.spec.features.enable_kafka_connect + vpc_id = try(var.inputs.network.attributes.vpc_id, null) + backup_hour = "3" + backup_minute = "0" + + lifecycle { + prevent_destroy = true + } +} diff --git a/modules/datastore/kafka/vultr/1.0/outputs.tf b/modules/datastore/kafka/vultr/1.0/outputs.tf new file mode 100644 index 00000000..3bb15330 --- /dev/null +++ b/modules/datastore/kafka/vultr/1.0/outputs.tf @@ -0,0 +1,18 @@ +locals { + output_attributes = { + cluster_name = module.name.name + kafka_version = var.instance.spec.version_config.version + namespace = "" + } + + output_interfaces = { + cluster = { + endpoint = "${vultr_database.db.host}:${vultr_database.db.port}" + connection_string = "${vultr_database.db.host}:${vultr_database.db.port}" + endpoints = { broker = "${vultr_database.db.host}:${vultr_database.db.port}" } + username = vultr_database.db.user + password = vultr_database.db.password + secrets = ["password"] + } + } +} diff --git a/modules/datastore/kafka/vultr/1.0/variables.tf b/modules/datastore/kafka/vultr/1.0/variables.tf new file mode 100644 index 00000000..e7c75eed --- /dev/null +++ b/modules/datastore/kafka/vultr/1.0/variables.tf @@ -0,0 +1,60 @@ +variable "instance" { + description = "Managed Kafka cluster on Vultr with SASL_SSL auth and trusted-IP allow-listing" + type = object({ + kind = string + flavor = string + version = string + spec = object({ + version_config = object({ + version = string + }) + sizing = object({ + plan = string + }) + features = optional(object({ + enable_kafka_rest = optional(bool, false) + enable_schema_registry = optional(bool, false) + enable_kafka_connect = optional(bool, false) + }), {}) + network_access = optional(object({ + trusted_ips = optional(list(string), []) + }), {}) + }) + }) + + validation { + condition = contains(["3.8", "3.9", "4.0", "4.1"], var.instance.spec.version_config.version) + error_message = "Kafka version must be one of: 3.8, 3.9, 4.0, 4.1." + } +} + +variable "instance_name" { + description = "The architectural name for the resource as added in the Facets blueprint designer." + type = string +} + +variable "environment" { + description = "An object containing details about the environment." + type = object({ + name = string + unique_name = string + cloud_tags = optional(map(string), {}) + }) +} + +variable "inputs" { + description = "A map of inputs requested by the module developer." + type = object({ + vultr_cloud_account = object({ + attributes = object({ + api_key = string + region = string + }) + }) + network = optional(object({ + attributes = object({ + vpc_id = string + }) + })) + }) +} diff --git a/modules/datastore/kafka/vultr/1.0/versions.tf b/modules/datastore/kafka/vultr/1.0/versions.tf new file mode 100644 index 00000000..1468f2c6 --- /dev/null +++ b/modules/datastore/kafka/vultr/1.0/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + vultr = { + source = "vultr/vultr" + version = ">= 2.0.0" + } + } +} diff --git a/modules/datastore/mysql/vultr/1.0/facets.yaml b/modules/datastore/mysql/vultr/1.0/facets.yaml new file mode 100644 index 00000000..62db7b03 --- /dev/null +++ b/modules/datastore/mysql/vultr/1.0/facets.yaml @@ -0,0 +1,129 @@ +intent: mysql +flavor: vultr +version: "1.0" +clouds: + - kubernetes +description: Managed MySQL database on Vultr with SSL/TLS and trusted-IP allow-listing +intentDetails: + type: Datastores + description: Vultr managed MySQL database service + displayName: MySQL + iconUrl: https://raw.githubusercontent.com/Facets-cloud/facets-modules-redesign/main/icons/mysql.svg +spec: + title: Vultr Managed MySQL Database + description: Managed MySQL database on Vultr + type: object + x-ui-order: + - version_config + - sizing + - advanced_config + - network_access + properties: + version_config: + type: object + title: Version Configuration + x-ui-order: + - version + properties: + version: + type: string + title: MySQL Version + description: MySQL engine major version + enum: + - "8" + - "8.4" + default: "8" + required: + - version + sizing: + type: object + title: Sizing & Performance + x-ui-order: + - plan + properties: + plan: + type: string + title: Database Plan + description: "Vultr managed database plan slug (encodes vCPU/RAM/node count, e.g. vultr-dbaas-hobbyist-cc-1-25-1). List plans via the Vultr API (GET /v2/databases/plans)." + default: vultr-dbaas-hobbyist-cc-1-25-1 + x-ui-placeholder: vultr-dbaas-hobbyist-cc-1-25-1 + required: + - plan + advanced_config: + type: object + title: Advanced Configuration + x-ui-toggle: true + properties: + slow_query_log: + type: boolean + title: Slow Query Log + description: Enable the MySQL slow query log + default: false + long_query_time: + type: integer + title: Long Query Time (seconds) + description: Queries slower than this are logged (only applies when slow query log is enabled) + minimum: 1 + maximum: 3600 + default: 10 + require_primary_key: + type: boolean + title: Require Primary Key + description: Reject tables created without a primary key + default: false + network_access: + type: object + title: Network Access + properties: + trusted_ips: + type: array + title: Trusted IPs + description: List of CIDRs permitted to connect (empty denies all external access) + default: [] + items: + type: string + pattern: ^([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]{1,2}$ + required: [] + required: + - version_config + - sizing +inputs: + vultr_cloud_account: + type: "@facets/vultr_cloud_account" + optional: false + displayName: Vultr Cloud Account + description: Vultr provider configuration and default region + providers: + - vultr + network: + type: "@facets/vultr-vpc-details" + optional: true + displayName: VPC Network + description: Optional VPC for private connectivity; when wired the database is attached via vpc_id + providers: [] +outputs: + default: + type: "@facets/mysql" + title: Vultr MySQL Database +sample: + kind: mysql + flavor: vultr + version: "1.0" + disabled: true + spec: + version_config: + version: "8" + sizing: + plan: vultr-dbaas-hobbyist-cc-1-25-1 + advanced_config: + slow_query_log: false + long_query_time: 10 + require_primary_key: false + network_access: + trusted_ips: [] +iac: + validated_files: + - main.tf + - variables.tf + - outputs.tf + - versions.tf diff --git a/modules/datastore/mysql/vultr/1.0/main.tf b/modules/datastore/mysql/vultr/1.0/main.tf new file mode 100644 index 00000000..9300f3b9 --- /dev/null +++ b/modules/datastore/mysql/vultr/1.0/main.tf @@ -0,0 +1,34 @@ +# Vultr Managed MySQL Module +# Creates a Vultr managed MySQL database. + +module "name" { + source = "github.com/Facets-cloud/facets-utility-modules//name" + environment = var.environment + limit = 32 + resource_name = var.instance_name + resource_type = "mysql" +} + +locals { + region = var.inputs.vultr_cloud_account.attributes.region +} + +resource "vultr_database" "db" { + label = module.name.name + database_engine = "mysql" + database_engine_version = var.instance.spec.version_config.version + region = local.region + plan = var.instance.spec.sizing.plan + trusted_ips = var.instance.spec.network_access.trusted_ips + + mysql_slow_query_log = var.instance.spec.advanced_config.slow_query_log + mysql_long_query_time = var.instance.spec.advanced_config.slow_query_log ? var.instance.spec.advanced_config.long_query_time : null + mysql_require_primary_key = var.instance.spec.advanced_config.require_primary_key + vpc_id = try(var.inputs.network.attributes.vpc_id, null) + backup_hour = "3" + backup_minute = "0" + + lifecycle { + prevent_destroy = true + } +} diff --git a/modules/datastore/mysql/vultr/1.0/outputs.tf b/modules/datastore/mysql/vultr/1.0/outputs.tf new file mode 100644 index 00000000..ef32f3aa --- /dev/null +++ b/modules/datastore/mysql/vultr/1.0/outputs.tf @@ -0,0 +1,25 @@ +locals { + # Vultr managed MySQL exposes a single primary host; reader and writer share it. + output_attributes = {} + + output_interfaces = { + writer = { + host = vultr_database.db.host + port = vultr_database.db.port + username = vultr_database.db.user + password = vultr_database.db.password + database = vultr_database.db.dbname + connection_string = format("mysql://%s:%s@%s:%d/%s?ssl-mode=REQUIRED", vultr_database.db.user, vultr_database.db.password, vultr_database.db.host, vultr_database.db.port, vultr_database.db.dbname) + secrets = ["password", "connection_string"] + } + reader = { + host = vultr_database.db.host + port = vultr_database.db.port + username = vultr_database.db.user + password = vultr_database.db.password + database = vultr_database.db.dbname + connection_string = format("mysql://%s:%s@%s:%d/%s?ssl-mode=REQUIRED", vultr_database.db.user, vultr_database.db.password, vultr_database.db.host, vultr_database.db.port, vultr_database.db.dbname) + secrets = ["password", "connection_string"] + } + } +} diff --git a/modules/datastore/mysql/vultr/1.0/variables.tf b/modules/datastore/mysql/vultr/1.0/variables.tf new file mode 100644 index 00000000..4001128c --- /dev/null +++ b/modules/datastore/mysql/vultr/1.0/variables.tf @@ -0,0 +1,60 @@ +variable "instance" { + description = "Managed MySQL database on Vultr with SSL/TLS and trusted-IP allow-listing" + type = object({ + kind = string + flavor = string + version = string + spec = object({ + version_config = object({ + version = string + }) + sizing = object({ + plan = string + }) + advanced_config = optional(object({ + slow_query_log = optional(bool, false) + long_query_time = optional(number, 10) + require_primary_key = optional(bool, false) + }), {}) + network_access = optional(object({ + trusted_ips = optional(list(string), []) + }), {}) + }) + }) + + validation { + condition = contains(["8", "8.4"], var.instance.spec.version_config.version) + error_message = "MySQL version must be one of: 8, 8.4." + } +} + +variable "instance_name" { + description = "The architectural name for the resource as added in the Facets blueprint designer." + type = string +} + +variable "environment" { + description = "An object containing details about the environment." + type = object({ + name = string + unique_name = string + cloud_tags = optional(map(string), {}) + }) +} + +variable "inputs" { + description = "A map of inputs requested by the module developer." + type = object({ + vultr_cloud_account = object({ + attributes = object({ + api_key = string + region = string + }) + }) + network = optional(object({ + attributes = object({ + vpc_id = string + }) + })) + }) +} diff --git a/modules/datastore/mysql/vultr/1.0/versions.tf b/modules/datastore/mysql/vultr/1.0/versions.tf new file mode 100644 index 00000000..1468f2c6 --- /dev/null +++ b/modules/datastore/mysql/vultr/1.0/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + vultr = { + source = "vultr/vultr" + version = ">= 2.0.0" + } + } +} diff --git a/modules/datastore/postgres/vultr/1.0/facets.yaml b/modules/datastore/postgres/vultr/1.0/facets.yaml index 7d8fc3a6..ca92d76d 100644 --- a/modules/datastore/postgres/vultr/1.0/facets.yaml +++ b/modules/datastore/postgres/vultr/1.0/facets.yaml @@ -75,6 +75,12 @@ inputs: description: Vultr provider configuration and default region providers: - vultr + network: + type: "@facets/vultr-vpc-details" + optional: true + displayName: VPC Network + description: Optional VPC for private connectivity; when wired the database is attached via vpc_id + providers: [] outputs: default: type: "@facets/postgres" diff --git a/modules/datastore/postgres/vultr/1.0/main.tf b/modules/datastore/postgres/vultr/1.0/main.tf index 247d6ba2..55b008a0 100644 --- a/modules/datastore/postgres/vultr/1.0/main.tf +++ b/modules/datastore/postgres/vultr/1.0/main.tf @@ -20,6 +20,7 @@ resource "vultr_database" "db" { region = local.region plan = var.instance.spec.sizing.plan trusted_ips = var.instance.spec.network_access.trusted_ips + vpc_id = try(var.inputs.network.attributes.vpc_id, null) lifecycle { prevent_destroy = true diff --git a/modules/datastore/postgres/vultr/1.0/variables.tf b/modules/datastore/postgres/vultr/1.0/variables.tf index 1ed8cf13..39be6d4e 100644 --- a/modules/datastore/postgres/vultr/1.0/variables.tf +++ b/modules/datastore/postgres/vultr/1.0/variables.tf @@ -46,5 +46,10 @@ variable "inputs" { region = string }) }) + network = optional(object({ + attributes = object({ + vpc_id = string + }) + })) }) } diff --git a/modules/datastore/redis/vultr/1.0/facets.yaml b/modules/datastore/redis/vultr/1.0/facets.yaml new file mode 100644 index 00000000..463eae33 --- /dev/null +++ b/modules/datastore/redis/vultr/1.0/facets.yaml @@ -0,0 +1,124 @@ +intent: redis +flavor: vultr +version: "1.0" +clouds: + - kubernetes +description: Managed Valkey (Redis-compatible) cache on Vultr with TLS and trusted-IP allow-listing +intentDetails: + type: Datastores + description: Vultr managed Valkey (Redis-compatible) cache service + displayName: Redis + iconUrl: https://raw.githubusercontent.com/Facets-cloud/facets-modules-redesign/main/icons/redis.svg +spec: + title: Vultr Managed Valkey (Redis-compatible) Cache + description: Managed Valkey cache on Vultr + type: object + x-ui-order: + - version_config + - sizing + - advanced_config + - network_access + properties: + version_config: + type: object + title: Version Configuration + x-ui-order: + - version + properties: + version: + type: string + title: Valkey Version + description: Valkey engine version + enum: + - "8.1" + - "9.0" + default: "8.1" + required: + - version + sizing: + type: object + title: Sizing & Performance + x-ui-order: + - plan + properties: + plan: + type: string + title: Database Plan + description: "Vultr managed database plan slug (encodes vCPU/RAM/node count, e.g. vultr-dbaas-hobbyist-cc-1-25-1). List plans via the Vultr API (GET /v2/databases/plans)." + default: vultr-dbaas-business-cc-hp-amd-2-12-2 + x-ui-placeholder: vultr-dbaas-business-cc-hp-amd-2-12-2 + required: + - plan + advanced_config: + type: object + title: Advanced Configuration + x-ui-toggle: true + properties: + eviction_policy: + type: string + title: Eviction Policy + description: How Valkey selects what to remove when memory is full + default: noeviction + enum: + - noeviction + - allkeys-lru + - volatile-lru + - allkeys-random + - volatile-random + - volatile-ttl + - volatile-lfu + - allkeys-lfu + network_access: + type: object + title: Network Access + properties: + trusted_ips: + type: array + title: Trusted IPs + description: List of CIDRs permitted to connect (empty denies all external access) + default: [] + items: + type: string + pattern: ^([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]{1,2}$ + required: [] + required: + - version_config + - sizing +inputs: + vultr_cloud_account: + type: "@facets/vultr_cloud_account" + optional: false + displayName: Vultr Cloud Account + description: Vultr provider configuration and default region + providers: + - vultr + network: + type: "@facets/vultr-vpc-details" + optional: true + displayName: VPC Network + description: Optional VPC for private connectivity; when wired the database is attached via vpc_id + providers: [] +outputs: + default: + type: "@facets/redis" + title: Vultr Valkey (Redis-compatible) Cache +sample: + kind: redis + flavor: vultr + version: "1.0" + disabled: true + spec: + version_config: + version: "8.1" + sizing: + plan: vultr-dbaas-business-cc-hp-amd-2-12-2 + advanced_config: + eviction_policy: noeviction + network_access: + trusted_ips: [] +iac: + validated_files: + - main.tf + - variables.tf + - outputs.tf + - versions.tf diff --git a/modules/datastore/redis/vultr/1.0/main.tf b/modules/datastore/redis/vultr/1.0/main.tf new file mode 100644 index 00000000..125b0a00 --- /dev/null +++ b/modules/datastore/redis/vultr/1.0/main.tf @@ -0,0 +1,31 @@ +# Vultr Managed Valkey (Redis-compatible) Module +# Creates a Vultr managed Valkey database. + +module "name" { + source = "github.com/Facets-cloud/facets-utility-modules//name" + environment = var.environment + limit = 32 + resource_name = var.instance_name + resource_type = "redis" +} + +locals { + region = var.inputs.vultr_cloud_account.attributes.region +} + +resource "vultr_database" "db" { + label = module.name.name + database_engine = "valkey" + database_engine_version = var.instance.spec.version_config.version + region = local.region + plan = var.instance.spec.sizing.plan + eviction_policy = var.instance.spec.advanced_config.eviction_policy + trusted_ips = var.instance.spec.network_access.trusted_ips + vpc_id = try(var.inputs.network.attributes.vpc_id, null) + backup_hour = "3" + backup_minute = "0" + + lifecycle { + prevent_destroy = true + } +} diff --git a/modules/datastore/redis/vultr/1.0/outputs.tf b/modules/datastore/redis/vultr/1.0/outputs.tf new file mode 100644 index 00000000..5e686183 --- /dev/null +++ b/modules/datastore/redis/vultr/1.0/outputs.tf @@ -0,0 +1,12 @@ +locals { + output_attributes = {} + output_interfaces = { + cluster = { + endpoint = "${vultr_database.db.host}:${vultr_database.db.port}" + connection_string = format("rediss://%s:%s@%s:%s", vultr_database.db.user, vultr_database.db.password, vultr_database.db.host, tostring(vultr_database.db.port)) + auth_token = vultr_database.db.password + port = tostring(vultr_database.db.port) + secrets = ["auth_token", "connection_string"] + } + } +} diff --git a/modules/datastore/redis/vultr/1.0/variables.tf b/modules/datastore/redis/vultr/1.0/variables.tf new file mode 100644 index 00000000..114fb1bc --- /dev/null +++ b/modules/datastore/redis/vultr/1.0/variables.tf @@ -0,0 +1,66 @@ +variable "instance" { + description = "Managed Valkey (Redis-compatible) cache on Vultr with TLS and trusted-IP allow-listing" + type = object({ + kind = string + flavor = string + version = string + spec = object({ + version_config = object({ + version = string + }) + sizing = object({ + plan = string + }) + advanced_config = optional(object({ + eviction_policy = optional(string, "noeviction") + }), {}) + network_access = optional(object({ + trusted_ips = optional(list(string), []) + }), {}) + }) + }) + + validation { + condition = contains(["8.1", "9.0"], var.instance.spec.version_config.version) + error_message = "Valkey version must be one of: 8.1, 9.0." + } + + validation { + condition = contains([ + "noeviction", "allkeys-lru", "volatile-lru", "allkeys-random", + "volatile-random", "volatile-ttl", "volatile-lfu", "allkeys-lfu" + ], var.instance.spec.advanced_config.eviction_policy) + error_message = "eviction_policy must be a valid Valkey eviction policy." + } +} + +variable "instance_name" { + description = "The architectural name for the resource as added in the Facets blueprint designer." + type = string +} + +variable "environment" { + description = "An object containing details about the environment." + type = object({ + name = string + unique_name = string + cloud_tags = optional(map(string), {}) + }) +} + +variable "inputs" { + description = "A map of inputs requested by the module developer." + type = object({ + vultr_cloud_account = object({ + attributes = object({ + api_key = string + region = string + }) + }) + network = optional(object({ + attributes = object({ + vpc_id = string + }) + })) + }) +} diff --git a/modules/datastore/redis/vultr/1.0/versions.tf b/modules/datastore/redis/vultr/1.0/versions.tf new file mode 100644 index 00000000..1468f2c6 --- /dev/null +++ b/modules/datastore/redis/vultr/1.0/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + vultr = { + source = "vultr/vultr" + version = ">= 2.0.0" + } + } +} diff --git a/modules/load_balancer/vultr/1.0/facets.yaml b/modules/load_balancer/vultr/1.0/facets.yaml new file mode 100644 index 00000000..be43fb6c --- /dev/null +++ b/modules/load_balancer/vultr/1.0/facets.yaml @@ -0,0 +1,175 @@ +intent: load_balancer +flavor: vultr +version: "1.0" +clouds: + - kubernetes +description: Vultr Load Balancer fronting a set of compute instances with health checks, forwarding rules, and SSL redirect +intentDetails: + type: Cloud & Infrastructure + description: Vultr managed load balancer + displayName: Load Balancer + iconUrl: https://raw.githubusercontent.com/Facets-cloud/facets-modules-redesign/main/icons/loadbalancer.svg +spec: + title: Vultr Load Balancer + description: Distributes traffic across attached Vultr compute instances + type: object + x-ui-order: + - region + - balancing_algorithm + - nodes + - ssl_redirect + - proxy_protocol + - forwarding_rules + - health_check + properties: + region: + type: string + title: Region + description: Vultr region; defaults to the compute/cloud-account region when empty + x-ui-overrides-only: true + balancing_algorithm: + type: string + title: Balancing Algorithm + enum: + - roundrobin + - leastconn + default: roundrobin + nodes: + type: integer + title: LB Nodes + description: Number of load balancer nodes (odd number for HA) + minimum: 1 + maximum: 99 + default: 1 + ssl_redirect: + type: boolean + title: SSL Redirect + description: Redirect HTTP to HTTPS + default: false + proxy_protocol: + type: boolean + title: Proxy Protocol + default: false + forwarding_rules: + type: object + title: Forwarding Rules + description: Frontend-to-backend port mappings; key is a unique rule id + default: + http: + frontend_protocol: http + frontend_port: "80" + backend_protocol: http + backend_port: "80" + patternProperties: + ".*": + type: object + properties: + frontend_protocol: + type: string + enum: + - http + - https + - tcp + default: http + frontend_port: + type: string + default: "80" + backend_protocol: + type: string + enum: + - http + - https + - tcp + default: http + backend_port: + type: string + default: "80" + required: + - frontend_protocol + - frontend_port + - backend_protocol + - backend_port + health_check: + type: object + title: Health Check + properties: + protocol: + type: string + enum: + - http + - tcp + default: tcp + port: + type: string + default: "80" + path: + type: string + default: "/" + check_interval: + type: integer + default: 15 + response_timeout: + type: integer + default: 5 + unhealthy_threshold: + type: integer + default: 5 + healthy_threshold: + type: integer + default: 5 + required: + - forwarding_rules +inputs: + vultr_cloud_account: + type: "@facets/vultr_cloud_account" + optional: false + displayName: Vultr Cloud Account + description: Vultr provider configuration and default region + providers: + - vultr + compute: + type: "@facets/vultr-instance-details" + optional: false + displayName: Compute Instances + description: Compute module whose instances are attached as load balancer backends + providers: [] + network: + type: "@facets/vultr-vpc-details" + optional: true + displayName: VPC Network + description: Optional VPC for the load balancer + providers: [] +outputs: + default: + type: "@facets/vultr-load-balancer" + title: Vultr Load Balancer +sample: + kind: load_balancer + flavor: vultr + version: "1.0" + disabled: true + spec: + balancing_algorithm: roundrobin + nodes: 1 + ssl_redirect: false + proxy_protocol: false + forwarding_rules: + http: + frontend_protocol: http + frontend_port: "80" + backend_protocol: http + backend_port: "80" + health_check: + protocol: tcp + port: "80" + path: "/" + check_interval: 15 + response_timeout: 5 + unhealthy_threshold: 5 + healthy_threshold: 5 +iac: + validated_files: + - main.tf + - variables.tf + - outputs.tf + - versions.tf diff --git a/modules/load_balancer/vultr/1.0/main.tf b/modules/load_balancer/vultr/1.0/main.tf new file mode 100644 index 00000000..a43b8cb4 --- /dev/null +++ b/modules/load_balancer/vultr/1.0/main.tf @@ -0,0 +1,50 @@ +# Vultr Load Balancer Module +# Fronts a set of compute instances (from the compute input) with forwarding +# rules, a health check, and optional SSL redirect / proxy protocol. + +module "name" { + source = "github.com/Facets-cloud/facets-utility-modules//name" + environment = var.environment + limit = 32 + resource_name = var.instance_name + resource_type = "lb" +} + +locals { + region = coalesce( + try(var.instance.spec.region, null), + try(var.inputs.compute.attributes.region, null), + var.inputs.vultr_cloud_account.attributes.region + ) + vpc_id = try(var.inputs.network.attributes.vpc_id, null) +} + +resource "vultr_load_balancer" "this" { + region = local.region + label = module.name.name + balancing_algorithm = var.instance.spec.balancing_algorithm + ssl_redirect = var.instance.spec.ssl_redirect + proxy_protocol = var.instance.spec.proxy_protocol + attached_instances = var.inputs.compute.attributes.instance_ids + vpc = local.vpc_id + + dynamic "forwarding_rules" { + for_each = var.instance.spec.forwarding_rules + content { + frontend_protocol = forwarding_rules.value.frontend_protocol + frontend_port = forwarding_rules.value.frontend_port + backend_protocol = forwarding_rules.value.backend_protocol + backend_port = forwarding_rules.value.backend_port + } + } + + health_check { + protocol = var.instance.spec.health_check.protocol + port = var.instance.spec.health_check.port + path = var.instance.spec.health_check.path + check_interval = var.instance.spec.health_check.check_interval + response_timeout = var.instance.spec.health_check.response_timeout + unhealthy_threshold = var.instance.spec.health_check.unhealthy_threshold + healthy_threshold = var.instance.spec.health_check.healthy_threshold + } +} diff --git a/modules/load_balancer/vultr/1.0/outputs.tf b/modules/load_balancer/vultr/1.0/outputs.tf new file mode 100644 index 00000000..7289af02 --- /dev/null +++ b/modules/load_balancer/vultr/1.0/outputs.tf @@ -0,0 +1,16 @@ +locals { + output_attributes = { + lb_id = vultr_load_balancer.this.id + status = vultr_load_balancer.this.status + ipv4 = vultr_load_balancer.this.ipv4 + ipv6 = vultr_load_balancer.this.ipv6 + } + + output_interfaces = { + frontend = { + host = vultr_load_balancer.this.ipv4 + ipv6 = vultr_load_balancer.this.ipv6 + secrets = [] + } + } +} diff --git a/modules/load_balancer/vultr/1.0/variables.tf b/modules/load_balancer/vultr/1.0/variables.tf new file mode 100644 index 00000000..69bf0268 --- /dev/null +++ b/modules/load_balancer/vultr/1.0/variables.tf @@ -0,0 +1,72 @@ +variable "instance" { + description = "Vultr load balancer fronting compute instances" + type = object({ + kind = string + flavor = string + version = string + spec = object({ + region = optional(string) + balancing_algorithm = optional(string, "roundrobin") + nodes = optional(number, 1) + ssl_redirect = optional(bool, false) + proxy_protocol = optional(bool, false) + forwarding_rules = map(object({ + frontend_protocol = string + frontend_port = string + backend_protocol = string + backend_port = string + })) + health_check = optional(object({ + protocol = optional(string, "tcp") + port = optional(string, "80") + path = optional(string, "/") + check_interval = optional(number, 15) + response_timeout = optional(number, 5) + unhealthy_threshold = optional(number, 5) + healthy_threshold = optional(number, 5) + }), {}) + }) + }) + + validation { + condition = contains(["roundrobin", "leastconn"], var.instance.spec.balancing_algorithm) + error_message = "balancing_algorithm must be roundrobin or leastconn." + } +} + +variable "instance_name" { + description = "The architectural name for the resource as added in the Facets blueprint designer." + type = string +} + +variable "environment" { + description = "An object containing details about the environment." + type = object({ + name = string + unique_name = string + cloud_tags = optional(map(string), {}) + }) +} + +variable "inputs" { + description = "A map of inputs requested by the module developer." + type = object({ + vultr_cloud_account = object({ + attributes = object({ + api_key = string + region = string + }) + }) + compute = object({ + attributes = object({ + instance_ids = list(string) + region = optional(string) + }) + }) + network = optional(object({ + attributes = object({ + vpc_id = string + }) + })) + }) +} diff --git a/modules/load_balancer/vultr/1.0/versions.tf b/modules/load_balancer/vultr/1.0/versions.tf new file mode 100644 index 00000000..1468f2c6 --- /dev/null +++ b/modules/load_balancer/vultr/1.0/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + vultr = { + source = "vultr/vultr" + version = ">= 2.0.0" + } + } +} diff --git a/outputs/vultr-instance-details/outputs.yaml b/outputs/vultr-instance-details/outputs.yaml new file mode 100644 index 00000000..cc6de4ca --- /dev/null +++ b/outputs/vultr-instance-details/outputs.yaml @@ -0,0 +1,55 @@ +name: '@facets/vultr-instance-details' +title: "Vultr Compute Instances" +description: "Vultr cloud compute instance set details (count-scaled VMs)" +properties: + type: object + properties: + attributes: + type: object + properties: + instance_ids: + type: array + items: + type: string + description: All instance IDs in the set + instance_id: + type: string + description: First instance ID (convenience) + main_ips: + type: array + items: + type: string + description: Public main IPs + main_ip: + type: string + description: First public main IP (convenience) + internal_ips: + type: array + items: + type: string + description: VPC internal IPs + region: + type: string + plan: + type: string + firewall_group_id: + type: string + interfaces: + type: object + properties: + ssh: + type: object + properties: + host: + type: string + port: + type: string + username: + type: string + password: + type: string + secrets: + type: array + items: + type: string +providers: [] diff --git a/outputs/vultr-load-balancer/outputs.yaml b/outputs/vultr-load-balancer/outputs.yaml new file mode 100644 index 00000000..b8abcf9c --- /dev/null +++ b/outputs/vultr-load-balancer/outputs.yaml @@ -0,0 +1,32 @@ +name: '@facets/vultr-load-balancer' +title: "Vultr Load Balancer" +description: "Vultr load balancer endpoint details" +properties: + type: object + properties: + attributes: + type: object + properties: + lb_id: + type: string + status: + type: string + ipv4: + type: string + ipv6: + type: string + interfaces: + type: object + properties: + frontend: + type: object + properties: + host: + type: string + ipv6: + type: string + secrets: + type: array + items: + type: string +providers: []