feat: initial commit

- have been working on this for some time now
This commit is contained in:
2025-05-30 22:36:55 +10:00
commit cb67816eee
188 changed files with 6145 additions and 0 deletions
+47
View File
@@ -0,0 +1,47 @@
locals {
netmask_to_prefix = {
"0.0.0.0" = 0
"128.0.0.0" = 1
"192.0.0.0" = 2
"224.0.0.0" = 3
"240.0.0.0" = 4
"248.0.0.0" = 5
"252.0.0.0" = 6
"254.0.0.0" = 7
"255.0.0.0" = 8
"255.128.0.0" = 9
"255.192.0.0" = 10
"255.224.0.0" = 11
"255.240.0.0" = 12
"255.248.0.0" = 13
"255.252.0.0" = 14
"255.254.0.0" = 15
"255.255.0.0" = 16
"255.255.128.0" = 17
"255.255.192.0" = 18
"255.255.224.0" = 19
"255.255.240.0" = 20
"255.255.248.0" = 21
"255.255.252.0" = 22
"255.255.254.0" = 23
"255.255.255.0" = 24
"255.255.255.128" = 25
"255.255.255.192" = 26
"255.255.255.224" = 27
"255.255.255.240" = 28
"255.255.255.248" = 29
"255.255.255.252" = 30
"255.255.255.254" = 31
"255.255.255.255" = 32
}
prefix_length = lookup(local.netmask_to_prefix, var.cobbler_netmask, 24)
host_bits = 32 - local.prefix_length
total_hosts = pow(2, local.host_bits)
last_usable_ip = local.total_hosts - 2
# derive subnet base from instance IP
cidr_block = cidrsubnet("${incus_instance.this.ipv4_address}/${local.prefix_length}", 0, 0)
# get the last usable IP
gateway_ip = cidrhost(local.cidr_block, local.last_usable_ip)
}
+139
View File
@@ -0,0 +1,139 @@
module "storage_volume" {
source = "../../../../../../modules/storage_volume"
for_each = {
for k, v in var.storage_volumes : "${var.name}-${k}" => v
}
name = each.key
pool = each.value.pool
description = lookup(each.value, "description", null)
type = lookup(each.value, "type", "custom")
content_type = lookup(each.value, "content_type", null)
config = lookup(each.value, "config", {})
}
resource "incus_instance" "this" {
name = var.name
image = var.image
description = var.description
type = var.type
ephemeral = var.ephemeral
running = var.running
profiles = var.profiles
dynamic "wait_for" {
for_each = var.wait_for
content {
type = wait_for.value.type
nic = wait_for.value.nic
}
}
dynamic "device" {
for_each = var.disk_devices
content {
name = device.value.name
type = device.value.type
properties = {
path = device.value.properties["path"]
source = device.value.properties["source"]
pool = device.value.properties["pool"]
}
}
}
dynamic "device" {
for_each = {
for k, v in var.storage_volumes : k => {
name = k
path = v.path
pool = v.pool
source = "${var.name}-${k}"
}
}
content {
name = device.value.name
type = "disk"
properties = {
path = device.value.path
source = device.value.source
pool = device.value.pool
}
}
}
depends_on = [
module.storage_volume
]
}
resource "cobbler_system" "this" {
name = "${var.name}.${var.cobbler_domain}"
hostname = "${var.name}.${var.cobbler_domain}"
profile = var.cobbler_profile
status = "testing"
name_servers = var.cobbler_name_servers
mgmt_classes = var.cobbler_mgmt_classes
name_servers_search = ["${var.cobbler_domain}"]
interface {
name = "eth0"
mac_address = incus_instance.this.mac_address
static = true
ip_address = incus_instance.this.ipv4_address
netmask = var.cobbler_netmask
dns_name = "${var.name}.${var.cobbler_domain}"
gateway = local.gateway_ip
}
depends_on = [incus_instance.this]
}
resource "puppetdb_node" "this" {
certname = "${var.name}.${var.cobbler_domain}"
depends_on = [incus_instance.this]
}
resource "null_resource" "wait_for_instance_ready" {
depends_on = [incus_instance.this]
provisioner "local-exec" {
command = "sleep 10"
}
}
resource "puppetca_certificate" "cert" {
name = "${var.name}.${var.cobbler_domain}"
env = "production"
sign = false
depends_on = [
null_resource.wait_for_instance_ready
]
lifecycle {
create_before_destroy = false
}
}
#resource "puppetca_certificate" "cert" {
# count = (
# var.check_on_instance_creation ? 1 :
# (
# can(incus_instance.this.ipv4_address) && incus_instance.this.ipv4_address != "" ? 1 : 0
# )
# )
#
# name = "${var.name}.${var.cobbler_domain}"
# env = "production"
# sign = false
#
# depends_on = [incus_instance.this]
#
# lifecycle {
# create_before_destroy = false
# }
#}
+12
View File
@@ -0,0 +1,12 @@
#output "vm_metadata" {
# value = {
# ipaddress = incus_instance.this.ipv4_address
# gateway = local.gateway_ip
# hostname = "${var.name}.${var.cobbler_domain}"
# interface = "eth0"
# }
#}
output "hostname" {
value = "${var.name}.${var.cobbler_domain}"
}
+19
View File
@@ -0,0 +1,19 @@
provider "cobbler" {
username = var.cobbler_username
password = var.cobbler_password
url = var.cobbler_url
}
provider "puppetdb" {
url = var.puppetdb_url
ca = var.puppet_cert_ca
cert = var.puppet_cert_pub
key = var.puppet_cert_priv
}
provider "puppetca" {
url = var.puppetca_url
ca = var.puppet_cert_ca
cert = var.puppet_cert_pub
key = var.puppet_cert_priv
}
+172
View File
@@ -0,0 +1,172 @@
variable "name" {
description = "Name of the instance."
type = string
}
variable "image" {
description = "Base image from which the instance will be created."
type = string
default = null
}
variable "description" {
description = "Description of the instance."
type = string
default = null
}
variable "type" {
description = "Instance type. Can be 'container' or 'virtual-machine'."
type = string
default = "container"
}
variable "ephemeral" {
description = "Whether this instance is ephemeral."
type = bool
default = false
}
variable "running" {
description = "Whether the instance should be started (running)."
type = bool
default = true
}
variable "wait_for" {
description = <<EOT
List of wait_for blocks. Each block should contain:
- type: Type of wait (e.g., ipv4, ipv6)
- nic: Network interface name (e.g., eth0)
EOT
type = list(object({
type = string
nic = string
}))
default = []
}
variable "profiles" {
description = "List of Incus config profiles to apply to the instance. Use [] to apply none."
type = list(string)
default = null
}
variable "disk_devices" {
description = <<EOT
List of disk devices to attach to the instance. Each device must be:
- name : Name of the device
- type : Must be "disk"
- properties : Must include at least 'path', 'source', and 'pool'
EOT
type = list(object({
name = string
type = string
properties = object({
path = string
source = string
pool = string
})
}))
default = []
validation {
condition = alltrue([for d in var.disk_devices : d.type == "disk"])
error_message = "All devices must have type 'disk'."
}
}
variable "storage_volumes" {
description = "Map of storage volumes to create and attach."
type = map(object({
pool = string
description = optional(string)
type = optional(string)
content_type = optional(string)
config = optional(map(string))
path = string
}))
default = {}
}
variable "cobbler_domain" {
description = "Domain to assign in Cobbler for the system hostname"
type = string
}
variable "cobbler_profile" {
description = "Cobbler OS profile to use"
type = string
}
variable "cobbler_netmask" {
description = "Cobbler eth0 netmask to use"
type = string
}
variable "cobbler_name_servers" {
description = "List of name servers for the Cobbler system"
type = list(string)
default = ["198.18.13.12", "198.18.13.13"]
}
variable "cobbler_mgmt_classes" {
description = "List of Cobbler management classes. Must contain exactly 1 item."
type = list(string)
default = ["roles::base"]
validation {
condition = length(var.cobbler_mgmt_classes) == 1
error_message = "You must provide exactly 1 Cobbler management class."
}
}
variable "cobbler_url" {
description = "The URL to the Cobbler API"
type = string
}
variable "cobbler_username" {
description = "The username for the Cobbler API"
type = string
}
variable "cobbler_password" {
description = "The password for the Cobbler API"
type = string
}
variable "puppet_cert_ca" {
description = "Puppet CA cert content"
type = string
}
variable "puppet_cert_pub" {
description = "PuppetDB client cert content"
type = string
}
variable "puppet_cert_priv" {
description = "PuppetDB client private key content"
type = string
}
variable "puppetdb_url" {
description = "The URL of the PuppetPB API"
type = string
default = "https://puppetdbapi.query.consul:8081"
}
variable "puppetca_url" {
description = "The URL of the Puppet Certificate Authority (CA) server"
type = string
default = "https://puppetca.query.consul:8140"
}
variable "check_on_instance_creation" {
description = "If false, defer puppetca_certificate creation until instance is fully created."
type = bool
default = false
}