mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
chore(examples): update sample devcontainer templates (#13796)
Updates docker and kubernetes devcontainer templates Co-authored-by: Mathias Fredriksson <mafredri@gmail.com>
This commit is contained in:
@@ -9,19 +9,17 @@ tags: [container, docker, devcontainer]
|
||||
|
||||
# Remote Development on Docker Containers (with Devcontainers)
|
||||
|
||||
Provision Docker containers as [Coder workspaces](https://coder.com/docs/workspaces) with this example template.
|
||||
|
||||
<!-- TODO: Add screenshot -->
|
||||
Provision Devcontainers as [Coder workspaces](https://coder.com/docs/workspaces) in Docker with this example template.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Infrastructure
|
||||
|
||||
The VM you run Coder on must have a running Docker socket and the `coder` user must be added to the Docker group:
|
||||
Coder must have access to a running Docker socket, and the `coder` user must be a member of the `docker` group:
|
||||
|
||||
```sh
|
||||
```shell
|
||||
# Add coder user to Docker group
|
||||
sudo adduser coder docker
|
||||
sudo usermod -aG docker coder
|
||||
|
||||
# Restart Coder server
|
||||
sudo systemctl restart coder
|
||||
@@ -32,19 +30,45 @@ sudo -u coder docker ps
|
||||
|
||||
## Architecture
|
||||
|
||||
Coder supports devcontainers with [envbuilder](https://github.com/coder/envbuilder), an open source project. Read more about this in [Coder's documentation](https://coder.com/docs/templates/dev-containers).
|
||||
Coder supports Devcontainers via [envbuilder](https://github.com/coder/envbuilder), an open source project. Read more about this in [Coder's documentation](https://coder.com/docs/templates/dev-containers).
|
||||
|
||||
This template provisions the following resources:
|
||||
|
||||
- Docker image (built by Docker socket and kept locally)
|
||||
- Docker container pod (ephemeral)
|
||||
- Docker volume (persistent on `/home/coder`)
|
||||
- Docker image (persistent)
|
||||
- Docker container (ephemeral)
|
||||
- Docker volume (persistent on `/workspaces`)
|
||||
|
||||
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 container image. Alternatively, individual developers can [personalize](https://coder.com/docs/dotfiles) their workspaces with dotfiles.
|
||||
with [`envbuilder`](https://github.com/coder/envbuilder).
|
||||
The Git repository is cloned inside the `/workspaces` volume if not present.
|
||||
Any local changes to the Devcontainer files inside the volume will be applied when you restart the workspace.
|
||||
Keep in mind that any tools or files outside of `/workspaces` or not added as part of the Devcontainer specification are not persisted.
|
||||
Edit the `devcontainer.json` instead!
|
||||
|
||||
> **Note**
|
||||
> This template is designed to be a starting point! Edit the Terraform to extend the template to support your use case.
|
||||
|
||||
### Editing the image
|
||||
## Docker-in-Docker
|
||||
|
||||
Edit the `Dockerfile` and run `coder templates push` to update workspaces.
|
||||
See the [Envbuilder documentation](https://github.com/coder/envbuilder/blob/main/docs/docker.md) for information on running Docker containers inside a devcontainer built by Envbuilder.
|
||||
|
||||
## Caching
|
||||
|
||||
To speed up your builds, you can use a container registry as a cache.
|
||||
When creating the template, set the parameter `cache_repo`.
|
||||
|
||||
For example, you can run a local registry:
|
||||
|
||||
```shell
|
||||
docker run --detach \
|
||||
--volume registry-cache:/var/lib/registry \
|
||||
--publish 5000:5000 \
|
||||
--name registry-cache \
|
||||
--net=host \
|
||||
registry:2
|
||||
```
|
||||
|
||||
Then, when creating the template, enter `localhost:5000/devcontainer-cache` for the parameter `cache_repo`.
|
||||
|
||||
> [!NOTE] We recommend using a registry cache with authentication enabled.
|
||||
> To allow Envbuilder to authenticate with the registry cache, specify the variable `cache_repo_docker_config_path`
|
||||
> with the path to a Docker config `.json` on disk containing valid credentials for the registry.
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
coder = {
|
||||
source = "coder/coder"
|
||||
source = "coder/coder"
|
||||
version = "~> 1.0.0"
|
||||
}
|
||||
docker = {
|
||||
source = "kreuzwerker/docker"
|
||||
@@ -9,16 +10,186 @@ terraform {
|
||||
}
|
||||
}
|
||||
|
||||
data "coder_provisioner" "me" {
|
||||
}
|
||||
|
||||
provider "docker" {
|
||||
}
|
||||
|
||||
data "coder_workspace" "me" {
|
||||
}
|
||||
provider "coder" {}
|
||||
provider "docker" {}
|
||||
data "coder_provisioner" "me" {}
|
||||
data "coder_workspace" "me" {}
|
||||
data "coder_workspace_owner" "me" {}
|
||||
|
||||
data "coder_parameter" "repo" {
|
||||
description = "Select a repository to automatically clone and start working with a devcontainer."
|
||||
display_name = "Repository (auto)"
|
||||
mutable = true
|
||||
name = "repo"
|
||||
option {
|
||||
name = "vercel/next.js"
|
||||
description = "The React Framework"
|
||||
value = "https://github.com/vercel/next.js"
|
||||
}
|
||||
option {
|
||||
name = "home-assistant/core"
|
||||
description = "🏡 Open source home automation that puts local control and privacy first."
|
||||
value = "https://github.com/home-assistant/core"
|
||||
}
|
||||
option {
|
||||
name = "discourse/discourse"
|
||||
description = "A platform for community discussion. Free, open, simple."
|
||||
value = "https://github.com/discourse/discourse"
|
||||
}
|
||||
option {
|
||||
name = "denoland/deno"
|
||||
description = "A modern runtime for JavaScript and TypeScript."
|
||||
value = "https://github.com/denoland/deno"
|
||||
}
|
||||
option {
|
||||
name = "microsoft/vscode"
|
||||
icon = "/icon/code.svg"
|
||||
description = "Code editing. Redefined."
|
||||
value = "https://github.com/microsoft/vscode"
|
||||
}
|
||||
option {
|
||||
name = "Custom"
|
||||
icon = "/emojis/1f5c3.png"
|
||||
description = "Specify a custom repo URL below"
|
||||
value = "custom"
|
||||
}
|
||||
order = 1
|
||||
}
|
||||
|
||||
data "coder_parameter" "custom_repo_url" {
|
||||
default = ""
|
||||
description = "Optionally enter a custom repository URL, see [awesome-devcontainers](https://github.com/manekinekko/awesome-devcontainers)."
|
||||
display_name = "Repository URL (custom)"
|
||||
name = "custom_repo_url"
|
||||
mutable = true
|
||||
order = 2
|
||||
}
|
||||
|
||||
data "coder_parameter" "fallback_image" {
|
||||
default = "codercom/enterprise-base:ubuntu"
|
||||
description = "This image runs if the devcontainer fails to build."
|
||||
display_name = "Fallback Image"
|
||||
mutable = true
|
||||
name = "fallback_image"
|
||||
order = 3
|
||||
}
|
||||
|
||||
data "coder_parameter" "devcontainer_builder" {
|
||||
description = <<-EOF
|
||||
Image that will build the devcontainer.
|
||||
We highly recommend using a specific release as the `:latest` tag will change.
|
||||
Find the latest version of Envbuilder here: https://github.com/coder/envbuilder/pkgs/container/envbuilder
|
||||
EOF
|
||||
display_name = "Devcontainer Builder"
|
||||
mutable = true
|
||||
name = "devcontainer_builder"
|
||||
default = "ghcr.io/coder/envbuilder:latest"
|
||||
order = 4
|
||||
}
|
||||
|
||||
variable "cache_repo" {
|
||||
default = ""
|
||||
description = "Use a container registry as a cache to speed up builds."
|
||||
sensitive = true
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "cache_repo_docker_config_path" {
|
||||
default = ""
|
||||
description = "Path to a docker config.json containing credentials to the provided cache repo, if required."
|
||||
sensitive = true
|
||||
type = string
|
||||
}
|
||||
|
||||
locals {
|
||||
container_name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}"
|
||||
devcontainer_builder_image = data.coder_parameter.devcontainer_builder.value
|
||||
git_author_name = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name)
|
||||
git_author_email = data.coder_workspace_owner.me.email
|
||||
repo_url = data.coder_parameter.repo.value == "custom" ? data.coder_parameter.custom_repo_url.value : data.coder_parameter.repo.value
|
||||
}
|
||||
|
||||
data "local_sensitive_file" "cache_repo_dockerconfigjson" {
|
||||
count = var.cache_repo_docker_config_path == "" ? 0 : 1
|
||||
filename = var.cache_repo_docker_config_path
|
||||
}
|
||||
|
||||
resource "docker_image" "devcontainer_builder_image" {
|
||||
name = local.devcontainer_builder_image
|
||||
}
|
||||
|
||||
resource "docker_volume" "workspaces" {
|
||||
name = "coder-${data.coder_workspace.me.id}"
|
||||
# Protect the volume from being deleted due to changes in attributes.
|
||||
lifecycle {
|
||||
ignore_changes = all
|
||||
}
|
||||
# Add labels in Docker to keep track of orphan resources.
|
||||
labels {
|
||||
label = "coder.owner"
|
||||
value = data.coder_workspace_owner.me.name
|
||||
}
|
||||
labels {
|
||||
label = "coder.owner_id"
|
||||
value = data.coder_workspace_owner.me.id
|
||||
}
|
||||
labels {
|
||||
label = "coder.workspace_id"
|
||||
value = data.coder_workspace.me.id
|
||||
}
|
||||
# This field becomes outdated if the workspace is renamed but can
|
||||
# be useful for debugging or cleaning out dangling volumes.
|
||||
labels {
|
||||
label = "coder.workspace_name_at_creation"
|
||||
value = data.coder_workspace.me.name
|
||||
}
|
||||
}
|
||||
|
||||
resource "docker_container" "workspace" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
image = local.devcontainer_builder_image
|
||||
# Uses lower() to avoid Docker restriction on container names.
|
||||
name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}"
|
||||
# Hostname makes the shell more user friendly: coder@my-workspace:~$
|
||||
hostname = data.coder_workspace.me.name
|
||||
# Use the docker gateway if the access URL is 127.0.0.1
|
||||
env = [
|
||||
"CODER_AGENT_TOKEN=${coder_agent.main.token}",
|
||||
"CODER_AGENT_URL=${replace(data.coder_workspace.me.access_url, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal")}",
|
||||
"ENVBUILDER_GIT_URL=${local.repo_url}",
|
||||
"ENVBUILDER_INIT_SCRIPT=${replace(coder_agent.main.init_script, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal")}",
|
||||
"ENVBUILDER_FALLBACK_IMAGE=${data.coder_parameter.fallback_image.value}",
|
||||
"ENVBUILDER_CACHE_REPO=${var.cache_repo}",
|
||||
"ENVBUILDER_DOCKER_CONFIG_BASE64=${try(data.local_sensitive_file.cache_repo_dockerconfigjson[0].content_base64, "")}",
|
||||
]
|
||||
host {
|
||||
host = "host.docker.internal"
|
||||
ip = "host-gateway"
|
||||
}
|
||||
volumes {
|
||||
container_path = "/workspaces"
|
||||
volume_name = docker_volume.workspaces.name
|
||||
read_only = false
|
||||
}
|
||||
# Add labels in Docker to keep track of orphan resources.
|
||||
labels {
|
||||
label = "coder.owner"
|
||||
value = data.coder_workspace_owner.me.name
|
||||
}
|
||||
labels {
|
||||
label = "coder.owner_id"
|
||||
value = data.coder_workspace_owner.me.id
|
||||
}
|
||||
labels {
|
||||
label = "coder.workspace_id"
|
||||
value = data.coder_workspace.me.id
|
||||
}
|
||||
labels {
|
||||
label = "coder.workspace_name"
|
||||
value = data.coder_workspace.me.name
|
||||
}
|
||||
}
|
||||
|
||||
resource "coder_agent" "main" {
|
||||
arch = data.coder_provisioner.me.arch
|
||||
os = "linux"
|
||||
@@ -36,10 +207,10 @@ resource "coder_agent" "main" {
|
||||
# You can remove this block if you'd prefer to configure Git manually or using
|
||||
# dotfiles. (see docs/dotfiles.md)
|
||||
env = {
|
||||
GIT_AUTHOR_NAME = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name)
|
||||
GIT_AUTHOR_EMAIL = "${data.coder_workspace_owner.me.email}"
|
||||
GIT_COMMITTER_NAME = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name)
|
||||
GIT_COMMITTER_EMAIL = "${data.coder_workspace_owner.me.email}"
|
||||
GIT_AUTHOR_NAME = local.git_author_name
|
||||
GIT_AUTHOR_EMAIL = local.git_author_email
|
||||
GIT_COMMITTER_NAME = local.git_author_name
|
||||
GIT_COMMITTER_EMAIL = local.git_author_email
|
||||
}
|
||||
|
||||
# The following metadata blocks are optional. They are used to display
|
||||
@@ -124,125 +295,3 @@ resource "coder_app" "code-server" {
|
||||
threshold = 6
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
resource "docker_volume" "workspaces" {
|
||||
name = "coder-${data.coder_workspace.me.id}"
|
||||
# Protect the volume from being deleted due to changes in attributes.
|
||||
lifecycle {
|
||||
ignore_changes = all
|
||||
}
|
||||
# Add labels in Docker to keep track of orphan resources.
|
||||
labels {
|
||||
label = "coder.owner"
|
||||
value = data.coder_workspace_owner.me.name
|
||||
}
|
||||
labels {
|
||||
label = "coder.owner_id"
|
||||
value = data.coder_workspace_owner.me.id
|
||||
}
|
||||
labels {
|
||||
label = "coder.workspace_id"
|
||||
value = data.coder_workspace.me.id
|
||||
}
|
||||
# This field becomes outdated if the workspace is renamed but can
|
||||
# be useful for debugging or cleaning out dangling volumes.
|
||||
labels {
|
||||
label = "coder.workspace_name_at_creation"
|
||||
value = data.coder_workspace.me.name
|
||||
}
|
||||
}
|
||||
|
||||
data "coder_parameter" "repo" {
|
||||
name = "repo"
|
||||
display_name = "Repository (auto)"
|
||||
order = 1
|
||||
description = "Select a repository to automatically clone and start working with a devcontainer."
|
||||
mutable = true
|
||||
option {
|
||||
name = "vercel/next.js"
|
||||
description = "The React Framework"
|
||||
value = "https://github.com/vercel/next.js"
|
||||
}
|
||||
option {
|
||||
name = "home-assistant/core"
|
||||
description = "🏡 Open source home automation that puts local control and privacy first."
|
||||
value = "https://github.com/home-assistant/core"
|
||||
}
|
||||
option {
|
||||
name = "discourse/discourse"
|
||||
description = "A platform for community discussion. Free, open, simple."
|
||||
value = "https://github.com/discourse/discourse"
|
||||
}
|
||||
option {
|
||||
name = "denoland/deno"
|
||||
description = "A modern runtime for JavaScript and TypeScript."
|
||||
value = "https://github.com/denoland/deno"
|
||||
}
|
||||
option {
|
||||
name = "microsoft/vscode"
|
||||
icon = "/icon/code.svg"
|
||||
description = "Code editing. Redefined."
|
||||
value = "https://github.com/microsoft/vscode"
|
||||
}
|
||||
option {
|
||||
name = "Custom"
|
||||
icon = "/emojis/1f5c3.png"
|
||||
description = "Specify a custom repo URL below"
|
||||
value = "custom"
|
||||
}
|
||||
}
|
||||
|
||||
data "coder_parameter" "custom_repo_url" {
|
||||
name = "custom_repo"
|
||||
display_name = "Repository URL (custom)"
|
||||
order = 2
|
||||
default = ""
|
||||
description = "Optionally enter a custom repository URL, see [awesome-devcontainers](https://github.com/manekinekko/awesome-devcontainers)."
|
||||
mutable = true
|
||||
}
|
||||
|
||||
resource "docker_container" "workspace" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
# Find the latest version here:
|
||||
# https://github.com/coder/envbuilder/tags
|
||||
image = "ghcr.io/coder/envbuilder:0.2.1"
|
||||
# Uses lower() to avoid Docker restriction on container names.
|
||||
name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}"
|
||||
# Hostname makes the shell more user friendly: coder@my-workspace:~$
|
||||
hostname = data.coder_workspace.me.name
|
||||
# Use the docker gateway if the access URL is 127.0.0.1
|
||||
env = [
|
||||
"CODER_AGENT_TOKEN=${coder_agent.main.token}",
|
||||
"CODER_AGENT_URL=${replace(data.coder_workspace.me.access_url, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal")}",
|
||||
"GIT_URL=${data.coder_parameter.repo.value == "custom" ? data.coder_parameter.custom_repo_url.value : data.coder_parameter.repo.value}",
|
||||
"INIT_SCRIPT=${replace(coder_agent.main.init_script, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal")}",
|
||||
"FALLBACK_IMAGE=codercom/enterprise-base:ubuntu" # This image runs if builds fail
|
||||
]
|
||||
host {
|
||||
host = "host.docker.internal"
|
||||
ip = "host-gateway"
|
||||
}
|
||||
volumes {
|
||||
container_path = "/workspaces"
|
||||
volume_name = docker_volume.workspaces.name
|
||||
read_only = false
|
||||
}
|
||||
# Add labels in Docker to keep track of orphan resources.
|
||||
labels {
|
||||
label = "coder.owner"
|
||||
value = data.coder_workspace_owner.me.name
|
||||
}
|
||||
labels {
|
||||
label = "coder.owner_id"
|
||||
value = data.coder_workspace_owner.me.id
|
||||
}
|
||||
labels {
|
||||
label = "coder.workspace_id"
|
||||
value = data.coder_workspace.me.id
|
||||
}
|
||||
labels {
|
||||
label = "coder.workspace_name"
|
||||
value = data.coder_workspace.me.name
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,17 +9,15 @@ tags: [container, kubernetes, devcontainer]
|
||||
|
||||
# Remote Development on Kubernetes Pods (with Devcontainers)
|
||||
|
||||
Provision Kubernetes Pods as [Coder workspaces](https://coder.com/docs/workspaces) with this example template.
|
||||
|
||||
<!-- TODO: Add screenshot -->
|
||||
Provision Devcontainers as [Coder workspaces](https://coder.com/docs/workspaces) on Kubernetes with this example template.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Infrastructure
|
||||
|
||||
**Cluster**: This template requires an existing Kubernetes cluster
|
||||
**Cluster**: This template requires an existing Kubernetes cluster.
|
||||
|
||||
**Container Image**: This template uses the [codercom/enterprise-base:ubuntu image](https://github.com/coder/enterprise-images/tree/main/images/base) with some dev tools preinstalled. To add additional tools, extend this image or build it yourself.
|
||||
**Container Image**: This template uses the [envbuilder image](https://github.com/coder/envbuilder) to build a Devcontainer from a `devcontainer.json`.
|
||||
|
||||
### Authentication
|
||||
|
||||
@@ -31,10 +29,24 @@ Coder supports devcontainers with [envbuilder](https://github.com/coder/envbuild
|
||||
|
||||
This template provisions the following resources:
|
||||
|
||||
- Kubernetes pod (ephemeral)
|
||||
- Kubernetes persistent volume claim (persistent on `/home/coder`)
|
||||
- Kubernetes deployment (ephemeral)
|
||||
- Kubernetes persistent volume claim (persistent on `/workspaces`)
|
||||
|
||||
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 container image. Alternatively, individual developers can [personalize](https://coder.com/docs/dotfiles) their workspaces with dotfiles.
|
||||
This template will fetch a Git repo containing a `devcontainer.json` specified by the `repo` parameter, and builds it
|
||||
with [`envbuilder`](https://github.com/coder/envbuilder).
|
||||
The Git repository is cloned inside the `/workspaces` volume if not present.
|
||||
Any local changes to the Devcontainer files inside the volume will be applied when you restart the workspace.
|
||||
As you might suspect, any tools or files outside of `/workspaces` or not added as part of the Devcontainer specification are not persisted.
|
||||
Edit the `devcontainer.json` instead!
|
||||
|
||||
> **Note**
|
||||
> This template is designed to be a starting point! Edit the Terraform to extend the template to support your use case.
|
||||
|
||||
## Caching
|
||||
|
||||
To speed up your builds, you can use a container registry as a cache.
|
||||
When creating the template, set the parameter `cache_repo`.
|
||||
|
||||
> [!NOTE] We recommend using a registry cache with authentication enabled.
|
||||
> To allow Envbuilder to authenticate with the registry cache, specify the variable `cache_repo_dockerconfig_secret`
|
||||
> with the name of a Kubernetes secret in the same namespace as Coder. The secret must contain the key `.dockerconfigjson`.
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
coder = {
|
||||
source = "coder/coder"
|
||||
source = "coder/coder"
|
||||
version = "~> 1.0.0"
|
||||
}
|
||||
kubernetes = {
|
||||
source = "hashicorp/kubernetes"
|
||||
@@ -9,11 +10,15 @@ terraform {
|
||||
}
|
||||
}
|
||||
|
||||
data "coder_provisioner" "me" {
|
||||
provider "coder" {}
|
||||
provider "kubernetes" {
|
||||
# Authenticate via ~/.kube/config or a Coder-specific ServiceAccount, depending on admin preferences
|
||||
config_path = var.use_kubeconfig == true ? "~/.kube/config" : null
|
||||
}
|
||||
|
||||
provider "coder" {
|
||||
}
|
||||
data "coder_provisioner" "me" {}
|
||||
data "coder_workspace" "me" {}
|
||||
data "coder_workspace_owner" "me" {}
|
||||
|
||||
variable "use_kubeconfig" {
|
||||
type = bool
|
||||
@@ -31,18 +36,278 @@ variable "use_kubeconfig" {
|
||||
|
||||
variable "namespace" {
|
||||
type = string
|
||||
description = "The Kubernetes namespace to create workspaces in (must exist prior to creating workspaces)"
|
||||
default = "default"
|
||||
description = "The Kubernetes namespace to create workspaces in (must exist prior to creating workspaces). If the Coder host is itself running as a Pod on the same Kubernetes cluster as you are deploying workspaces to, set this to the same namespace."
|
||||
}
|
||||
|
||||
provider "kubernetes" {
|
||||
# Authenticate via ~/.kube/config or a Coder-specific ServiceAccount, depending on admin preferences
|
||||
config_path = var.use_kubeconfig == true ? "~/.kube/config" : null
|
||||
variable "cache_repo" {
|
||||
default = ""
|
||||
description = "Use a container registry as a cache to speed up builds."
|
||||
sensitive = true
|
||||
type = string
|
||||
}
|
||||
|
||||
|
||||
data "coder_workspace" "me" {
|
||||
data "coder_parameter" "cpu" {
|
||||
type = "number"
|
||||
name = "cpu"
|
||||
display_name = "CPU"
|
||||
description = "CPU limit (cores)."
|
||||
default = "2"
|
||||
icon = "/emojis/1f5a5.png"
|
||||
mutable = true
|
||||
validation {
|
||||
min = 1
|
||||
max = 99999
|
||||
}
|
||||
order = 1
|
||||
}
|
||||
|
||||
data "coder_parameter" "memory" {
|
||||
type = "number"
|
||||
name = "memory"
|
||||
display_name = "Memory"
|
||||
description = "Memory limit (GiB)."
|
||||
default = "2"
|
||||
icon = "/icon/memory.svg"
|
||||
mutable = true
|
||||
validation {
|
||||
min = 1
|
||||
max = 99999
|
||||
}
|
||||
order = 2
|
||||
}
|
||||
|
||||
data "coder_parameter" "workspaces_volume_size" {
|
||||
name = "workspaces_volume_size"
|
||||
display_name = "Workspaces volume size"
|
||||
description = "Size of the `/workspaces` volume (GiB)."
|
||||
default = "10"
|
||||
type = "number"
|
||||
icon = "/emojis/1f4be.png"
|
||||
mutable = false
|
||||
validation {
|
||||
min = 1
|
||||
max = 99999
|
||||
}
|
||||
order = 3
|
||||
}
|
||||
|
||||
data "coder_parameter" "repo" {
|
||||
description = "Select a repository to automatically clone and start working with a devcontainer."
|
||||
display_name = "Repository (auto)"
|
||||
mutable = true
|
||||
name = "repo"
|
||||
order = 4
|
||||
type = "string"
|
||||
}
|
||||
|
||||
data "coder_parameter" "fallback_image" {
|
||||
default = "codercom/enterprise-base:ubuntu"
|
||||
description = "This image runs if the devcontainer fails to build."
|
||||
display_name = "Fallback Image"
|
||||
mutable = true
|
||||
name = "fallback_image"
|
||||
order = 6
|
||||
}
|
||||
|
||||
data "coder_parameter" "devcontainer_builder" {
|
||||
description = <<-EOF
|
||||
Image that will build the devcontainer.
|
||||
We highly recommend using a specific release as the `:latest` tag will change.
|
||||
Find the latest version of Envbuilder here: https://github.com/coder/envbuilder/pkgs/container/envbuilder
|
||||
EOF
|
||||
display_name = "Devcontainer Builder"
|
||||
mutable = true
|
||||
name = "devcontainer_builder"
|
||||
default = "ghcr.io/coder/envbuilder:latest"
|
||||
order = 7
|
||||
}
|
||||
|
||||
variable "cache_repo_secret_name" {
|
||||
default = ""
|
||||
description = "Path to a docker config.json containing credentials to the provided cache repo, if required."
|
||||
sensitive = true
|
||||
type = string
|
||||
}
|
||||
|
||||
data "kubernetes_secret" "cache_repo_dockerconfig_secret" {
|
||||
count = var.cache_repo_secret_name == "" ? 0 : 1
|
||||
metadata {
|
||||
name = var.cache_repo_secret_name
|
||||
namespace = var.namespace
|
||||
}
|
||||
}
|
||||
|
||||
locals {
|
||||
deployment_name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}"
|
||||
devcontainer_builder_image = data.coder_parameter.devcontainer_builder.value
|
||||
git_author_name = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name)
|
||||
git_author_email = data.coder_workspace_owner.me.email
|
||||
repo_url = data.coder_parameter.repo.value
|
||||
}
|
||||
|
||||
resource "kubernetes_persistent_volume_claim" "home" {
|
||||
metadata {
|
||||
name = "coder-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}-home"
|
||||
namespace = var.namespace
|
||||
labels = {
|
||||
"app.kubernetes.io/name" = "coder-pvc"
|
||||
"app.kubernetes.io/instance" = "coder-pvc-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}"
|
||||
"app.kubernetes.io/part-of" = "coder"
|
||||
//Coder-specific labels.
|
||||
"com.coder.resource" = "true"
|
||||
"com.coder.workspace.id" = data.coder_workspace.me.id
|
||||
"com.coder.workspace.name" = data.coder_workspace.me.name
|
||||
"com.coder.user.id" = data.coder_workspace_owner.me.id
|
||||
"com.coder.user.username" = data.coder_workspace_owner.me.name
|
||||
}
|
||||
annotations = {
|
||||
"com.coder.user.email" = data.coder_workspace_owner.me.email
|
||||
}
|
||||
}
|
||||
wait_until_bound = false
|
||||
spec {
|
||||
access_modes = ["ReadWriteOnce"]
|
||||
resources {
|
||||
requests = {
|
||||
storage = "${data.coder_parameter.workspaces_volume_size.value}Gi"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "kubernetes_deployment" "main" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
depends_on = [
|
||||
kubernetes_persistent_volume_claim.home
|
||||
]
|
||||
wait_for_rollout = false
|
||||
metadata {
|
||||
name = local.deployment_name
|
||||
namespace = var.namespace
|
||||
labels = {
|
||||
"app.kubernetes.io/name" = "coder-workspace"
|
||||
"app.kubernetes.io/instance" = local.deployment_name
|
||||
"app.kubernetes.io/part-of" = "coder"
|
||||
"com.coder.resource" = "true"
|
||||
"com.coder.workspace.id" = data.coder_workspace.me.id
|
||||
"com.coder.workspace.name" = data.coder_workspace.me.name
|
||||
"com.coder.user.id" = data.coder_workspace_owner.me.id
|
||||
"com.coder.user.username" = data.coder_workspace_owner.me.name
|
||||
}
|
||||
annotations = {
|
||||
"com.coder.user.email" = data.coder_workspace_owner.me.email
|
||||
}
|
||||
}
|
||||
|
||||
spec {
|
||||
replicas = 1
|
||||
selector {
|
||||
match_labels = {
|
||||
"app.kubernetes.io/name" = "coder-workspace"
|
||||
}
|
||||
}
|
||||
strategy {
|
||||
type = "Recreate"
|
||||
}
|
||||
|
||||
template {
|
||||
metadata {
|
||||
labels = {
|
||||
"app.kubernetes.io/name" = "coder-workspace"
|
||||
}
|
||||
}
|
||||
spec {
|
||||
security_context {}
|
||||
|
||||
container {
|
||||
name = "dev"
|
||||
image = local.devcontainer_builder_image
|
||||
image_pull_policy = "Always"
|
||||
security_context {}
|
||||
env {
|
||||
name = "CODER_AGENT_TOKEN"
|
||||
value = coder_agent.main.token
|
||||
}
|
||||
env {
|
||||
name = "CODER_AGENT_URL"
|
||||
value = data.coder_workspace.me.access_url
|
||||
}
|
||||
env {
|
||||
name = "ENVBUILDER_GIT_URL"
|
||||
value = local.repo_url
|
||||
}
|
||||
env {
|
||||
name = "ENVBUILDER_INIT_SCRIPT"
|
||||
value = coder_agent.main.init_script
|
||||
}
|
||||
env {
|
||||
name = "ENVBUILDER_FALLBACK_IMAGE"
|
||||
value = data.coder_parameter.fallback_image.value
|
||||
}
|
||||
env {
|
||||
name = "ENVBUILDER_CACHE_REPO"
|
||||
value = var.cache_repo
|
||||
}
|
||||
env {
|
||||
name = "ENVBUILDER_DOCKER_CONFIG_BASE64"
|
||||
value = try(data.kubernetes_secret.cache_repo_dockerconfig_secret[0].data[".dockerconfigjson"], "")
|
||||
}
|
||||
# You may need to adjust this if you get an error regarding deleting files when building the workspace.
|
||||
# For example, when testing in KinD, it was necessary to set `/product_name` and `/product_uuid` in
|
||||
# addition to `/var/run`.
|
||||
# env {
|
||||
# name = "ENVBUILDER_IGNORE_PATHS"
|
||||
# value = "/product_name,/product_uuid,/var/run"
|
||||
# }
|
||||
resources {
|
||||
requests = {
|
||||
"cpu" = "250m"
|
||||
"memory" = "512Mi"
|
||||
}
|
||||
limits = {
|
||||
"cpu" = "${data.coder_parameter.cpu.value}"
|
||||
"memory" = "${data.coder_parameter.memory.value}Gi"
|
||||
}
|
||||
}
|
||||
volume_mount {
|
||||
mount_path = "/home/coder"
|
||||
name = "home"
|
||||
read_only = false
|
||||
}
|
||||
}
|
||||
|
||||
volume {
|
||||
name = "home"
|
||||
persistent_volume_claim {
|
||||
claim_name = kubernetes_persistent_volume_claim.home.metadata.0.name
|
||||
read_only = false
|
||||
}
|
||||
}
|
||||
|
||||
affinity {
|
||||
// This affinity attempts to spread out all workspace pods evenly across
|
||||
// nodes.
|
||||
pod_anti_affinity {
|
||||
preferred_during_scheduling_ignored_during_execution {
|
||||
weight = 1
|
||||
pod_affinity_term {
|
||||
topology_key = "kubernetes.io/hostname"
|
||||
label_selector {
|
||||
match_expressions {
|
||||
key = "app.kubernetes.io/name"
|
||||
operator = "In"
|
||||
values = ["coder-workspace"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
data "coder_workspace_owner" "me" {}
|
||||
|
||||
resource "coder_agent" "main" {
|
||||
arch = data.coder_provisioner.me.arch
|
||||
@@ -61,12 +326,77 @@ resource "coder_agent" "main" {
|
||||
# You can remove this block if you'd prefer to configure Git manually or using
|
||||
# dotfiles. (see docs/dotfiles.md)
|
||||
env = {
|
||||
GIT_AUTHOR_NAME = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name)
|
||||
GIT_AUTHOR_EMAIL = "${data.coder_workspace_owner.me.email}"
|
||||
GIT_COMMITTER_NAME = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name)
|
||||
GIT_COMMITTER_EMAIL = "${data.coder_workspace_owner.me.email}"
|
||||
GIT_AUTHOR_NAME = local.git_author_name
|
||||
GIT_AUTHOR_EMAIL = local.git_author_email
|
||||
GIT_COMMITTER_NAME = local.git_author_name
|
||||
GIT_COMMITTER_EMAIL = local.git_author_email
|
||||
}
|
||||
|
||||
# The following metadata blocks are optional. They are used to display
|
||||
# information about your workspace in the dashboard. You can remove them
|
||||
# if you don't want to display any information.
|
||||
# For basic resources, you can use the `coder stat` command.
|
||||
# If you need more control, you can write your own script.
|
||||
metadata {
|
||||
display_name = "CPU Usage"
|
||||
key = "0_cpu_usage"
|
||||
script = "coder stat cpu"
|
||||
interval = 10
|
||||
timeout = 1
|
||||
}
|
||||
|
||||
metadata {
|
||||
display_name = "RAM Usage"
|
||||
key = "1_ram_usage"
|
||||
script = "coder stat mem"
|
||||
interval = 10
|
||||
timeout = 1
|
||||
}
|
||||
|
||||
metadata {
|
||||
display_name = "Home Disk"
|
||||
key = "3_home_disk"
|
||||
script = "coder stat disk --path $HOME"
|
||||
interval = 60
|
||||
timeout = 1
|
||||
}
|
||||
|
||||
metadata {
|
||||
display_name = "CPU Usage (Host)"
|
||||
key = "4_cpu_usage_host"
|
||||
script = "coder stat cpu --host"
|
||||
interval = 10
|
||||
timeout = 1
|
||||
}
|
||||
|
||||
metadata {
|
||||
display_name = "Memory Usage (Host)"
|
||||
key = "5_mem_usage_host"
|
||||
script = "coder stat mem --host"
|
||||
interval = 10
|
||||
timeout = 1
|
||||
}
|
||||
|
||||
metadata {
|
||||
display_name = "Load Average (Host)"
|
||||
key = "6_load_host"
|
||||
# get load avg scaled by number of cores
|
||||
script = <<EOT
|
||||
echo "`cat /proc/loadavg | awk '{ print $1 }'` `nproc`" | awk '{ printf "%0.2f", $1/$2 }'
|
||||
EOT
|
||||
interval = 60
|
||||
timeout = 1
|
||||
}
|
||||
|
||||
metadata {
|
||||
display_name = "Swap Usage (Host)"
|
||||
key = "7_swap_host"
|
||||
script = <<EOT
|
||||
free -b | awk '/^Swap/ { printf("%.1f/%.1f", $3/1024.0/1024.0/1024.0, $2/1024.0/1024.0/1024.0) }'
|
||||
EOT
|
||||
interval = 10
|
||||
timeout = 1
|
||||
}
|
||||
}
|
||||
|
||||
resource "coder_app" "code-server" {
|
||||
@@ -84,146 +414,3 @@ resource "coder_app" "code-server" {
|
||||
threshold = 6
|
||||
}
|
||||
}
|
||||
|
||||
resource "kubernetes_persistent_volume_claim" "workspaces" {
|
||||
metadata {
|
||||
name = "coder-${data.coder_workspace.me.id}"
|
||||
namespace = var.namespace
|
||||
labels = {
|
||||
"coder.owner" = data.coder_workspace_owner.me.name
|
||||
"coder.owner_id" = data.coder_workspace_owner.me.id
|
||||
"coder.workspace_id" = data.coder_workspace.me.id
|
||||
"coder.workspace_name_at_creation" = data.coder_workspace.me.name
|
||||
}
|
||||
}
|
||||
wait_until_bound = false
|
||||
spec {
|
||||
access_modes = ["ReadWriteOnce"]
|
||||
resources {
|
||||
requests = {
|
||||
storage = "10Gi" // adjust as needed
|
||||
}
|
||||
}
|
||||
}
|
||||
lifecycle {
|
||||
ignore_changes = all
|
||||
}
|
||||
}
|
||||
|
||||
data "coder_parameter" "repo" {
|
||||
name = "repo"
|
||||
display_name = "Repository (auto)"
|
||||
order = 1
|
||||
description = "Select a repository to automatically clone and start working with a devcontainer."
|
||||
mutable = true
|
||||
option {
|
||||
name = "vercel/next.js"
|
||||
description = "The React Framework"
|
||||
value = "https://github.com/vercel/next.js"
|
||||
}
|
||||
option {
|
||||
name = "home-assistant/core"
|
||||
description = "🏡 Open source home automation that puts local control and privacy first."
|
||||
value = "https://github.com/home-assistant/core"
|
||||
}
|
||||
option {
|
||||
name = "discourse/discourse"
|
||||
description = "A platform for community discussion. Free, open, simple."
|
||||
value = "https://github.com/discourse/discourse"
|
||||
}
|
||||
option {
|
||||
name = "denoland/deno"
|
||||
description = "A modern runtime for JavaScript and TypeScript."
|
||||
value = "https://github.com/denoland/deno"
|
||||
}
|
||||
option {
|
||||
name = "microsoft/vscode"
|
||||
icon = "/icon/code.svg"
|
||||
description = "Code editing. Redefined."
|
||||
value = "https://github.com/microsoft/vscode"
|
||||
}
|
||||
option {
|
||||
name = "Custom"
|
||||
icon = "/emojis/1f5c3.png"
|
||||
description = "Specify a custom repo URL below"
|
||||
value = "custom"
|
||||
}
|
||||
}
|
||||
|
||||
data "coder_parameter" "custom_repo_url" {
|
||||
name = "custom_repo"
|
||||
display_name = "Repository URL (custom)"
|
||||
order = 2
|
||||
default = ""
|
||||
description = "Optionally enter a custom repository URL, see [awesome-devcontainers](https://github.com/manekinekko/awesome-devcontainers)."
|
||||
mutable = true
|
||||
}
|
||||
|
||||
resource "kubernetes_deployment" "workspace" {
|
||||
metadata {
|
||||
name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}"
|
||||
namespace = var.namespace
|
||||
labels = {
|
||||
"coder.owner" = data.coder_workspace_owner.me.name
|
||||
"coder.owner_id" = data.coder_workspace_owner.me.id
|
||||
"coder.workspace_id" = data.coder_workspace.me.id
|
||||
"coder.workspace_name" = data.coder_workspace.me.name
|
||||
}
|
||||
}
|
||||
spec {
|
||||
replicas = data.coder_workspace.me.start_count
|
||||
selector {
|
||||
match_labels = {
|
||||
"coder.workspace_id" = data.coder_workspace.me.id
|
||||
}
|
||||
}
|
||||
strategy {
|
||||
type = "Recreate"
|
||||
}
|
||||
template {
|
||||
metadata {
|
||||
labels = {
|
||||
"coder.workspace_id" = data.coder_workspace.me.id
|
||||
}
|
||||
}
|
||||
spec {
|
||||
container {
|
||||
name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}"
|
||||
# Find the latest version here:
|
||||
# https://github.com/coder/envbuilder/tags
|
||||
image = "ghcr.io/coder/envbuilder:0.2.1"
|
||||
env {
|
||||
name = "CODER_AGENT_TOKEN"
|
||||
value = coder_agent.main.token
|
||||
}
|
||||
env {
|
||||
name = "CODER_AGENT_URL"
|
||||
value = replace(data.coder_workspace.me.access_url, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal")
|
||||
}
|
||||
env {
|
||||
name = "GIT_URL"
|
||||
value = data.coder_parameter.repo.value == "custom" ? data.coder_parameter.custom_repo_url.value : data.coder_parameter.repo.value
|
||||
}
|
||||
env {
|
||||
name = "INIT_SCRIPT"
|
||||
value = replace(coder_agent.main.init_script, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal")
|
||||
}
|
||||
env {
|
||||
name = "FALLBACK_IMAGE"
|
||||
value = "codercom/enterprise-base:ubuntu"
|
||||
}
|
||||
volume_mount {
|
||||
name = "workspaces"
|
||||
mount_path = "/workspaces"
|
||||
}
|
||||
}
|
||||
volume {
|
||||
name = "workspaces"
|
||||
persistent_volume_claim {
|
||||
claim_name = kubernetes_persistent_volume_claim.workspaces.metadata.0.name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user