commit 63c37b28dc66da45bb39df0ef97f7d6b89d3b741 Author: ITQ Date: Fri Dec 26 15:47:40 2025 +0300 init: initial commit diff --git a/.env.template b/.env.template new file mode 100644 index 0000000..6c82072 --- /dev/null +++ b/.env.template @@ -0,0 +1,5 @@ +# This is just a template file, create .env file +# Change all vars before going to production and remove all comments (!) +# Below all environment variables and default values + +LIBVIRT_DEFAULT_URI=qemu:///system diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b971ac8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +# Terraform files +**/.terraform/* +*.auto.tfvars +*.tfoverride +.envrc +.terraformrc +terraform.rc +terraform.tfstate +terraform.tfstate* +.terraform* + +# Env files +.env diff --git a/configs/cloud-init/network.yaml.tpl b/configs/cloud-init/network.yaml.tpl new file mode 100644 index 0000000..c60b20c --- /dev/null +++ b/configs/cloud-init/network.yaml.tpl @@ -0,0 +1,17 @@ +#cloud-config +network: + version: 2 + ethernets: + enp1s0: + accept-ra: false + dhcp4: false + dhcp6: false + addresses: + - "${ipv4_address}/${ipv4_prefix}" + - "${ipv6_address}/${ipv6_prefix}" + gateway4: "${ipv4_gateway}" + gateway6: "${ipv6_gateway}" + nameservers: + addresses: + - "1.1.1.1" + - "2606:4700:4700::1111" diff --git a/configs/cloud-init/node.yaml.tpl b/configs/cloud-init/node.yaml.tpl new file mode 100644 index 0000000..de3e0ff --- /dev/null +++ b/configs/cloud-init/node.yaml.tpl @@ -0,0 +1,30 @@ +#cloud-config +hostname: node +manage_etc_hosts: false + +users: + - name: ubuntu + gecos: ubuntu + sudo: ALL=(ALL) NOPASSWD:ALL + lock_passwd: false + ssh_pwauth: true + shell: /bin/bash + +ssh_pwauth: true +chpasswd: + list: | + ubuntu:ubuntu + expire: false + +growpart: + mode: auto + devices: ["/"] + +package_update: false +package_upgrade: false + +write_files: + - path: /etc/hosts + append: true + encoding: b64 + content: ${hosts_file} diff --git a/configs/hosts b/configs/hosts new file mode 100644 index 0000000..2187b72 --- /dev/null +++ b/configs/hosts @@ -0,0 +1,4 @@ +10.6.6.1 _gateway + +10.6.6.10 node +2001:db8:ca2:2::10 node diff --git a/configs/libvirt/patch.xsl b/configs/libvirt/patch.xsl new file mode 100644 index 0000000..8ed50dc --- /dev/null +++ b/configs/libvirt/patch.xsl @@ -0,0 +1,22 @@ + + + + + + + + + + sata + + + + + + + + hvm + + + + \ No newline at end of file diff --git a/locals.tf b/locals.tf new file mode 100644 index 0000000..ac00534 --- /dev/null +++ b/locals.tf @@ -0,0 +1,35 @@ +locals { + dot_env_file_path = ".env" + dot_env_regex = "(?m:^\\s*([^#\\s]\\S*)\\s*=\\s*[\"']?(.*[^\"'\\s])[\"']?\\s*$)" + dot_env = { for tuple in regexall(local.dot_env_regex, file(local.dot_env_file_path)) : tuple[0] => sensitive(tuple[1]) } + + uri = local.dot_env["LIBVIRT_DEFAULT_URI"] + + cidrs = [ + var.network_ipv4_cidr, + var.network_ipv6_cidr, + ] + image_source = "${var.image_pool_folder}/${var.image_filename}" + user_templates = merge( + { node = "node.yaml.tpl" }, + ) + + ipv4_prefix = tonumber(split("/", var.network_ipv4_cidr)[1]) + ipv4_gateway = cidrhost(var.network_ipv4_cidr, 1) + ipv6_prefix = tonumber(split("/", var.network_ipv6_cidr)[1]) + ipv6_gateway = cidrhost(var.network_ipv6_cidr, 1) + + node_addrs = merge( + { node = { ipv4 = var.node_ipv4, ipv6 = var.node_ipv6 } }, + ) + + nodes = { for key, addr in local.node_addrs : + key => { + ipv4 = addr.ipv4 + ipv6 = addr.ipv6 + image = libvirt_volume.node_image.id + user_data = data.template_file.user_data[key].rendered + network_config = data.template_file.network_config[key].rendered + } + } +} diff --git a/main.tf b/main.tf new file mode 100644 index 0000000..2eb26b8 --- /dev/null +++ b/main.tf @@ -0,0 +1,62 @@ +resource "libvirt_network" "default" { + name = "${var.naming_prefix}default" + mode = "nat" + addresses = local.cidrs + mtu = var.mtu + autostart = true + + dns { + local_only = false + + forwarders { + address = "1.1.1.1" + } + } +} + +resource "libvirt_volume" "base" { + name = "${var.naming_prefix}ubuntu_noble" + source = local.image_source +} + +resource "libvirt_volume" "node_image" { + name = "${var.naming_prefix}node_image" + base_volume_id = libvirt_volume.base.id + size = 10737418240 +} + +data "template_file" "user_data" { + for_each = local.user_templates + + template = file("${path.root}/configs/cloud-init/${each.value}") + vars = { + hosts_file = base64encode(file("${path.root}/configs/hosts")) + } +} + +data "template_file" "network_config" { + for_each = local.user_templates + + template = file("${path.root}/configs/cloud-init/network.yaml.tpl") + vars = { + ipv4_address = local.node_addrs[each.key].ipv4 + ipv4_prefix = local.ipv4_prefix + ipv4_gateway = local.ipv4_gateway + ipv6_address = local.node_addrs[each.key].ipv6 + ipv6_prefix = local.ipv6_prefix + ipv6_gateway = local.ipv6_gateway + } +} + +module "nodes" { + source = "./modules/instance" + for_each = local.nodes + + name = "${var.naming_prefix}${each.key}" + network_id = libvirt_network.default.id + volume_base_id = each.value.image + user_data = each.value.user_data + network_config = each.value.network_config + vcpu = var.node_cpu + memory = var.node_mem +} diff --git a/modules/instance/main.tf b/modules/instance/main.tf new file mode 100644 index 0000000..0625f4b --- /dev/null +++ b/modules/instance/main.tf @@ -0,0 +1,35 @@ +resource "libvirt_volume" "image" { + name = "${var.name}_image" + base_volume_id = var.volume_base_id +} + +resource "libvirt_cloudinit_disk" "init" { + name = "${var.name}_cloudinit" + user_data = var.user_data + network_config = var.network_config +} + +resource "libvirt_domain" "this" { + name = var.name + vcpu = var.vcpu + memory = var.memory + + cpu { mode = "host-passthrough" } + + disk { volume_id = libvirt_volume.image.id } + cloudinit = libvirt_cloudinit_disk.init.id + + network_interface { + network_id = var.network_id + wait_for_lease = false + } + + arch = "x86_64" + type = "kvm" + machine = "q35" + + running = true + autostart = false + + xml { xslt = file("${path.root}/configs/libvirt/patch.xsl") } +} diff --git a/modules/instance/provider.tf b/modules/instance/provider.tf new file mode 100644 index 0000000..0509a13 --- /dev/null +++ b/modules/instance/provider.tf @@ -0,0 +1,7 @@ +terraform { + required_providers { + libvirt = { + source = "dmacvicar/libvirt" + } + } +} diff --git a/modules/instance/variables.tf b/modules/instance/variables.tf new file mode 100644 index 0000000..34e2d3b --- /dev/null +++ b/modules/instance/variables.tf @@ -0,0 +1,7 @@ +variable "name" {} +variable "network_id" {} +variable "volume_base_id" {} +variable "user_data" {} +variable "network_config" {} +variable "vcpu" {} +variable "memory" {} diff --git a/provider.tf b/provider.tf new file mode 100644 index 0000000..88c8416 --- /dev/null +++ b/provider.tf @@ -0,0 +1,11 @@ +terraform { + required_providers { + libvirt = { + source = "dmacvicar/libvirt" + } + } +} + +provider "libvirt" { + uri = local.uri +} diff --git a/variables.tf b/variables.tf new file mode 100644 index 0000000..9f357e2 --- /dev/null +++ b/variables.tf @@ -0,0 +1,56 @@ +variable "naming_prefix" { + type = string + default = "" +} + +variable "dot_env_file_path" { + type = string + default = ".env" +} + +variable "image_pool_folder" { + description = "Local folder where base images are stored" + type = string + default = "./assets/images" +} + +variable "image_filename" { + description = "Name of the base cloud image file" + type = string + default = "noble-server-cloudimg-amd64.img" +} + +variable "node_ipv4" { + type = string + default = "10.6.6.10" +} + +variable "node_ipv6" { + type = string + default = "2001:db8:ca2:2::10" +} + +variable "network_ipv4_cidr" { + type = string + default = "10.6.6.0/24" +} + +variable "network_ipv6_cidr" { + type = string + default = "2001:db8:ca2:2::/64" +} + +variable "mtu" { + type = number + default = 1500 +} + +variable "node_cpu" { + type = number + default = 2 +} + +variable "node_mem" { + type = number + default = 1024 +}