mirror of
https://github.com/coder/coder.git
synced 2026-11-06 08:12:45 +08:00
This PR changes template names and docs to follow the `<provider>-<os/whatever>` format for all templates. I've decided not to split this into multiple PRs because I'd have to edit rebase the other PRs once one of them gets merged, this should be relatively low-impact anyways. This aligns with our goals to make templates more user-friendly. Closes #15754
456 lines
14 KiB
Terraform
456 lines
14 KiB
Terraform
terraform {
|
|
required_providers {
|
|
coder = {
|
|
source = "coder/coder"
|
|
version = "~> 1.0.0"
|
|
}
|
|
kubernetes = {
|
|
source = "hashicorp/kubernetes"
|
|
}
|
|
envbuilder = {
|
|
source = "coder/envbuilder"
|
|
}
|
|
}
|
|
}
|
|
|
|
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 "envbuilder" {}
|
|
|
|
data "coder_provisioner" "me" {}
|
|
data "coder_workspace" "me" {}
|
|
data "coder_workspace_owner" "me" {}
|
|
|
|
variable "use_kubeconfig" {
|
|
type = bool
|
|
description = <<-EOF
|
|
Use host kubeconfig? (true/false)
|
|
|
|
Set this to false if the Coder host is itself running as a Pod on the same
|
|
Kubernetes cluster as you are deploying workspaces to.
|
|
|
|
Set this to true if the Coder host is running outside the Kubernetes cluster
|
|
for workspaces. A valid "~/.kube/config" must be present on the Coder host.
|
|
EOF
|
|
default = false
|
|
}
|
|
|
|
variable "namespace" {
|
|
type = string
|
|
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."
|
|
}
|
|
|
|
variable "cache_repo" {
|
|
default = ""
|
|
description = "Use a container registry as a cache to speed up builds."
|
|
type = string
|
|
}
|
|
|
|
variable "insecure_cache_repo" {
|
|
default = false
|
|
description = "Enable this option if your cache registry does not serve HTTPS."
|
|
type = bool
|
|
}
|
|
|
|
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-${lower(data.coder_workspace.me.id)}"
|
|
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
|
|
# The envbuilder provider requires a key-value map of environment variables.
|
|
envbuilder_env = {
|
|
# ENVBUILDER_GIT_URL and ENVBUILDER_CACHE_REPO will be overridden by the provider
|
|
# if the cache repo is enabled.
|
|
"ENVBUILDER_GIT_URL" : local.repo_url,
|
|
"ENVBUILDER_CACHE_REPO" : var.cache_repo,
|
|
"CODER_AGENT_TOKEN" : coder_agent.main.token,
|
|
# Use the docker gateway if the access URL is 127.0.0.1
|
|
"CODER_AGENT_URL" : replace(data.coder_workspace.me.access_url, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal"),
|
|
# Use the docker gateway if the access URL is 127.0.0.1
|
|
"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_DOCKER_CONFIG_BASE64" : try(data.kubernetes_secret.cache_repo_dockerconfig_secret[0].data[".dockerconfigjson"], ""),
|
|
"ENVBUILDER_PUSH_IMAGE" : var.cache_repo == "" ? "" : "true",
|
|
"ENVBUILDER_INSECURE" : "${var.insecure_cache_repo}",
|
|
# 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`.
|
|
# "ENVBUILDER_IGNORE_PATHS": "/product_name,/product_uuid,/var/run",
|
|
}
|
|
}
|
|
|
|
# Check for the presence of a prebuilt image in the cache repo
|
|
# that we can use instead.
|
|
resource "envbuilder_cached_image" "cached" {
|
|
count = var.cache_repo == "" ? 0 : data.coder_workspace.me.start_count
|
|
builder_image = local.devcontainer_builder_image
|
|
git_url = local.repo_url
|
|
cache_repo = var.cache_repo
|
|
extra_env = local.envbuilder_env
|
|
insecure = var.insecure_cache_repo
|
|
}
|
|
|
|
resource "kubernetes_persistent_volume_claim" "workspaces" {
|
|
metadata {
|
|
name = "coder-${lower(data.coder_workspace.me.id)}-workspaces"
|
|
namespace = var.namespace
|
|
labels = {
|
|
"app.kubernetes.io/name" = "coder-${lower(data.coder_workspace.me.id)}-workspaces"
|
|
"app.kubernetes.io/instance" = "coder-${lower(data.coder_workspace.me.id)}-workspaces"
|
|
"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"
|
|
}
|
|
}
|
|
# storage_class_name = "local-path" # Configure the StorageClass to use here, if required.
|
|
}
|
|
}
|
|
|
|
resource "kubernetes_deployment" "main" {
|
|
count = data.coder_workspace.me.start_count
|
|
depends_on = [
|
|
kubernetes_persistent_volume_claim.workspaces
|
|
]
|
|
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 = var.cache_repo == "" ? local.devcontainer_builder_image : envbuilder_cached_image.cached.0.image
|
|
image_pull_policy = "Always"
|
|
security_context {}
|
|
|
|
# Set the environment using cached_image.cached.0.env if the cache repo is enabled.
|
|
# Otherwise, use the local.envbuilder_env.
|
|
# You could alternatively write the environment variables to a ConfigMap or Secret
|
|
# and use that as `env_from`.
|
|
dynamic "env" {
|
|
for_each = nonsensitive(var.cache_repo == "" ? local.envbuilder_env : envbuilder_cached_image.cached.0.env_map)
|
|
content {
|
|
name = env.key
|
|
value = env.value
|
|
}
|
|
}
|
|
|
|
resources {
|
|
requests = {
|
|
"cpu" = "250m"
|
|
"memory" = "512Mi"
|
|
}
|
|
limits = {
|
|
"cpu" = "${data.coder_parameter.cpu.value}"
|
|
"memory" = "${data.coder_parameter.memory.value}Gi"
|
|
}
|
|
}
|
|
volume_mount {
|
|
mount_path = "/workspaces"
|
|
name = "workspaces"
|
|
read_only = false
|
|
}
|
|
}
|
|
|
|
volume {
|
|
name = "workspaces"
|
|
persistent_volume_claim {
|
|
claim_name = kubernetes_persistent_volume_claim.workspaces.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"]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
resource "coder_agent" "main" {
|
|
arch = data.coder_provisioner.me.arch
|
|
os = "linux"
|
|
startup_script = <<-EOT
|
|
set -e
|
|
|
|
# Install the latest code-server.
|
|
# Append "--version x.x.x" to install a specific version of code-server.
|
|
curl -fsSL https://code-server.dev/install.sh | sh -s -- --method=standalone --prefix=/tmp/code-server
|
|
|
|
# Start code-server in the background.
|
|
/tmp/code-server/bin/code-server --auth none --port 13337 >/tmp/code-server.log 2>&1 &
|
|
EOT
|
|
dir = "/workspaces"
|
|
|
|
# These environment variables allow you to make Git commits right away after creating a
|
|
# workspace. Note that they take precedence over configuration defined in ~/.gitconfig!
|
|
# You can remove this block if you'd prefer to configure Git manually or using
|
|
# dotfiles. (see docs/dotfiles.md)
|
|
env = {
|
|
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 = "Workspaces Disk"
|
|
key = "3_workspaces_disk"
|
|
script = "coder stat disk --path /workspaces"
|
|
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" {
|
|
agent_id = coder_agent.main.id
|
|
slug = "code-server"
|
|
display_name = "code-server"
|
|
url = "http://localhost:13337/?folder=/workspaces"
|
|
icon = "/icon/code.svg"
|
|
subdomain = false
|
|
share = "owner"
|
|
|
|
healthcheck {
|
|
url = "http://localhost:13337/healthz"
|
|
interval = 5
|
|
threshold = 6
|
|
}
|
|
}
|
|
|
|
resource "coder_metadata" "container_info" {
|
|
count = data.coder_workspace.me.start_count
|
|
resource_id = coder_agent.main.id
|
|
item {
|
|
key = "workspace image"
|
|
value = var.cache_repo == "" ? local.devcontainer_builder_image : envbuilder_cached_image.cached.0.image
|
|
}
|
|
item {
|
|
key = "git url"
|
|
value = local.repo_url
|
|
}
|
|
item {
|
|
key = "cache repo"
|
|
value = var.cache_repo == "" ? "not enabled" : var.cache_repo
|
|
}
|
|
}
|