From 866765ac67d557b733d4a349f8522583d1e74bac Mon Sep 17 00:00:00 2001 From: Muhammad Umair Ali Date: Sat, 23 Aug 2025 17:21:37 +0500 Subject: [PATCH 01/11] trigger redeploy with updated CORS_ORIGIN From 9c6c0e1b0869cbdcbf10e988b5bf1fae2e94115e Mon Sep 17 00:00:00 2001 From: Muhammad Umair Ali Date: Sat, 23 Aug 2025 17:27:03 +0500 Subject: [PATCH 02/11] feat(linode-vm): add new Linode VM template and configuration --- registry/umair/templates/linode-vm/README.md | 47 ++ .../cloud-init/cloud-config.yaml.tftpl | 55 +++ registry/umair/templates/linode-vm/main.tf | 420 ++++++++++++++++++ 3 files changed, 522 insertions(+) create mode 100644 registry/umair/templates/linode-vm/README.md create mode 100644 registry/umair/templates/linode-vm/cloud-init/cloud-config.yaml.tftpl create mode 100644 registry/umair/templates/linode-vm/main.tf diff --git a/registry/umair/templates/linode-vm/README.md b/registry/umair/templates/linode-vm/README.md new file mode 100644 index 000000000..f05189724 --- /dev/null +++ b/registry/umair/templates/linode-vm/README.md @@ -0,0 +1,47 @@ +--- +display_name: Linode Instance (Linux) +description: Provision Linode instances as Coder workspaces +icon: ../../../../.icons/akamai.svg +verified: false +tags: [vm, linux, linode] +--- + +# Remote Development on Linode Instances + +Provision Linode instances as [Coder workspaces](https://coder.com/docs/workspaces) with this example template. + + + +## Prerequisites + +To deploy workspaces as Linode instances, you'll need: + +- Linode [personal access token (PAT)](https://www.linode.com/docs/products/tools/api/guides/manage-api-tokens/) + +### Authentication + +This template assumes that the Coder Provisioner is run in an environment that is authenticated with Linode. + +Obtain a [Linode Personal Access Token](https://cloud.linode.com/profile/tokens) and set the `linode_token` variable when deploying the template. +For other ways to authenticate [consult the Terraform provider's docs](https://registry.terraform.io/providers/linode/linode/latest/docs). + +## Features + +- **Multiple Instance Types**: From Nanode 1GB to 32GB configurations +- **Comprehensive OS Support**: Ubuntu, Debian, CentOS, Fedora, AlmaLinux, Rocky Linux +- **Global Regions**: 32 Linode regions across North America, Europe, Asia-Pacific, South America, and Australia +- **Persistent Storage**: Configurable volumes (10GB-1TB) that persist across workspace restarts +- **Development Tools**: Pre-configured with VS Code Server +- **Monitoring**: Built-in CPU, memory, and disk usage monitoring + +## Architecture + +This template provisions the following resources: + +- Linode instance (ephemeral, deleted on stop) +- Linode volume (persistent, mounted to `/home/coder`) + +This means, when the workspace restarts, any tools or files outside of the home directory are not persisted. To pre-bake tools into the workspace (e.g. `python3`), modify the VM image, or use a [startup script](https://registry.terraform.io/providers/coder/coder/latest/docs/resources/script). + +> [!NOTE] +> This template is designed to be a starting point! Edit the Terraform to extend the template to support your use case. diff --git a/registry/umair/templates/linode-vm/cloud-init/cloud-config.yaml.tftpl b/registry/umair/templates/linode-vm/cloud-init/cloud-config.yaml.tftpl new file mode 100644 index 000000000..993bcb4b9 --- /dev/null +++ b/registry/umair/templates/linode-vm/cloud-init/cloud-config.yaml.tftpl @@ -0,0 +1,55 @@ +#cloud-config +users: + - name: ${username} + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + groups: sudo + shell: /bin/bash +packages: + - git + - curl + - wget + - unzip +disk_setup: + /dev/sdb: + table_type: 'gpt' + layout: true + overwrite: false +fs_setup: + - label: ${home_volume_label} + filesystem: ext4 + device: /dev/sdb + partition: auto +mounts: + - ["/dev/sdb", "/home/${username}", "ext4", "defaults,uid=1000,gid=1000", "0", "2"] +write_files: + - path: /opt/coder/init + permissions: "0755" + encoding: b64 + content: ${init_script} + - path: /etc/systemd/system/coder-agent.service + permissions: "0644" + content: | + [Unit] + Description=Coder Agent + After=network-online.target + Wants=network-online.target + + [Service] + User=${username} + ExecStart=/opt/coder/init + Environment=CODER_AGENT_TOKEN=${coder_agent_token} + Restart=always + RestartSec=10 + TimeoutStopSec=90 + KillMode=process + + OOMScoreAdjust=-1000 + SyslogIdentifier=coder-agent + + [Install] + WantedBy=multi-user.target +runcmd: + - mkdir -p /home/${username} + - chown ${username}:${username} /home/${username} + - systemctl enable coder-agent + - systemctl start coder-agent diff --git a/registry/umair/templates/linode-vm/main.tf b/registry/umair/templates/linode-vm/main.tf new file mode 100644 index 000000000..8fe2725a5 --- /dev/null +++ b/registry/umair/templates/linode-vm/main.tf @@ -0,0 +1,420 @@ +terraform { + required_providers { + coder = { + source = "coder/coder" + } + linode = { + source = "linode/linode" + } + } +} + +provider "coder" {} + +# Variable for Linode API token +variable "linode_token" { + description = "Linode API token for authentication" + type = string + sensitive = true +} + +# Configure the Linode Provider +provider "linode" { + token = var.linode_token +} + +data "coder_workspace" "me" {} +data "coder_workspace_owner" "me" {} + +resource "coder_agent" "main" { + os = "linux" + arch = "amd64" + + metadata { + key = "cpu" + display_name = "CPU Usage" + interval = 5 + timeout = 5 + script = "coder stat cpu" + } + metadata { + key = "memory" + display_name = "Memory Usage" + interval = 5 + timeout = 5 + script = "coder stat mem" + } + metadata { + key = "home" + display_name = "Home Usage" + interval = 600 # every 10 minutes + timeout = 30 # df can take a while on large filesystems + script = "coder stat disk --path /home/${lower(data.coder_workspace_owner.me.name)}" + } +} + +# See https://registry.coder.com/modules/coder/code-server +module "code-server" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/code-server/coder" + version = "~> 1.0" + + agent_id = coder_agent.main.id + order = 1 +} + +data "coder_parameter" "region" { + name = "region" + display_name = "Region" + description = "This is the region where your workspace will be created." + icon = "/emojis/1f30e.png" + type = "string" + default = "us-east" + mutable = false + + option { + name = "Newark, NJ (US East)" + value = "us-east" + icon = "/emojis/1f1fa-1f1f8.png" + } + option { + name = "Washington, DC (US East)" + value = "us-iad" + icon = "/emojis/1f1fa-1f1f8.png" + } + option { + name = "Fremont, CA (US West)" + value = "us-west" + icon = "/emojis/1f1fa-1f1f8.png" + } + option { + name = "Los Angeles, CA (US West)" + value = "us-lax" + icon = "/emojis/1f1fa-1f1f8.png" + } + option { + name = "Dallas, TX (US Central)" + value = "us-central" + icon = "/emojis/1f1fa-1f1f8.png" + } + option { + name = "Chicago, IL (US Central)" + value = "us-ord" + icon = "/emojis/1f1fa-1f1f8.png" + } + option { + name = "Atlanta, GA (US Southeast)" + value = "us-southeast" + icon = "/emojis/1f1fa-1f1f8.png" + } + option { + name = "Miami, FL (US Southeast)" + value = "us-mia" + icon = "/emojis/1f1fa-1f1f8.png" + } + option { + name = "Seattle, WA (US West)" + value = "us-sea" + icon = "/emojis/1f1fa-1f1f8.png" + } + option { + name = "Toronto, CA" + value = "ca-central" + icon = "/emojis/1f1e8-1f1e6.png" + } + option { + name = "London, UK" + value = "eu-west" + icon = "/emojis/1f1ec-1f1e7.png" + } + option { + name = "London 2, UK" + value = "gb-lon" + icon = "/emojis/1f1ec-1f1e7.png" + } + option { + name = "Frankfurt, DE" + value = "eu-central" + icon = "/emojis/1f1e9-1f1ea.png" + } + option { + name = "Frankfurt 2, DE" + value = "de-fra-2" + icon = "/emojis/1f1e9-1f1ea.png" + } + option { + name = "Paris, FR" + value = "fr-par" + icon = "/emojis/1f1eb-1f1f7.png" + } + option { + name = "Amsterdam, NL" + value = "nl-ams" + icon = "/emojis/1f1f3-1f1f1.png" + } + option { + name = "Stockholm, SE" + value = "se-sto" + icon = "/emojis/1f1f8-1f1ea.png" + } + option { + name = "Madrid, ES" + value = "es-mad" + icon = "/emojis/1f1ea-1f1f8.png" + } + option { + name = "Milan, IT" + value = "it-mil" + icon = "/emojis/1f1ee-1f1f9.png" + } + option { + name = "Singapore, SG" + value = "ap-south" + icon = "/emojis/1f1f8-1f1ec.png" + } + option { + name = "Singapore 2, SG" + value = "sg-sin-2" + icon = "/emojis/1f1f8-1f1ec.png" + } + option { + name = "Tokyo 2, JP" + value = "ap-northeast" + icon = "/emojis/1f1ef-1f1f5.png" + } + option { + name = "Tokyo 3, JP" + value = "jp-tyo-3" + icon = "/emojis/1f1ef-1f1f5.png" + } + option { + name = "Osaka, JP" + value = "jp-osa" + icon = "/emojis/1f1ef-1f1f5.png" + } + option { + name = "Sydney, AU" + value = "ap-southeast" + icon = "/emojis/1f1e6-1f1fa.png" + } + option { + name = "Melbourne, AU" + value = "au-mel" + icon = "/emojis/1f1e6-1f1fa.png" + } + option { + name = "Mumbai, IN" + value = "ap-west" + icon = "/emojis/1f1ee-1f1f3.png" + } + option { + name = "Mumbai 2, IN" + value = "in-bom-2" + icon = "/emojis/1f1ee-1f1f3.png" + } + option { + name = "Chennai, IN" + value = "in-maa" + icon = "/emojis/1f1ee-1f1f3.png" + } + option { + name = "Jakarta, ID" + value = "id-cgk" + icon = "/emojis/1f1ee-1f1f3.png" + } + option { + name = "Sao Paulo, BR" + value = "br-gru" + icon = "/emojis/1f1e7-1f1f7.png" + } +} + +data "coder_parameter" "instance_type" { + name = "instance_type" + display_name = "Instance Type" + description = "Which Linode instance type would you like to use?" + default = "g6-nanode-1" + type = "string" + icon = "/icon/memory.svg" + mutable = false + + option { + name = "Nanode 1GB (1 vCPU, 1 GB RAM)" + value = "g6-nanode-1" + } + option { + name = "Linode 2GB (1 vCPU, 2 GB RAM)" + value = "g6-standard-1" + } + option { + name = "Linode 4GB (2 vCPU, 4 GB RAM)" + value = "g6-standard-2" + } + option { + name = "Linode 8GB (4 vCPU, 8 GB RAM)" + value = "g6-standard-4" + } + option { + name = "Linode 16GB (6 vCPU, 16 GB RAM)" + value = "g6-standard-6" + } + option { + name = "Linode 32GB (8 vCPU, 32 GB RAM)" + value = "g6-standard-8" + } +} + +data "coder_parameter" "instance_image" { + name = "instance_image" + display_name = "Instance Image" + description = "Which Linode image would you like to use?" + default = "linode/ubuntu22.04" + type = "string" + mutable = false + + option { + name = "Ubuntu 22.04 LTS" + value = "linode/ubuntu22.04" + icon = "/icon/ubuntu.svg" + } + option { + name = "Ubuntu 20.04 LTS" + value = "linode/ubuntu20.04" + icon = "/icon/ubuntu.svg" + } + option { + name = "Debian 12" + value = "linode/debian12" + icon = "/icon/debian.svg" + } + option { + name = "Debian 11" + value = "linode/debian11" + icon = "/icon/debian.svg" + } + option { + name = "CentOS Stream 9" + value = "linode/centos-stream9" + icon = "/icon/centos.svg" + } + option { + name = "Fedora 39" + value = "linode/fedora39" + icon = "/icon/fedora.svg" + } + option { + name = "Fedora 38" + value = "linode/fedora38" + icon = "/icon/fedora.svg" + } + option { + name = "AlmaLinux 9" + value = "linode/almalinux9" + icon = "/icon/almalinux.svg" + } + option { + name = "Rocky Linux 9" + value = "linode/rocky9" + icon = "/icon/rockylinux.svg" + } +} + +data "coder_parameter" "home_volume_size" { + name = "home_volume_size" + display_name = "Home Volume Size" + description = "How large would you like your home volume to be (in GB)?" + type = "number" + default = 20 + mutable = false + + validation { + min = 10 + max = 1024 + } +} + +resource "linode_volume" "home_volume" { + label = "coder-${substr(data.coder_workspace.me.id, 0, 8)}-home" + size = data.coder_parameter.home_volume_size.value + region = data.coder_parameter.region.value + + # Protect the volume from being deleted due to changes in attributes. + lifecycle { + ignore_changes = all + } +} + +resource "linode_instance" "workspace" { + count = data.coder_workspace.me.start_count + label = "coder-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}" + region = data.coder_parameter.region.value + type = data.coder_parameter.instance_type.value + + private_ip = true + + metadata { + user_data = base64encode(templatefile("cloud-init/cloud-config.yaml.tftpl", { + username = lower(data.coder_workspace_owner.me.name) + home_volume_label = linode_volume.home_volume.label + init_script = base64encode(coder_agent.main.init_script) + coder_agent_token = coder_agent.main.token + })) + } +} + +# Create boot disk +resource "linode_instance_disk" "boot_disk" { + count = data.coder_workspace.me.start_count + label = "boot" + linode_id = linode_instance.workspace[0].id + size = 25000 # 25GB boot disk + image = data.coder_parameter.instance_image.value +} + +# Create instance configuration with volume attached +resource "linode_instance_config" "boot_config" { + count = data.coder_workspace.me.start_count + label = "boot_config" + linode_id = linode_instance.workspace[0].id + + device { + device_name = "sda" + disk_id = linode_instance_disk.boot_disk[0].id + } + + device { + device_name = "sdb" + volume_id = linode_volume.home_volume.id + } + + root_device = "/dev/sda" + kernel = "linode/latest-64bit" + booted = true +} + +resource "coder_metadata" "workspace-info" { + count = data.coder_workspace.me.start_count + resource_id = linode_instance.workspace[0].id + + item { + key = "region" + value = linode_instance.workspace[0].region + } + item { + key = "type" + value = linode_instance.workspace[0].type + } + item { + key = "ipv4" + value = tolist(linode_instance.workspace[0].ipv4)[0] + } +} + +resource "coder_metadata" "volume-info" { + resource_id = linode_volume.home_volume.id + + item { + key = "size" + value = "${linode_volume.home_volume.size} GB" + } +} From 3edc5924ee58a3a9daf24ef313e23295c837c7da Mon Sep 17 00:00:00 2001 From: Muhammad Umair Ali Date: Sat, 23 Aug 2025 19:54:16 +0500 Subject: [PATCH 03/11] feat(linode-vm): added some optimization --- .../cloud-init/cloud-config.yaml.tftpl | 1 + registry/umair/templates/linode-vm/main.tf | 81 +++++++------------ 2 files changed, 30 insertions(+), 52 deletions(-) diff --git a/registry/umair/templates/linode-vm/cloud-init/cloud-config.yaml.tftpl b/registry/umair/templates/linode-vm/cloud-init/cloud-config.yaml.tftpl index 993bcb4b9..6145633d2 100644 --- a/registry/umair/templates/linode-vm/cloud-init/cloud-config.yaml.tftpl +++ b/registry/umair/templates/linode-vm/cloud-init/cloud-config.yaml.tftpl @@ -1,4 +1,5 @@ #cloud-config +hostname: ${hostname} users: - name: ${username} sudo: ["ALL=(ALL) NOPASSWD:ALL"] diff --git a/registry/umair/templates/linode-vm/main.tf b/registry/umair/templates/linode-vm/main.tf index 8fe2725a5..826809bec 100644 --- a/registry/umair/templates/linode-vm/main.tf +++ b/registry/umair/templates/linode-vm/main.tf @@ -16,11 +16,12 @@ variable "linode_token" { description = "Linode API token for authentication" type = string sensitive = true + default = "" } # Configure the Linode Provider provider "linode" { - token = var.linode_token + token = var.linode_token != "" ? var.linode_token : null } data "coder_workspace" "me" {} @@ -53,6 +54,12 @@ resource "coder_agent" "main" { } } +locals { + vm_name = "coder-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}" + root_disk_label = substr("${local.vm_name}-root", 0, 32) + home_volume_label = substr("${local.vm_name}-home", 0, 32) +} + # See https://registry.coder.com/modules/coder/code-server module "code-server" { count = data.coder_workspace.me.start_count @@ -268,43 +275,23 @@ data "coder_parameter" "instance_image" { name = "instance_image" display_name = "Instance Image" description = "Which Linode image would you like to use?" - default = "linode/ubuntu22.04" + default = "linode/ubuntu24.04" type = "string" mutable = false option { - name = "Ubuntu 22.04 LTS" - value = "linode/ubuntu22.04" - icon = "/icon/ubuntu.svg" - } - option { - name = "Ubuntu 20.04 LTS" - value = "linode/ubuntu20.04" + name = "Ubuntu 24.04 LTS" + value = "linode/ubuntu24.04" icon = "/icon/ubuntu.svg" } option { - name = "Debian 12" - value = "linode/debian12" + name = "Debian 13" + value = "linode/debian13" icon = "/icon/debian.svg" } option { - name = "Debian 11" - value = "linode/debian11" - icon = "/icon/debian.svg" - } - option { - name = "CentOS Stream 9" - value = "linode/centos-stream9" - icon = "/icon/centos.svg" - } - option { - name = "Fedora 39" - value = "linode/fedora39" - icon = "/icon/fedora.svg" - } - option { - name = "Fedora 38" - value = "linode/fedora38" + name = "Fedora 42" + value = "linode/fedora42" icon = "/icon/fedora.svg" } option { @@ -321,20 +308,21 @@ data "coder_parameter" "instance_image" { data "coder_parameter" "home_volume_size" { name = "home_volume_size" - display_name = "Home Volume Size" + display_name = "Home Volume Size (GB)" description = "How large would you like your home volume to be (in GB)?" type = "number" default = 20 - mutable = false + mutable = true validation { - min = 10 - max = 1024 + min = 10 + max = 1024 + monotonic = "increasing" } } resource "linode_volume" "home_volume" { - label = "coder-${substr(data.coder_workspace.me.id, 0, 8)}-home" + label = local.home_volume_label size = data.coder_parameter.home_volume_size.value region = data.coder_parameter.region.value @@ -346,7 +334,7 @@ resource "linode_volume" "home_volume" { resource "linode_instance" "workspace" { count = data.coder_workspace.me.start_count - label = "coder-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}" + label = local.vm_name region = data.coder_parameter.region.value type = data.coder_parameter.instance_type.value @@ -354,16 +342,18 @@ resource "linode_instance" "workspace" { metadata { user_data = base64encode(templatefile("cloud-init/cloud-config.yaml.tftpl", { + hostname = local.vm_name username = lower(data.coder_workspace_owner.me.name) home_volume_label = linode_volume.home_volume.label init_script = base64encode(coder_agent.main.init_script) coder_agent_token = coder_agent.main.token })) } + tags = ["coder", "workspace", lower(data.coder_workspace_owner.me.name), lower(data.coder_workspace.me.name)] } -# Create boot disk -resource "linode_instance_disk" "boot_disk" { +# Create root disk +resource "linode_instance_disk" "root" { count = data.coder_workspace.me.start_count label = "boot" linode_id = linode_instance.workspace[0].id @@ -372,14 +362,14 @@ resource "linode_instance_disk" "boot_disk" { } # Create instance configuration with volume attached -resource "linode_instance_config" "boot_config" { +resource "linode_instance_config" "workspace" { count = data.coder_workspace.me.start_count - label = "boot_config" + label = "${local.vm_name}-config" linode_id = linode_instance.workspace[0].id device { device_name = "sda" - disk_id = linode_instance_disk.boot_disk[0].id + disk_id = linode_instance_disk.root[0].id } device { @@ -404,17 +394,4 @@ resource "coder_metadata" "workspace-info" { key = "type" value = linode_instance.workspace[0].type } - item { - key = "ipv4" - value = tolist(linode_instance.workspace[0].ipv4)[0] - } -} - -resource "coder_metadata" "volume-info" { - resource_id = linode_volume.home_volume.id - - item { - key = "size" - value = "${linode_volume.home_volume.size} GB" - } } From eac45555a0706ca89fdcae5e307f4c830e6ced36 Mon Sep 17 00:00:00 2001 From: Muhammad Umair Ali Date: Sat, 23 Aug 2025 22:24:09 +0500 Subject: [PATCH 04/11] add tyo = tyo # For Tokyo location code in typos.toml --- .github/typos.toml | 1 + .icons/akamai.svg | 37 +++++++++++++++++++ .../modules/digitalocean-region/main.test.ts | 0 3 files changed, 38 insertions(+) create mode 100644 .icons/akamai.svg create mode 100644 registry/umair/modules/digitalocean-region/main.test.ts diff --git a/.github/typos.toml b/.github/typos.toml index f27257a23..42252aca6 100644 --- a/.github/typos.toml +++ b/.github/typos.toml @@ -1,5 +1,6 @@ [default.extend-words] muc = "muc" # For Munich location code +tyo = "tyo" # For Tokyo location code Hashi = "Hashi" HashiCorp = "HashiCorp" diff --git a/.icons/akamai.svg b/.icons/akamai.svg new file mode 100644 index 000000000..20de7e04f --- /dev/null +++ b/.icons/akamai.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + diff --git a/registry/umair/modules/digitalocean-region/main.test.ts b/registry/umair/modules/digitalocean-region/main.test.ts new file mode 100644 index 000000000..e69de29bb From 0a1a8e8fcc4cc993e476d921cf7a807d65f2fba9 Mon Sep 17 00:00:00 2001 From: Muhammad Umair Ali Date: Sat, 23 Aug 2025 22:32:34 +0500 Subject: [PATCH 05/11] fix ci --- .../modules/digitalocean-region/README.md | 87 ++++++++ .../modules/digitalocean-region/main.test.ts | 46 +++++ .../umair/modules/digitalocean-region/main.tf | 187 ++++++++++++++++++ 3 files changed, 320 insertions(+) create mode 100644 registry/umair/modules/digitalocean-region/README.md create mode 100644 registry/umair/modules/digitalocean-region/main.tf diff --git a/registry/umair/modules/digitalocean-region/README.md b/registry/umair/modules/digitalocean-region/README.md new file mode 100644 index 000000000..dca6d2997 --- /dev/null +++ b/registry/umair/modules/digitalocean-region/README.md @@ -0,0 +1,87 @@ +--- +display_name: DigitalOcean Region +description: A parameter with human region names and icons +icon: ../../../../.icons/digital-ocean.svg +verified: true +tags: [helper, parameter, digitalocean, regions] +--- + +# DigitalOcean Region + +This module adds DigitalOcean regions to your Coder template with automatic GPU filtering. You can customize display names and icons using the `custom_names` and `custom_icons` arguments. + +The simplest usage is: + +```tf +module "digitalocean-region" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/digitalocean-region/coder" + version = "1.0.0" + default = "ams3" +} +``` + +## Examples + +### Basic usage + +```tf +module "digitalocean-region" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/digitalocean-region/coder" + version = "1.0.0" +} +``` + +### With custom configuration + +```tf +module "digitalocean-region" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/digitalocean-region/coder" + version = "1.0.0" + default = "ams3" + mutable = true + + custom_icons = { + "ams3" = "/emojis/1f1f3-1f1f1.png" + } + + custom_names = { + "ams3" = "Europe - Amsterdam (Primary)" + } +} +``` + +### GPU-only toggle (internal parameter) + +This module automatically exposes a "GPU-only regions" checkbox in the template UI. When checked, it shows only GPU-capable regions and auto-selects the first one. When unchecked, it shows all available regions. + +## Available Regions + +Refer to DigitalOcean’s official availability matrix for the most up-to-date information. + +- GPU availability: currently only in `nyc2` and `tor1` (per DO docs). Others are non-GPU. +- See: https://docs.digitalocean.com/platform/regional-availability/ + +### All datacenters (GPU status) + +- `nyc2` - New York, United States (Legacy) - **GPU available** +- `tor1` - Toronto, Canada - **GPU available** +- `nyc3` - New York, United States +- `ams3` - Amsterdam, Netherlands +- `sfo3` - San Francisco, United States +- `sgp1` - Singapore +- `lon1` - London, United Kingdom +- `fra1` - Frankfurt, Germany +- `blr1` - Bangalore, India +- `syd1` - Sydney, Australia +- `atl1` - Atlanta, United States +- `nyc1` - New York, United States (Legacy) +- `sfo2` - San Francisco, United States (Legacy) +- `sfo1` - San Francisco, United States (Legacy) +- `ams2` - Amsterdam, Netherlands (Legacy) + +## Associated template + +Also see the Coder template registry for a [DigitalOcean Droplet template](https://registry.coder.com/templates/digitalocean-droplet) that provisions workspaces as DigitalOcean Droplets. \ No newline at end of file diff --git a/registry/umair/modules/digitalocean-region/main.test.ts b/registry/umair/modules/digitalocean-region/main.test.ts index e69de29bb..af30ff4e7 100644 --- a/registry/umair/modules/digitalocean-region/main.test.ts +++ b/registry/umair/modules/digitalocean-region/main.test.ts @@ -0,0 +1,46 @@ +import { describe, expect, it } from "bun:test"; +import { + runTerraformApply, + runTerraformInit, + testRequiredVariables, +} from "~test"; + +describe("digitalocean-region", async () => { + await runTerraformInit(import.meta.dir); + + testRequiredVariables(import.meta.dir, {}); + + + it("default output", async () => { + const state = await runTerraformApply(import.meta.dir, {}); + expect(state.outputs.value.value).toBe("ams2"); + }); + + it("customized default", async () => { + const state = await runTerraformApply(import.meta.dir, { + regions: '["nyc1","ams3"]', + default: "ams3", + }); + expect(state.outputs.value.value).toBe("ams3"); + }); + + it("gpu only invalid default", async () => { + const state = await runTerraformApply(import.meta.dir, { + regions: '["nyc1"]', + default: "nyc1", + gpu_only: "true", + }); + expect(state.outputs.value.value).toBe("nyc1"); + }); + + it("gpu only valid default", async () => { + const state = await runTerraformApply(import.meta.dir, { + regions: '["tor1"]', + default: "tor1", + gpu_only: "true", + }); + expect(state.outputs.value.value).toBe("tor1"); + }); + + // Add more tests as needed for coder_parameter_order or other features +}); \ No newline at end of file diff --git a/registry/umair/modules/digitalocean-region/main.tf b/registry/umair/modules/digitalocean-region/main.tf new file mode 100644 index 000000000..7e8ae4079 --- /dev/null +++ b/registry/umair/modules/digitalocean-region/main.tf @@ -0,0 +1,187 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + coder = { + source = "coder/coder" + version = ">= 0.11" + } + } +} + +variable "display_name" { + default = "DigitalOcean Region" + description = "The display name of the parameter." + type = string +} + +variable "description" { + default = "The region to deploy workspace infrastructure." + description = "The description of the parameter." + type = string +} + +variable "default" { + default = null + description = "Default region" + type = string +} + + + +variable "mutable" { + default = false + description = "Whether the parameter can be changed after creation." + type = bool +} + +variable "custom_names" { + default = {} + description = "A map of custom display names for region IDs." + type = map(string) +} + +variable "custom_icons" { + default = {} + description = "A map of custom icons for region IDs." + type = map(string) +} + +variable "single_zone_per_region" { + default = true + description = "Whether to only include a single zone per region." + type = bool +} + +variable "coder_parameter_order" { + type = number + description = "The order determines the position of a template parameter in the UI/CLI presentation. The lowest order is shown first and parameters with equal order are sorted by name (ascending order)." + default = null +} + +data "coder_parameter" "gpu_only" { + name = "digitalocean_gpu_only" + display_name = "GPU-only regions" + description = "Show only regions with GPUs" + type = "bool" + form_type = "checkbox" + default = false + mutable = var.mutable + order = var.coder_parameter_order +} + +locals { + zones = { + # Active datacenters (recommended for new workloads) + "nyc1" = { + gpu = false + name = "New York City, USA (NYC1)" + icon = "/emojis/1f1fa-1f1f8.png" + } + "nyc3" = { + gpu = false + name = "New York City, USA (NYC3)" + icon = "/emojis/1f1fa-1f1f8.png" + } + "ams3" = { + gpu = false + name = "Amsterdam, Netherlands" + icon = "/emojis/1f1f3-1f1f1.png" + } + "sfo3" = { + gpu = false + name = "San Francisco, USA" + icon = "/emojis/1f1fa-1f1f8.png" + } + "sgp1" = { + gpu = false + name = "Singapore" + icon = "/emojis/1f1f8-1f1ec.png" + } + "lon1" = { + gpu = false + name = "London, United Kingdom" + icon = "/emojis/1f1ec-1f1e7.png" + } + "fra1" = { + gpu = false + name = "Frankfurt, Germany" + icon = "/emojis/1f1e9-1f1ea.png" + } + "tor1" = { + gpu = true + name = "Toronto, Canada" + icon = "/emojis/1f1e8-1f1e6.png" + } + "blr1" = { + gpu = false + name = "Bangalore, India" + icon = "/emojis/1f1ee-1f1f3.png" + } + "syd1" = { + gpu = false + name = "Sydney, Australia" + icon = "/emojis/1f1e6-1f1fa.png" + } + "atl1" = { + gpu = false + name = "Atlanta, USA" + icon = "/emojis/1f1fa-1f1f8.png" + } + # Legacy/Restricted datacenters (not recommended for new workloads) + "nyc2" = { + gpu = true # GPU available but restricted to existing users + name = "New York City, USA (Legacy)" + icon = "/emojis/1f1fa-1f1f8.png" + } + "sfo2" = { + gpu = false # No GPU available per current regional availability + name = "San Francisco, USA (Legacy SFO2)" + icon = "/emojis/1f1fa-1f1f8.png" + } + "sfo1" = { + gpu = false # No GPU in legacy datacenter + name = "San Francisco, USA (Legacy SFO1)" + icon = "/emojis/1f1fa-1f1f8.png" + } + "ams2" = { + gpu = false # No GPU in legacy datacenter + name = "Amsterdam, Netherlands (Legacy)" + icon = "/emojis/1f1f3-1f1f1.png" + } + } +} + +locals { + allowed_regions = data.coder_parameter.gpu_only.value ? [for k, v in local.zones : k if v.gpu] : keys(local.zones) + default_region = data.coder_parameter.gpu_only.value ? (length([for k, v in local.zones : k if v.gpu]) > 0 ? [for k, v in local.zones : k if v.gpu][0] : null) : (var.default != null && var.default != "" ? var.default : keys(local.zones)[0]) +} + +data "coder_parameter" "region" { + name = "digitalocean_region" + display_name = var.display_name + description = var.description + icon = "/icon/digital-ocean.svg" + mutable = var.mutable + form_type = "radio" + default = local.default_region + order = var.coder_parameter_order + dynamic "option" { + for_each = { + for k, v in local.zones : k => v + if contains(local.allowed_regions, k) + } + content { + icon = try(var.custom_icons[option.key], option.value.icon) + name = try(var.custom_names[option.key], option.value.name) + description = option.key + value = option.key + } + } + + +} +output "value" { + description = "DigitalOcean region identifier." + value = data.coder_parameter.region.value +} \ No newline at end of file From 3459987e0437044a81e283ca2aa6fea4bbaf76eb Mon Sep 17 00:00:00 2001 From: Muhammad Umair Ali Date: Sat, 23 Aug 2025 22:35:26 +0500 Subject: [PATCH 06/11] fix: fmt:ci --- registry/umair/modules/digitalocean-region/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/umair/modules/digitalocean-region/README.md b/registry/umair/modules/digitalocean-region/README.md index dca6d2997..0a919b51d 100644 --- a/registry/umair/modules/digitalocean-region/README.md +++ b/registry/umair/modules/digitalocean-region/README.md @@ -84,4 +84,4 @@ Refer to DigitalOcean’s official availability matrix for the most up-to-date i ## Associated template -Also see the Coder template registry for a [DigitalOcean Droplet template](https://registry.coder.com/templates/digitalocean-droplet) that provisions workspaces as DigitalOcean Droplets. \ No newline at end of file +Also see the Coder template registry for a [DigitalOcean Droplet template](https://registry.coder.com/templates/digitalocean-droplet) that provisions workspaces as DigitalOcean Droplets. From e7bfda6619dc29c2d6c230125071493af5e499cd Mon Sep 17 00:00:00 2001 From: Muhammad Umair Ali Date: Mon, 25 Aug 2025 17:46:00 +0500 Subject: [PATCH 07/11] clean Pr --- .../modules/digitalocean-region/README.md | 87 -------- .../modules/digitalocean-region/main.test.ts | 46 ----- .../umair/modules/digitalocean-region/main.tf | 187 ------------------ 3 files changed, 320 deletions(-) delete mode 100644 registry/umair/modules/digitalocean-region/README.md delete mode 100644 registry/umair/modules/digitalocean-region/main.test.ts delete mode 100644 registry/umair/modules/digitalocean-region/main.tf diff --git a/registry/umair/modules/digitalocean-region/README.md b/registry/umair/modules/digitalocean-region/README.md deleted file mode 100644 index 0a919b51d..000000000 --- a/registry/umair/modules/digitalocean-region/README.md +++ /dev/null @@ -1,87 +0,0 @@ ---- -display_name: DigitalOcean Region -description: A parameter with human region names and icons -icon: ../../../../.icons/digital-ocean.svg -verified: true -tags: [helper, parameter, digitalocean, regions] ---- - -# DigitalOcean Region - -This module adds DigitalOcean regions to your Coder template with automatic GPU filtering. You can customize display names and icons using the `custom_names` and `custom_icons` arguments. - -The simplest usage is: - -```tf -module "digitalocean-region" { - count = data.coder_workspace.me.start_count - source = "registry.coder.com/coder/digitalocean-region/coder" - version = "1.0.0" - default = "ams3" -} -``` - -## Examples - -### Basic usage - -```tf -module "digitalocean-region" { - count = data.coder_workspace.me.start_count - source = "registry.coder.com/coder/digitalocean-region/coder" - version = "1.0.0" -} -``` - -### With custom configuration - -```tf -module "digitalocean-region" { - count = data.coder_workspace.me.start_count - source = "registry.coder.com/coder/digitalocean-region/coder" - version = "1.0.0" - default = "ams3" - mutable = true - - custom_icons = { - "ams3" = "/emojis/1f1f3-1f1f1.png" - } - - custom_names = { - "ams3" = "Europe - Amsterdam (Primary)" - } -} -``` - -### GPU-only toggle (internal parameter) - -This module automatically exposes a "GPU-only regions" checkbox in the template UI. When checked, it shows only GPU-capable regions and auto-selects the first one. When unchecked, it shows all available regions. - -## Available Regions - -Refer to DigitalOcean’s official availability matrix for the most up-to-date information. - -- GPU availability: currently only in `nyc2` and `tor1` (per DO docs). Others are non-GPU. -- See: https://docs.digitalocean.com/platform/regional-availability/ - -### All datacenters (GPU status) - -- `nyc2` - New York, United States (Legacy) - **GPU available** -- `tor1` - Toronto, Canada - **GPU available** -- `nyc3` - New York, United States -- `ams3` - Amsterdam, Netherlands -- `sfo3` - San Francisco, United States -- `sgp1` - Singapore -- `lon1` - London, United Kingdom -- `fra1` - Frankfurt, Germany -- `blr1` - Bangalore, India -- `syd1` - Sydney, Australia -- `atl1` - Atlanta, United States -- `nyc1` - New York, United States (Legacy) -- `sfo2` - San Francisco, United States (Legacy) -- `sfo1` - San Francisco, United States (Legacy) -- `ams2` - Amsterdam, Netherlands (Legacy) - -## Associated template - -Also see the Coder template registry for a [DigitalOcean Droplet template](https://registry.coder.com/templates/digitalocean-droplet) that provisions workspaces as DigitalOcean Droplets. diff --git a/registry/umair/modules/digitalocean-region/main.test.ts b/registry/umair/modules/digitalocean-region/main.test.ts deleted file mode 100644 index af30ff4e7..000000000 --- a/registry/umair/modules/digitalocean-region/main.test.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { describe, expect, it } from "bun:test"; -import { - runTerraformApply, - runTerraformInit, - testRequiredVariables, -} from "~test"; - -describe("digitalocean-region", async () => { - await runTerraformInit(import.meta.dir); - - testRequiredVariables(import.meta.dir, {}); - - - it("default output", async () => { - const state = await runTerraformApply(import.meta.dir, {}); - expect(state.outputs.value.value).toBe("ams2"); - }); - - it("customized default", async () => { - const state = await runTerraformApply(import.meta.dir, { - regions: '["nyc1","ams3"]', - default: "ams3", - }); - expect(state.outputs.value.value).toBe("ams3"); - }); - - it("gpu only invalid default", async () => { - const state = await runTerraformApply(import.meta.dir, { - regions: '["nyc1"]', - default: "nyc1", - gpu_only: "true", - }); - expect(state.outputs.value.value).toBe("nyc1"); - }); - - it("gpu only valid default", async () => { - const state = await runTerraformApply(import.meta.dir, { - regions: '["tor1"]', - default: "tor1", - gpu_only: "true", - }); - expect(state.outputs.value.value).toBe("tor1"); - }); - - // Add more tests as needed for coder_parameter_order or other features -}); \ No newline at end of file diff --git a/registry/umair/modules/digitalocean-region/main.tf b/registry/umair/modules/digitalocean-region/main.tf deleted file mode 100644 index 7e8ae4079..000000000 --- a/registry/umair/modules/digitalocean-region/main.tf +++ /dev/null @@ -1,187 +0,0 @@ -terraform { - required_version = ">= 1.0" - - required_providers { - coder = { - source = "coder/coder" - version = ">= 0.11" - } - } -} - -variable "display_name" { - default = "DigitalOcean Region" - description = "The display name of the parameter." - type = string -} - -variable "description" { - default = "The region to deploy workspace infrastructure." - description = "The description of the parameter." - type = string -} - -variable "default" { - default = null - description = "Default region" - type = string -} - - - -variable "mutable" { - default = false - description = "Whether the parameter can be changed after creation." - type = bool -} - -variable "custom_names" { - default = {} - description = "A map of custom display names for region IDs." - type = map(string) -} - -variable "custom_icons" { - default = {} - description = "A map of custom icons for region IDs." - type = map(string) -} - -variable "single_zone_per_region" { - default = true - description = "Whether to only include a single zone per region." - type = bool -} - -variable "coder_parameter_order" { - type = number - description = "The order determines the position of a template parameter in the UI/CLI presentation. The lowest order is shown first and parameters with equal order are sorted by name (ascending order)." - default = null -} - -data "coder_parameter" "gpu_only" { - name = "digitalocean_gpu_only" - display_name = "GPU-only regions" - description = "Show only regions with GPUs" - type = "bool" - form_type = "checkbox" - default = false - mutable = var.mutable - order = var.coder_parameter_order -} - -locals { - zones = { - # Active datacenters (recommended for new workloads) - "nyc1" = { - gpu = false - name = "New York City, USA (NYC1)" - icon = "/emojis/1f1fa-1f1f8.png" - } - "nyc3" = { - gpu = false - name = "New York City, USA (NYC3)" - icon = "/emojis/1f1fa-1f1f8.png" - } - "ams3" = { - gpu = false - name = "Amsterdam, Netherlands" - icon = "/emojis/1f1f3-1f1f1.png" - } - "sfo3" = { - gpu = false - name = "San Francisco, USA" - icon = "/emojis/1f1fa-1f1f8.png" - } - "sgp1" = { - gpu = false - name = "Singapore" - icon = "/emojis/1f1f8-1f1ec.png" - } - "lon1" = { - gpu = false - name = "London, United Kingdom" - icon = "/emojis/1f1ec-1f1e7.png" - } - "fra1" = { - gpu = false - name = "Frankfurt, Germany" - icon = "/emojis/1f1e9-1f1ea.png" - } - "tor1" = { - gpu = true - name = "Toronto, Canada" - icon = "/emojis/1f1e8-1f1e6.png" - } - "blr1" = { - gpu = false - name = "Bangalore, India" - icon = "/emojis/1f1ee-1f1f3.png" - } - "syd1" = { - gpu = false - name = "Sydney, Australia" - icon = "/emojis/1f1e6-1f1fa.png" - } - "atl1" = { - gpu = false - name = "Atlanta, USA" - icon = "/emojis/1f1fa-1f1f8.png" - } - # Legacy/Restricted datacenters (not recommended for new workloads) - "nyc2" = { - gpu = true # GPU available but restricted to existing users - name = "New York City, USA (Legacy)" - icon = "/emojis/1f1fa-1f1f8.png" - } - "sfo2" = { - gpu = false # No GPU available per current regional availability - name = "San Francisco, USA (Legacy SFO2)" - icon = "/emojis/1f1fa-1f1f8.png" - } - "sfo1" = { - gpu = false # No GPU in legacy datacenter - name = "San Francisco, USA (Legacy SFO1)" - icon = "/emojis/1f1fa-1f1f8.png" - } - "ams2" = { - gpu = false # No GPU in legacy datacenter - name = "Amsterdam, Netherlands (Legacy)" - icon = "/emojis/1f1f3-1f1f1.png" - } - } -} - -locals { - allowed_regions = data.coder_parameter.gpu_only.value ? [for k, v in local.zones : k if v.gpu] : keys(local.zones) - default_region = data.coder_parameter.gpu_only.value ? (length([for k, v in local.zones : k if v.gpu]) > 0 ? [for k, v in local.zones : k if v.gpu][0] : null) : (var.default != null && var.default != "" ? var.default : keys(local.zones)[0]) -} - -data "coder_parameter" "region" { - name = "digitalocean_region" - display_name = var.display_name - description = var.description - icon = "/icon/digital-ocean.svg" - mutable = var.mutable - form_type = "radio" - default = local.default_region - order = var.coder_parameter_order - dynamic "option" { - for_each = { - for k, v in local.zones : k => v - if contains(local.allowed_regions, k) - } - content { - icon = try(var.custom_icons[option.key], option.value.icon) - name = try(var.custom_names[option.key], option.value.name) - description = option.key - value = option.key - } - } - - -} -output "value" { - description = "DigitalOcean region identifier." - value = data.coder_parameter.region.value -} \ No newline at end of file From 0ee6423bcadcb6273209036c22928fa094816354 Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Tue, 26 Aug 2025 12:40:11 +0500 Subject: [PATCH 08/11] Update .icons/akamai.svg --- .icons/akamai.svg | 39 +++------------------------------------ 1 file changed, 3 insertions(+), 36 deletions(-) diff --git a/.icons/akamai.svg b/.icons/akamai.svg index 20de7e04f..4af3fe08b 100644 --- a/.icons/akamai.svg +++ b/.icons/akamai.svg @@ -1,37 +1,4 @@ - - - - - - - - - - - - - - - - - - + + Akamai + From 656f4766b1b57d3756bbce24508655724d5d30e2 Mon Sep 17 00:00:00 2001 From: m4rrypro <121078823+m4rrypro@users.noreply.github.com> Date: Tue, 26 Aug 2025 12:53:32 +0500 Subject: [PATCH 09/11] Update registry/umair/templates/linode-vm/README.md Co-authored-by: Atif Ali --- registry/umair/templates/linode-vm/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/umair/templates/linode-vm/README.md b/registry/umair/templates/linode-vm/README.md index f05189724..8ba19aa25 100644 --- a/registry/umair/templates/linode-vm/README.md +++ b/registry/umair/templates/linode-vm/README.md @@ -30,7 +30,7 @@ For other ways to authenticate [consult the Terraform provider's docs](https://r - **Multiple Instance Types**: From Nanode 1GB to 32GB configurations - **Comprehensive OS Support**: Ubuntu, Debian, CentOS, Fedora, AlmaLinux, Rocky Linux - **Global Regions**: 32 Linode regions across North America, Europe, Asia-Pacific, South America, and Australia -- **Persistent Storage**: Configurable volumes (10GB-1TB) that persist across workspace restarts +- **Persistent Storage**: Configurable volumes (10GB-1TB) that persist `$HOME` across workspace restarts - **Development Tools**: Pre-configured with VS Code Server - **Monitoring**: Built-in CPU, memory, and disk usage monitoring From 3b29e448c5794273560967c08758a0ca56b05b7f Mon Sep 17 00:00:00 2001 From: m4rrypro <121078823+m4rrypro@users.noreply.github.com> Date: Tue, 26 Aug 2025 12:53:44 +0500 Subject: [PATCH 10/11] Update registry/umair/templates/linode-vm/cloud-init/cloud-config.yaml.tftpl Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../templates/linode-vm/cloud-init/cloud-config.yaml.tftpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/umair/templates/linode-vm/cloud-init/cloud-config.yaml.tftpl b/registry/umair/templates/linode-vm/cloud-init/cloud-config.yaml.tftpl index 6145633d2..d432b0793 100644 --- a/registry/umair/templates/linode-vm/cloud-init/cloud-config.yaml.tftpl +++ b/registry/umair/templates/linode-vm/cloud-init/cloud-config.yaml.tftpl @@ -21,7 +21,7 @@ fs_setup: device: /dev/sdb partition: auto mounts: - - ["/dev/sdb", "/home/${username}", "ext4", "defaults,uid=1000,gid=1000", "0", "2"] + - ["/dev/sdb", "/home/${username}", "ext4", "defaults", "0", "2"] write_files: - path: /opt/coder/init permissions: "0755" From 7dca9f7acf40a8b2b6d4d46fc14069ddf74bef9b Mon Sep 17 00:00:00 2001 From: m4rrypro <121078823+m4rrypro@users.noreply.github.com> Date: Tue, 26 Aug 2025 17:28:59 +0500 Subject: [PATCH 11/11] Update registry/umair/templates/linode-vm/main.tf Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- registry/umair/templates/linode-vm/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/umair/templates/linode-vm/main.tf b/registry/umair/templates/linode-vm/main.tf index 826809bec..8462a6c46 100644 --- a/registry/umair/templates/linode-vm/main.tf +++ b/registry/umair/templates/linode-vm/main.tf @@ -227,7 +227,7 @@ data "coder_parameter" "region" { option { name = "Jakarta, ID" value = "id-cgk" - icon = "/emojis/1f1ee-1f1f3.png" + icon = "/emojis/1f1ee-1f1e9.png" } option { name = "Sao Paulo, BR"