diff --git a/registry/l-nmch/.images/avatar.png b/registry/l-nmch/.images/avatar.png new file mode 100644 index 000000000..9117d7e31 Binary files /dev/null and b/registry/l-nmch/.images/avatar.png differ diff --git a/registry/l-nmch/README.md b/registry/l-nmch/README.md new file mode 100644 index 000000000..131437801 --- /dev/null +++ b/registry/l-nmch/README.md @@ -0,0 +1,13 @@ +--- +display_name: "Léo Nonnenmacher" +bio: "DevOps | Administrator" +github: "l-nmch" +avatar: "./.images/avatar.png" +linkedin: "https://fr.linkedin.com/in/léo-nonnenmacher" +support_email: "leo@nonnenmacher-logel.fr" +status: "community" +--- + +# Léo Nonnenmacher + +DevOps | Administrator \ No newline at end of file diff --git a/registry/l-nmch/templates/incus-instance/README.md b/registry/l-nmch/templates/incus-instance/README.md new file mode 100644 index 000000000..ded1bf25a --- /dev/null +++ b/registry/l-nmch/templates/incus-instance/README.md @@ -0,0 +1,61 @@ +--- +display_name: Incus Instance +description: Provision Instance on Incus/LXD as Coder workspaces +icon: ../../../../.icons/proxmox.svg +verified: false +tags: [incus, lxc, vm, cloud-init, container] +--- + +> Based on the work of [umair](https://github.com/l-nmch/registry/tree/main/registry/umair) + +# Incus VM Template for Coder + +Provision Linux VMs & Containers on Incus/LXD as [Coder workspaces](https://coder.com/docs/workspaces). The template deploys an instance with cloud-init, and runs the Coder agent under the workspace owner's Linux user. + +## Prerequisites + +- Incus server / cluster with exposed API + +### Setup the template + +1. Create an Incus trust token: + +```bash +incus config trust add coder # Save the token +``` + +2. Setup an Incus project with a network: + +```bash +incus project create Coder -c features.network=true +incus network create Main --project Coder +``` + +3. Prepare `terraform.tfvars` in your environment: + +```bash +remote_name = "incus" +remote_address = "https://incus.local:8443" +remote_project = "Coder" +remote_network = "Main" +remote_storage_pool = "local" +remote_token = "" +``` + +## Use + +```bash +coder template push incus-instance -d . +``` + +## Warnings + +Incus often works with cloud image, please use `cloud` tagged images such as `images:ubuntu/22.04/cloud` to be able to use cloud-init (Using non cloud tagged images will lead into your workspaces not working as the coder agent installs through cloud-init) + +## References + +- Incus: [source](https://linuxcontainers.org/incus/) +- Incus Terraform Provider: [source](https://registry.terraform.io/providers/lxc/incus/latest/docs) +- Coder – Best practices & templates: + - https://coder.com/docs/tutorials/best-practices/speed-up-templates + - https://coder.com/docs/tutorials/template-from-scratch \ No newline at end of file diff --git a/registry/l-nmch/templates/incus-instance/cloud-init/user-data.tftpl b/registry/l-nmch/templates/incus-instance/cloud-init/user-data.tftpl new file mode 100644 index 000000000..2cd34d0ac --- /dev/null +++ b/registry/l-nmch/templates/incus-instance/cloud-init/user-data.tftpl @@ -0,0 +1,53 @@ +#cloud-config +hostname: ${hostname} + +users: + - name: ${linux_user} + groups: [sudo] + shell: /bin/bash + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + +package_update: false +package_upgrade: false +packages: + - curl + - ca-certificates + - git + - jq + +write_files: + - path: /opt/coder/init.sh + permissions: "0755" + owner: root:root + encoding: b64 + content: | + ${coder_init_script_b64} + + - path: /etc/systemd/system/coder-agent.service + permissions: "0644" + owner: root:root + content: | + [Unit] + Description=Coder Agent + Wants=network-online.target + After=network-online.target + + [Service] + Type=simple + User=${linux_user} + WorkingDirectory=/home/${linux_user} + Environment=HOME=/home/${linux_user} + Environment=CODER_AGENT_TOKEN=${coder_token} + ExecStart=/opt/coder/init.sh + OOMScoreAdjust=-1000 + Restart=always + RestartSec=5 + + [Install] + WantedBy=multi-user.target + +runcmd: + - systemctl daemon-reload + - systemctl enable --now coder-agent.service + +final_message: "Cloud-init complete on ${hostname}" \ No newline at end of file diff --git a/registry/l-nmch/templates/incus-instance/main.tf b/registry/l-nmch/templates/incus-instance/main.tf new file mode 100644 index 000000000..4e7a7cb52 --- /dev/null +++ b/registry/l-nmch/templates/incus-instance/main.tf @@ -0,0 +1,219 @@ +terraform { + required_providers { + coder = { + source = "coder/coder" + } + incus = { + source = "lxc/incus" + version = "1.0.2" + } + } +} + +provider "coder" {} + +provider "incus" { + accept_remote_certificate = true + generate_client_certificates = true + default_remote = var.remote_name + remote { + name = var.remote_name + address = var.remote_address + token = var.remote_token + } +} + +variable "remote_name" { + description = "Incus remote host/cluster name" + type = string + default = "remote" +} + +variable "remote_address" { + description = "Incus remote address (e.g. https://lxc.example.com:8443)" + type = string +} + +variable "remote_token" { + description = "Incus remote API token with permissions to manage instances" + type = string + sensitive = true +} + +variable "remote_project" { + description = "Incus remote project to use for instances" + type = string + default = "default" +} + +variable "remote_network" { + description = "Incus remote network to attach instances to" + type = string +} + +variable "remote_profiles" { + description = "Incus remote profiles to use for instances" + type = list(string) + default = [] +} + +variable "remote_storage_pool" { + description = "Incus remote storage pool to use for instances" + type = string +} + +data "coder_workspace" "me" {} +data "coder_workspace_owner" "me" {} + +data "coder_parameter" "cpu_cores" { + name = "cpu_cores" + display_name = "CPU Cores" + type = "number" + default = 2 + mutable = true +} + +data "coder_parameter" "memory_mb" { + name = "memory_mb" + display_name = "Memory (MB)" + type = "number" + default = 4096 + mutable = true +} + +data "coder_parameter" "disk_size_gb" { + name = "disk_size_gb" + display_name = "Disk Size (GB)" + type = "number" + default = 20 + mutable = true + validation { + min = 10 + max = 100 + monotonic = "increasing" + } +} + +data "coder_parameter" "image" { + name = "image" + display_name = "Instance Image" + type = "string" + default = "images:ubuntu/22.04/cloud" + mutable = true +} + +data "coder_parameter" "instance_type" { + name = "instance_type" + display_name = "Instance Type" + type = "string" + default = "virtual-machine" + mutable = true + + option { + name = "Virtual Machine" + value = "virtual-machine" + } + + option { + name = "LXC Container" + value = "container" + } +} + +resource "coder_agent" "dev" { + arch = "amd64" + os = "linux" + + env = { + GIT_AUTHOR_NAME = data.coder_workspace_owner.me.name + GIT_AUTHOR_EMAIL = data.coder_workspace_owner.me.email + } + + startup_script_behavior = "non-blocking" + startup_script = <<-EOT + set -e + # Add any startup scripts here + EOT + + metadata { + display_name = "CPU Usage" + key = "cpu_usage" + script = "coder stat cpu" + interval = 10 + timeout = 1 + order = 1 + } + + metadata { + display_name = "RAM Usage" + key = "ram_usage" + script = "coder stat mem" + interval = 10 + timeout = 1 + order = 2 + } + + metadata { + display_name = "Disk Usage" + key = "disk_usage" + script = "coder stat disk" + interval = 600 + timeout = 30 + order = 3 + } +} + +locals { + hostname = lower(data.coder_workspace.me.name) + vm_name = "coder-${lower(data.coder_workspace_owner.me.name)}-${local.hostname}" + snippet_filename = "${local.vm_name}.yml" + base_user = replace(replace(replace(lower(data.coder_workspace_owner.me.name), " ", "-"), "/", "-"), "@", "-") # to avoid special characters in the username + linux_user = contains(["root", "admin", "daemon", "bin", "sys"], local.base_user) ? "${local.base_user}1" : local.base_user # to avoid conflict with system users + + rendered_user_data = templatefile("${path.module}/cloud-init/user-data.tftpl", { + coder_token = coder_agent.dev.token + coder_init_script_b64 = base64encode(coder_agent.dev.init_script) + hostname = local.vm_name + linux_user = local.linux_user + }) +} + +resource "incus_instance" "workspace" { + count = data.coder_workspace.me.start_count + name = local.vm_name + image = data.coder_parameter.image.value + type = data.coder_parameter.instance_type.value + profiles = var.remote_profiles + project = var.remote_project + running = true + config = { + "limits.cpu" = tostring(data.coder_parameter.cpu_cores.value) + "limits.memory" = "${data.coder_parameter.memory_mb.value}MiB" + "cloud-init.user-data" = local.rendered_user_data + } + + device { + name = "root" + type = "disk" + properties = { + path = "/" + pool = var.remote_storage_pool + size = "${data.coder_parameter.disk_size_gb.value}GiB" + } + } + + device { + name = "eth-1" + type = "nic" + properties = { + network = var.remote_network + } + } +} + +module "code-server" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/code-server/coder" + version = "1.3.1" + agent_id = coder_agent.dev.id +} \ No newline at end of file