-
Notifications
You must be signed in to change notification settings - Fork 40
create DigitalOcean region module #221
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,155 @@ | ||||||||||||||||
--- | ||||||||||||||||
display_name: DigitalOcean Region | ||||||||||||||||
description: Provides a region selection parameter for DigitalOcean resources | ||||||||||||||||
icon: ../../../../.icons/digitalocean.svg | ||||||||||||||||
maintainer_github: coder | ||||||||||||||||
verified: true | ||||||||||||||||
tags: [helper, digitalocean, region] | ||||||||||||||||
--- | ||||||||||||||||
|
||||||||||||||||
# DigitalOcean Region | ||||||||||||||||
|
||||||||||||||||
This module provides a standardized region selection parameter for DigitalOcean resources. It includes all volume-supporting regions with user-friendly names and country flag icons. | ||||||||||||||||
|
||||||||||||||||
## Features | ||||||||||||||||
|
||||||||||||||||
- **Volume Support**: Only includes regions that support DigitalOcean volumes | ||||||||||||||||
- **User-Friendly**: Display names with country/city information | ||||||||||||||||
- **Visual Icons**: Country flag emojis for each region | ||||||||||||||||
- **Configurable**: Customizable default region and parameter details | ||||||||||||||||
- **Consistent**: Standardized region selection across templates | ||||||||||||||||
|
||||||||||||||||
## Usage | ||||||||||||||||
|
||||||||||||||||
### Basic Usage | ||||||||||||||||
|
||||||||||||||||
```tf | ||||||||||||||||
module "region" { | ||||||||||||||||
source = "registry.coder.com/coder/digitalocean-region/coder" | ||||||||||||||||
version = "1.0.0" | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
resource "digitalocean_volume" "home_volume" { | ||||||||||||||||
region = module.region.value | ||||||||||||||||
# ... other configuration | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
resource "digitalocean_droplet" "workspace" { | ||||||||||||||||
region = module.region.value | ||||||||||||||||
# ... other configuration | ||||||||||||||||
} | ||||||||||||||||
``` | ||||||||||||||||
|
||||||||||||||||
### Customized Configuration | ||||||||||||||||
|
||||||||||||||||
```tf | ||||||||||||||||
module "region" { | ||||||||||||||||
source = "registry.coder.com/coder/digitalocean-region/coder" | ||||||||||||||||
version = "1.0.0" | ||||||||||||||||
|
||||||||||||||||
default = "sfo3" | ||||||||||||||||
mutable = true | ||||||||||||||||
display_name = "Datacenter Location" | ||||||||||||||||
description = "Select the datacenter region for your development environment" | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
resource "digitalocean_droplet" "workspace" { | ||||||||||||||||
region = module.region.value | ||||||||||||||||
# ... other configuration | ||||||||||||||||
} | ||||||||||||||||
``` | ||||||||||||||||
|
||||||||||||||||
## Available Regions | ||||||||||||||||
|
||||||||||||||||
The module includes the following DigitalOcean regions: | ||||||||||||||||
|
||||||||||||||||
| Region Code | Location | Notes | | ||||||||||||||||
| ----------- | ------------------------------ | ------------ | | ||||||||||||||||
| `tor1` | Canada (Toronto) | 🇨🇦 | | ||||||||||||||||
| `fra1` | Germany (Frankfurt) | 🇩🇪 | | ||||||||||||||||
| `blr1` | India (Bangalore) | 🇮🇳 | | ||||||||||||||||
| `ams3` | Netherlands (Amsterdam) | 🇳🇱 (Default) | | ||||||||||||||||
| `sgp1` | Singapore | 🇸🇬 | | ||||||||||||||||
| `lon1` | United Kingdom (London) | 🇬🇧 | | ||||||||||||||||
| `sfo2` | United States (California - 2) | 🇺🇸 | | ||||||||||||||||
| `sfo3` | United States (California - 3) | 🇺🇸 | | ||||||||||||||||
| `nyc1` | United States (New York - 1) | 🇺🇸 | | ||||||||||||||||
| `nyc3` | United States (New York - 3) | 🇺🇸 | | ||||||||||||||||
|
||||||||||||||||
> **Note**: Some regions (nyc1, sfo1, ams2) are excluded because they do not support volumes, which are commonly used for persistent data storage. | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we instead add an additional input to control getting only the regiosn with volumes? |
||||||||||||||||
|
||||||||||||||||
## Variables | ||||||||||||||||
|
||||||||||||||||
| Variable | Type | Default | Description | | ||||||||||||||||
| -------------- | -------- | ------------------------------------------------------------ | ---------------------------------------------------------- | | ||||||||||||||||
| `default` | `string` | `"ams3"` | The default region to select | | ||||||||||||||||
| `mutable` | `bool` | `false` | Whether the region can be changed after workspace creation | | ||||||||||||||||
| `name` | `string` | `"region"` | The name of the parameter | | ||||||||||||||||
| `display_name` | `string` | `"Region"` | The display name of the parameter | | ||||||||||||||||
| `description` | `string` | `"This is the region where your workspace will be created."` | The description of the parameter | | ||||||||||||||||
| `icon` | `string` | `"/emojis/1f30e.png"` | The icon to display for the parameter | | ||||||||||||||||
Comment on lines
+81
to
+90
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove this section as we already parse and render Variables on the registry frontend. |
||||||||||||||||
|
||||||||||||||||
## Outputs | ||||||||||||||||
|
||||||||||||||||
| Output | Type | Description | | ||||||||||||||||
| -------------- | -------- | ---------------------------------------- | | ||||||||||||||||
| `value` | `string` | The selected region value (e.g., "ams3") | | ||||||||||||||||
| `name` | `string` | The parameter name | | ||||||||||||||||
| `display_name` | `string` | The parameter display name | | ||||||||||||||||
|
||||||||||||||||
## Examples | ||||||||||||||||
|
||||||||||||||||
### With Custom Default Region | ||||||||||||||||
|
||||||||||||||||
```tf | ||||||||||||||||
module "region" { | ||||||||||||||||
source = "registry.coder.com/coder/digitalocean-region/coder" | ||||||||||||||||
version = "1.0.0" | ||||||||||||||||
|
||||||||||||||||
default = "sfo3" # Default to San Francisco | ||||||||||||||||
} | ||||||||||||||||
``` | ||||||||||||||||
|
||||||||||||||||
### With Mutable Region Selection | ||||||||||||||||
|
||||||||||||||||
```tf | ||||||||||||||||
module "region" { | ||||||||||||||||
source = "registry.coder.com/coder/digitalocean-region/coder" | ||||||||||||||||
version = "1.0.0" | ||||||||||||||||
|
||||||||||||||||
mutable = true # Allow changing region after workspace creation | ||||||||||||||||
} | ||||||||||||||||
``` | ||||||||||||||||
|
||||||||||||||||
### Integration with DigitalOcean Resources | ||||||||||||||||
|
||||||||||||||||
```tf | ||||||||||||||||
module "region" { | ||||||||||||||||
source = "registry.coder.com/coder/digitalocean-region/coder" | ||||||||||||||||
version = "1.0.0" | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
resource "digitalocean_volume" "home_volume" { | ||||||||||||||||
region = module.region.value | ||||||||||||||||
name = "coder-${data.coder_workspace.me.id}-home" | ||||||||||||||||
size = 20 | ||||||||||||||||
initial_filesystem_type = "ext4" | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
resource "digitalocean_droplet" "workspace" { | ||||||||||||||||
region = module.region.value | ||||||||||||||||
name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}" | ||||||||||||||||
image = "ubuntu-22-04-x64" | ||||||||||||||||
size = "s-2vcpu-4gb" | ||||||||||||||||
|
||||||||||||||||
volume_ids = [digitalocean_volume.home_volume.id] | ||||||||||||||||
} | ||||||||||||||||
``` | ||||||||||||||||
|
||||||||||||||||
## Benefits | ||||||||||||||||
|
||||||||||||||||
1. **Standardization**: Consistent region selection across all DigitalOcean templates | ||||||||||||||||
2. **Maintenance**: Single place to update region options | ||||||||||||||||
3. **User Experience**: Better UX with descriptive names and icons | ||||||||||||||||
4. **Reliability**: Only includes regions that support required features | ||||||||||||||||
5. **Flexibility**: Customizable for different use cases | ||||||||||||||||
Comment on lines
+149
to
+155
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Remove this section |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import { describe, expect, it } from "bun:test"; | ||
import { | ||
findResourceInstance, | ||
runTerraformInit, | ||
runTerraformApply, | ||
testRequiredVariables, | ||
} from "~test"; | ||
|
||
describe("digitalocean-region", async () => { | ||
type TestVariables = { | ||
default?: string; | ||
mutable?: boolean; | ||
name?: string; | ||
display_name?: string; | ||
description?: string; | ||
icon?: string; | ||
}; | ||
|
||
await runTerraformInit(import.meta.dir); | ||
|
||
it("can run apply with no variables", async () => { | ||
const state = await runTerraformApply<TestVariables>(import.meta.dir, {}); | ||
const parameter = findResourceInstance(state, "coder_parameter"); | ||
expect(parameter.name).toBe("region"); | ||
expect(parameter.display_name).toBe("Region"); | ||
expect(parameter.default).toBe("ams3"); | ||
expect(parameter.mutable).toBe(false); | ||
expect(parameter.type).toBe("string"); | ||
expect(parameter.icon).toBe("/emojis/1f30e.png"); | ||
}); | ||
|
||
it("can customize default region", async () => { | ||
const state = await runTerraformApply<TestVariables>(import.meta.dir, { | ||
default: "sfo3", | ||
}); | ||
const parameter = findResourceInstance(state, "coder_parameter"); | ||
expect(parameter.default).toBe("sfo3"); | ||
}); | ||
|
||
it("can make region mutable", async () => { | ||
const state = await runTerraformApply<TestVariables>(import.meta.dir, { | ||
mutable: true, | ||
}); | ||
const parameter = findResourceInstance(state, "coder_parameter"); | ||
expect(parameter.mutable).toBe(true); | ||
}); | ||
|
||
it("can customize parameter details", async () => { | ||
const state = await runTerraformApply<TestVariables>(import.meta.dir, { | ||
name: "datacenter", | ||
display_name: "Datacenter Location", | ||
description: "Select your preferred datacenter", | ||
icon: "/emojis/1f4cd.png", | ||
}); | ||
const parameter = findResourceInstance(state, "coder_parameter", "region"); | ||
expect(parameter.name).toBe("datacenter"); | ||
expect(parameter.display_name).toBe("Datacenter Location"); | ||
expect(parameter.description).toBe("Select your preferred datacenter"); | ||
expect(parameter.icon).toBe("/emojis/1f4cd.png"); | ||
}); | ||
|
||
it("includes all expected region options", async () => { | ||
const state = await runTerraformApply<TestVariables>(import.meta.dir, {}); | ||
const parameter = findResourceInstance(state, "coder_parameter"); | ||
|
||
const expectedRegions = [ | ||
{ value: "tor1", name: "Canada (Toronto)", icon: "/emojis/1f1e8-1f1e6.png", description: "" }, | ||
{ value: "fra1", name: "Germany (Frankfurt)", icon: "/emojis/1f1e9-1f1ea.png", description: "" }, | ||
{ value: "blr1", name: "India (Bangalore)", icon: "/emojis/1f1ee-1f1f3.png", description: "" }, | ||
{ value: "ams3", name: "Netherlands (Amsterdam)", icon: "/emojis/1f1f3-1f1f1.png", description: "" }, | ||
{ value: "sgp1", name: "Singapore", icon: "/emojis/1f1f8-1f1ec.png", description: "" }, | ||
{ value: "lon1", name: "United Kingdom (London)", icon: "/emojis/1f1ec-1f1e7.png", description: "" }, | ||
{ value: "sfo2", name: "United States (California - 2)", icon: "/emojis/1f1fa-1f1f8.png", description: "" }, | ||
{ value: "sfo3", name: "United States (California - 3)", icon: "/emojis/1f1fa-1f1f8.png", description: "" }, | ||
{ value: "nyc1", name: "United States (New York - 1)", icon: "/emojis/1f1fa-1f1f8.png", description: "" }, | ||
{ value: "nyc3", name: "United States (New York - 3)", icon: "/emojis/1f1fa-1f1f8.png", description: "" }, | ||
]; | ||
|
||
expect(parameter.option).toHaveLength(expectedRegions.length); | ||
|
||
expectedRegions.forEach((expectedRegion, index) => { | ||
expect(parameter.option[index]).toEqual(expectedRegion); | ||
}); | ||
}); | ||
|
||
it("outputs the correct values", async () => { | ||
const state = await runTerraformApply<TestVariables>(import.meta.dir, { | ||
default: "sfo3", | ||
name: "test_region", | ||
display_name: "Test Region", | ||
}); | ||
|
||
expect(state.outputs.value.value).toBe("sfo3"); | ||
expect(state.outputs.name.value).toBe("test_region"); | ||
expect(state.outputs.display_name.value).toBe("Test Region"); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
terraform { | ||
required_version = ">= 1.0" | ||
|
||
required_providers { | ||
coder = { | ||
source = "coder/coder" | ||
version = ">= 0.23" | ||
} | ||
} | ||
} | ||
|
||
variable "default" { | ||
type = string | ||
description = "The default region to select" | ||
default = "ams3" | ||
} | ||
|
||
variable "mutable" { | ||
type = bool | ||
description = "Whether the region can be changed after workspace creation" | ||
default = false | ||
} | ||
|
||
variable "name" { | ||
type = string | ||
description = "The name of the parameter" | ||
default = "region" | ||
} | ||
|
||
variable "display_name" { | ||
type = string | ||
description = "The display name of the parameter" | ||
default = "Region" | ||
} | ||
|
||
variable "description" { | ||
type = string | ||
description = "The description of the parameter" | ||
default = "This is the region where your workspace will be created." | ||
} | ||
|
||
variable "icon" { | ||
type = string | ||
description = "The icon to display for the parameter" | ||
default = "/emojis/1f30e.png" | ||
} | ||
|
||
data "coder_parameter" "region" { | ||
name = var.name | ||
display_name = var.display_name | ||
description = var.description | ||
icon = var.icon | ||
type = "string" | ||
default = var.default | ||
mutable = var.mutable | ||
# nyc1, sfo1, and ams2 regions were excluded because they do not support volumes, which are used to persist data while decreasing cost | ||
option { | ||
name = "Canada (Toronto)" | ||
value = "tor1" | ||
icon = "/emojis/1f1e8-1f1e6.png" | ||
} | ||
option { | ||
name = "Germany (Frankfurt)" | ||
value = "fra1" | ||
icon = "/emojis/1f1e9-1f1ea.png" | ||
} | ||
option { | ||
name = "India (Bangalore)" | ||
value = "blr1" | ||
icon = "/emojis/1f1ee-1f1f3.png" | ||
} | ||
option { | ||
name = "Netherlands (Amsterdam)" | ||
value = "ams3" | ||
icon = "/emojis/1f1f3-1f1f1.png" | ||
} | ||
option { | ||
name = "Singapore" | ||
value = "sgp1" | ||
icon = "/emojis/1f1f8-1f1ec.png" | ||
} | ||
option { | ||
name = "United Kingdom (London)" | ||
value = "lon1" | ||
icon = "/emojis/1f1ec-1f1e7.png" | ||
} | ||
option { | ||
name = "United States (California - 2)" | ||
value = "sfo2" | ||
icon = "/emojis/1f1fa-1f1f8.png" | ||
} | ||
option { | ||
name = "United States (California - 3)" | ||
value = "sfo3" | ||
icon = "/emojis/1f1fa-1f1f8.png" | ||
} | ||
option { | ||
name = "United States (New York - 1)" | ||
value = "nyc1" | ||
icon = "/emojis/1f1fa-1f1f8.png" | ||
} | ||
option { | ||
name = "United States (New York - 3)" | ||
value = "nyc3" | ||
icon = "/emojis/1f1fa-1f1f8.png" | ||
} | ||
} | ||
|
||
output "value" { | ||
description = "The selected region value" | ||
value = data.coder_parameter.region.value | ||
} | ||
|
||
output "name" { | ||
description = "The selected region name" | ||
value = data.coder_parameter.region.name | ||
} | ||
|
||
output "display_name" { | ||
description = "The selected region display name" | ||
value = data.coder_parameter.region.display_name | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no more needed