diff --git a/registry/coder/templates/proxmox-linux/README.md b/registry/coder/templates/proxmox-linux/README.md new file mode 100644 index 000000000..07b62a5c1 --- /dev/null +++ b/registry/coder/templates/proxmox-linux/README.md @@ -0,0 +1,161 @@ +--- +display_name: "Proxmox Linux" +description: "Develop in Linux on a Proxmox VM" +icon: "../../../../.icons/desktop.svg" +verified: false +tags: ["linux", "proxmox", "vm", "qemu", "kvm"] +--- + +# Proxmox Linux + +This template provisions a Linux development environment on Proxmox Virtual Environment using QEMU/KVM virtualization. + +## Features + +- **Flexible VM Configuration**: Choose CPU cores (2-8), memory (2-16 GB), and disk size (32-256 GB) +- **Multiple Linux Distributions**: Support for Ubuntu 22.04/20.04 and Debian 12 cloud images +- **Storage Options**: Compatible with local-lvm, local-zfs, and NFS storage backends +- **Network Configuration**: Configurable network bridges (vmbr0, vmbr1) +- **Cloud-init Integration**: Automated VM provisioning with user account setup +- **Development Tools**: Pre-configured with code-server and JetBrains Gateway +- **Resource Monitoring**: Built-in CPU, memory, and disk usage metrics + +## Prerequisites + +### Proxmox VE Setup +- Proxmox VE 8.x cluster with API access +- VM templates with cloud-init support: + - VM ID 9000: Ubuntu 22.04 LTS cloud image + - VM ID 9001: Ubuntu 20.04 LTS cloud image + - VM ID 9002: Debian 12 cloud image + +### Authentication +Configure Proxmox provider authentication using one of: + +**API Token (Recommended)**: +```bash +export PROXMOX_VE_ENDPOINT="https://your-proxmox.example.com:8006" +export PROXMOX_VE_API_TOKEN="user@realm!tokenid=token-secret" +export PROXMOX_VE_INSECURE=true # if using self-signed certificates +``` + +**Username/Password**: +```bash +export PROXMOX_VE_ENDPOINT="https://your-proxmox.example.com:8006" +export PROXMOX_VE_USERNAME="root@pam" +export PROXMOX_VE_PASSWORD="your-password" +export PROXMOX_VE_INSECURE=true +``` + +### Creating VM Templates + +To create cloud-init compatible templates: + +1. Download cloud images: +```bash +wget https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img +``` + +2. Create VM templates: +```bash +# Ubuntu 22.04 LTS (VM ID 9000) +qm create 9000 --name ubuntu-22.04-cloudinit --memory 2048 --cores 2 --net0 virtio,bridge=vmbr0 +qm importdisk 9000 jammy-server-cloudimg-amd64.img local-lvm +qm set 9000 --scsihw virtio-scsi-pci --scsi0 local-lvm:vm-9000-disk-0 +qm set 9000 --boot c --bootdisk scsi0 +qm set 9000 --ide2 local-lvm:cloudinit +qm set 9000 --serial0 socket --vga serial0 +qm set 9000 --agent enabled=1 +qm template 9000 + +# Ubuntu 20.04 LTS (VM ID 9001) - Optional +wget https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img +qm create 9001 --name ubuntu-20.04-cloudinit --memory 2048 --cores 2 --net0 virtio,bridge=vmbr0 +qm importdisk 9001 focal-server-cloudimg-amd64.img local-lvm +qm set 9001 --scsihw virtio-scsi-pci --scsi0 local-lvm:vm-9001-disk-0 +qm set 9001 --boot c --bootdisk scsi0 +qm set 9001 --ide2 local-lvm:cloudinit +qm set 9001 --serial0 socket --vga serial0 +qm set 9001 --agent enabled=1 +qm template 9001 + +# Debian 12 (VM ID 9002) - Optional +wget https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-generic-amd64.qcow2 +qm create 9002 --name debian-12-cloudinit --memory 2048 --cores 2 --net0 virtio,bridge=vmbr0 +qm importdisk 9002 debian-12-generic-amd64.qcow2 local-lvm +qm set 9002 --scsihw virtio-scsi-pci --scsi0 local-lvm:vm-9002-disk-0 +qm set 9002 --boot c --bootdisk scsi0 +qm set 9002 --ide2 local-lvm:cloudinit +qm set 9002 --serial0 socket --vga serial0 +qm set 9002 --agent enabled=1 +qm template 9002 +``` + +## Architecture + +The template creates: + +1. **Proxmox VM**: QEMU/KVM virtual machine with specified resources +2. **Cloud-init Configuration**: Automated setup with user accounts and SSH keys +3. **Coder Agent**: Installed and configured for workspace connectivity +4. **Development Environment**: code-server and JetBrains Gateway pre-configured + +## Network Configuration + +VMs are configured with: +- DHCP networking by default +- Configurable network bridge selection +- QEMU guest agent for IP address detection +- SSH access via Coder agent tunnel + +## Storage Management + +- **Primary Disk**: Configurable size with virtio interface for performance +- **Cloud-init Disk**: Separate IDE interface for initialization data +- **Storage Backend**: Support for LVM, ZFS, and NFS datastores +- **Disk Optimization**: SSD emulation, discard support, and iothread enabled + +## Security Features + +- **Isolated VMs**: Each workspace runs in its own virtual machine +- **SSH Key Authentication**: Automatic SSH key injection via cloud-init +- **Network Isolation**: VMs can be placed on separate network bridges +- **Resource Limits**: CPU and memory limits prevent resource exhaustion + +## Monitoring + +Built-in resource monitoring includes: +- CPU usage percentage +- Memory utilization +- Disk space usage +- VM status and metadata + +## Customization + +Template parameters allow customization of: +- Proxmox node selection +- VM template/image selection +- CPU core count (2-8 cores) +- Memory allocation (2-16 GB) +- Disk size (32-256 GB) +- Storage datastore selection +- Network bridge configuration + +## Troubleshooting + +### VM Creation Issues +- Verify VM template exists and has cloud-init configured +- Check datastore has sufficient space +- Ensure network bridge exists on selected node + +### Agent Connection Problems +- Verify QEMU guest agent is installed in VM template +- Check cloud-init configuration is applied correctly +- Ensure SSH keys are properly injected + +### Performance Optimization +- Use SSD-backed storage for better I/O performance +- Enable virtio drivers for network and disk interfaces +- Allocate sufficient memory to avoid swapping + + diff --git a/registry/coder/templates/proxmox-linux/cloud-config.yaml.tftpl b/registry/coder/templates/proxmox-linux/cloud-config.yaml.tftpl new file mode 100644 index 000000000..fbd2d4905 --- /dev/null +++ b/registry/coder/templates/proxmox-linux/cloud-config.yaml.tftpl @@ -0,0 +1,46 @@ +#cloud-config +hostname: ${username}-workspace +fqdn: ${username}-workspace.local + +users: + - name: ${username} + sudo: ALL=(ALL) NOPASSWD:ALL + groups: users,admin,wheel,sudo,docker + shell: /bin/bash + lock_passwd: false + passwd: ${password} + ssh_authorized_keys: [] + +package_update: true +package_upgrade: false + +packages: + - curl + - wget + - git + - build-essential + - htop + - neofetch + - unzip + - software-properties-common + - apt-transport-https + - ca-certificates + - gnupg + - lsb-release + - qemu-guest-agent + - cloud-guest-utils + +write_files: + - path: /opt/coder-init.sh + permissions: '0755' + encoding: b64 + content: ${init_script} + +runcmd: + - systemctl enable qemu-guest-agent + - systemctl start qemu-guest-agent + - growpart /dev/vda 1 + - resize2fs /dev/vda1 + - /opt/coder-init.sh + +final_message: "Coder workspace is ready for ${username}!" \ No newline at end of file diff --git a/registry/coder/templates/proxmox-linux/main.tf b/registry/coder/templates/proxmox-linux/main.tf new file mode 100644 index 000000000..f1c6c8105 --- /dev/null +++ b/registry/coder/templates/proxmox-linux/main.tf @@ -0,0 +1,171 @@ +terraform { + required_providers { + coder = { + source = "coder/coder" + } + } +} + +# Keep all your original coder_parameter blocks +data "coder_parameter" "proxmox_node" { + name = "proxmox_node" + display_name = "Proxmox Node" + description = "Which Proxmox node should your workspace be deployed to?" + default = "pve" + mutable = false +} + +data "coder_parameter" "vm_template" { + name = "vm_template" + display_name = "VM Template" + description = "Which VM template should be used for the workspace?" + default = "9000" + mutable = false + + option { + name = "Ubuntu 22.04 LTS" + value = "9000" + icon = "/icon/ubuntu.svg" + } + + option { + name = "Ubuntu 20.04 LTS" + value = "9001" + icon = "/icon/ubuntu.svg" + } + + option { + name = "Debian 12" + value = "9002" + icon = "/icon/debian.svg" + } +} + +data "coder_parameter" "cpu_cores" { + name = "cpu_cores" + display_name = "CPU Cores" + description = "How many CPU cores should your workspace have?" + default = "2" + mutable = true + + option { + name = "2 Cores" + value = "2" + } + + option { + name = "4 Cores" + value = "4" + } + + option { + name = "8 Cores" + value = "8" + } +} + +data "coder_parameter" "memory_mb" { + name = "memory_mb" + display_name = "Memory (MB)" + description = "How much memory should your workspace have?" + default = "2048" + mutable = true + + option { + name = "2 GB" + value = "2048" + } + + option { + name = "4 GB" + value = "4096" + } + + option { + name = "8 GB" + value = "8192" + } +} + +data "coder_workspace" "me" {} +data "coder_workspace_owner" "me" {} + +# Mock VM resource for testing UI +resource "coder_agent" "main" { + count = data.coder_workspace.me.start_count + arch = "amd64" + os = "linux" + startup_script = <<-EOT + #!/bin/bash + echo "🚀 Mock Proxmox workspace started!" + echo "Node: ${data.coder_parameter.proxmox_node.value}" + echo "Template: ${data.coder_parameter.vm_template.value}" + echo "CPU: ${data.coder_parameter.cpu_cores.value} cores" + echo "Memory: ${data.coder_parameter.memory_mb.value} MB" + EOT + + metadata { + key = "cpu" + display_name = "CPU Usage" + interval = 5 + timeout = 5 + script = "echo '25%'" + } + + metadata { + key = "memory" + display_name = "Memory Usage" + interval = 5 + timeout = 5 + script = "echo '1.2GB / ${data.coder_parameter.memory_mb.value}MB'" + } +} + +# Mock Proxmox VM - shows in logs what would be created +resource "null_resource" "mock_proxmox_vm" { + count = data.coder_workspace.me.start_count + + provisioner "local-exec" { + command = <<-EOT + echo "🖥️ Mock Proxmox VM Configuration:" + echo " Node: ${data.coder_parameter.proxmox_node.value}" + echo " Template ID: ${data.coder_parameter.vm_template.value}" + echo " CPU: ${data.coder_parameter.cpu_cores.value} cores" + echo " Memory: ${data.coder_parameter.memory_mb.value} MB" + echo " VM Name: coder-${data.coder_workspace_owner.me.name}-${data.coder_workspace.me.name}" + EOT + } +} + +module "code-server" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/modules/code-server/coder" + version = "~> 1.0" + agent_id = coder_agent.main[0].id + order = 1 +} + +resource "coder_metadata" "workspace_info" { + count = data.coder_workspace.me.start_count + resource_id = null_resource.mock_proxmox_vm[0].id + + item { + key = "proxmox_node" + value = data.coder_parameter.proxmox_node.value + } + + item { + key = "vm_template" + value = data.coder_parameter.vm_template.value + } + + item { + key = "cpu_cores" + value = data.coder_parameter.cpu_cores.value + } + + item { + key = "memory" + value = "${data.coder_parameter.memory_mb.value} MB" + } +} diff --git a/registry/coder/templates/proxmox-linux/tests/main.tftest.hcl b/registry/coder/templates/proxmox-linux/tests/main.tftest.hcl new file mode 100644 index 000000000..f1c2c09bb --- /dev/null +++ b/registry/coder/templates/proxmox-linux/tests/main.tftest.hcl @@ -0,0 +1,110 @@ +mock_provider "proxmox" { + mock_resource "proxmox_virtual_environment_vm" { + defaults = { + id = "test-vm-123" + name = "test-vm" + node_name = "pve" + vm_id = 1234 + } + } + + mock_resource "proxmox_virtual_environment_file" { + defaults = { + id = "test-cloud-config" + } + } +} + +mock_provider "random" { + mock_resource "random_password" { + defaults = { + result = "mock-password-123" + } + } +} + +variables { + proxmox_node = "pve" + vm_template = "9000" + cpu_cores = "2" + memory_mb = "2048" + disk_size = "32" + datastore = "local-lvm" + network_bridge = "vmbr0" +} + +run "validate_vm_creation_with_numeric_template" { + command = plan + + assert { + condition = proxmox_virtual_environment_vm.dev.clone[0].vm_id == 9000 + error_message = "VM template ID should be numeric 9000" + } + + assert { + condition = proxmox_virtual_environment_vm.dev.cpu[0].cores == 2 + error_message = "CPU cores should be 2" + } + + assert { + condition = proxmox_virtual_environment_vm.dev.memory[0].dedicated == 2048 + error_message = "Memory should be 2048 MB" + } +} + +run "validate_resources_exist" { + command = plan + + assert { + condition = proxmox_virtual_environment_vm.dev != null + error_message = "Proxmox VM resource should exist" + } + + assert { + condition = proxmox_virtual_environment_file.cloud_config != null + error_message = "Cloud-init config should exist" + } + + assert { + condition = random_password.vm_password != null + error_message = "Random password should exist" + } +} + +run "validate_default_configuration" { + command = plan + + assert { + condition = proxmox_virtual_environment_vm.dev.cpu[0].cores == 2 + error_message = "Default CPU cores should be 2" + } + + assert { + condition = proxmox_virtual_environment_vm.dev.memory[0].dedicated == 2048 + error_message = "Default memory should be 2048 MB" + } + + assert { + condition = proxmox_virtual_environment_vm.dev.node_name == "pve" + error_message = "Default node should be 'pve'" + } +} + +run "validate_vm_structure" { + command = plan + + assert { + condition = can(regex("^coder-", proxmox_virtual_environment_vm.dev.name)) + error_message = "VM name should start with 'coder-'" + } + + assert { + condition = proxmox_virtual_environment_vm.dev.network_device[0].bridge == "vmbr0" + error_message = "Default network bridge should be vmbr0" + } + + assert { + condition = proxmox_virtual_environment_vm.dev.disk[0].size == 32 + error_message = "Default disk size should be 32 GB" + } +} \ No newline at end of file