Files
coder/examples/templates/kubernetes-devcontainer/main.tf
T
Phorcys 069655ace9 chore: unify template naming (#15757)
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
2024-12-05 22:37:25 +05:00

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
}
}