233 lines
9.8 KiB
HCL
233 lines
9.8 KiB
HCL
terraform {
|
|
required_version = ">= 1.3.0"
|
|
|
|
required_providers {
|
|
coder = {
|
|
source = "coder/coder"
|
|
version = ">= 2.7"
|
|
}
|
|
docker = {
|
|
source = "kreuzwerker/docker"
|
|
version = "~> 2.25"
|
|
}
|
|
http = {
|
|
source = "hashicorp/http"
|
|
version = ">= 3.0"
|
|
}
|
|
}
|
|
}
|
|
|
|
provider "coder" {}
|
|
|
|
provider "docker" {
|
|
host = var.docker_socket != "" ? var.docker_socket : null
|
|
}
|
|
|
|
provider "http" {}
|
|
|
|
# Workspace context
|
|
|
|
data "coder_provisioner" "me" {}
|
|
data "coder_workspace" "me" {}
|
|
data "coder_workspace_owner" "me" {}
|
|
|
|
# User inputs kept intentionally small so the template is easy to launch.
|
|
|
|
data "coder_parameter" "project_repository" {
|
|
name = "project_repository"
|
|
display_name = "Project repository"
|
|
description = "Optional Git URL cloned into /workspaces on first startup."
|
|
default = ""
|
|
mutable = true
|
|
order = 1
|
|
}
|
|
|
|
data "coder_parameter" "enable_services" {
|
|
name = "enable_services"
|
|
display_name = "Enable PostgreSQL / Redis / Qdrant"
|
|
description = "Provision bundled data services inside the workspace network."
|
|
type = "bool"
|
|
default = "true"
|
|
mutable = true
|
|
order = 2
|
|
}
|
|
|
|
data "coder_parameter" "enable_ai_tools" {
|
|
name = "enable_ai_tools"
|
|
display_name = "Install AI tooling"
|
|
description = "Run the bundled AI helper scripts (Claude, Cursor, Windsurf)."
|
|
type = "bool"
|
|
default = "true"
|
|
mutable = true
|
|
order = 3
|
|
}
|
|
|
|
data "coder_parameter" "enable_pgadmin" {
|
|
name = "enable_pgadmin"
|
|
display_name = "Expose pgAdmin"
|
|
description = "Start the pgAdmin container when database services are enabled."
|
|
type = "bool"
|
|
default = "true"
|
|
mutable = true
|
|
order = 4
|
|
}
|
|
|
|
data "coder_parameter" "enable_marimo" {
|
|
name = "enable_marimo"
|
|
display_name = "Expose Marimo"
|
|
description = "Start the optional Marimo notebook container."
|
|
type = "bool"
|
|
default = "false"
|
|
mutable = true
|
|
order = 5
|
|
}
|
|
|
|
data "coder_parameter" "enable_dev_endpoints" {
|
|
name = "enable_dev_endpoints"
|
|
display_name = "Expose Dev HTTP Ports"
|
|
description = "Create Development Services app shortcuts for localhost:3000, 5173, and 8000."
|
|
type = "bool"
|
|
default = "false"
|
|
mutable = true
|
|
order = 6
|
|
}
|
|
|
|
data "coder_parameter" "enable_jetbrains" {
|
|
name = "enable_jetbrains"
|
|
display_name = "JetBrains Gateway"
|
|
description = "Install JetBrains Gateway integration for this workspace."
|
|
type = "bool"
|
|
default = "true"
|
|
mutable = true
|
|
order = 7
|
|
}
|
|
|
|
data "coder_parameter" "ai_prompt" {
|
|
name = "AI Prompt"
|
|
display_name = "AI Task Prompt"
|
|
description = "Optional pre-filled prompt shown when starting a Claude Code task."
|
|
type = "string"
|
|
default = ""
|
|
mutable = true
|
|
order = 8
|
|
form_type = "textarea"
|
|
}
|
|
|
|
locals {
|
|
bind_mounts = [
|
|
{ host = "${var.host_home_path}", container = "/home/coder" },
|
|
{ host = "/var/run/docker.sock", container = "/var/run/docker.sock" },
|
|
]
|
|
|
|
workspace_id = data.coder_workspace.me.id
|
|
container_name = "coder-${local.workspace_id}"
|
|
|
|
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
|
|
|
|
project_repo_url = trimspace(data.coder_parameter.project_repository.value)
|
|
gitea_pat = trimspace(var.gitea_pat)
|
|
github_pat = trimspace(var.github_pat)
|
|
|
|
services_enabled = data.coder_parameter.enable_services.value
|
|
pgadmin_enabled = data.coder_parameter.enable_pgadmin.value
|
|
marimo_enabled = data.coder_parameter.enable_marimo.value
|
|
dev_endpoints_enabled = data.coder_parameter.enable_dev_endpoints.value
|
|
ai_enabled = data.coder_parameter.enable_ai_tools.value
|
|
port_forwarding = local.services_enabled || local.marimo_enabled
|
|
|
|
postgres_url = "postgresql://postgres:${var.postgres_password}@postgres-${local.workspace_id}:5432/postgres"
|
|
redis_url = "redis://:${var.redis_password}@redis-${local.workspace_id}:6379"
|
|
qdrant_url = "http://qdrant-${local.workspace_id}:6333"
|
|
|
|
agent_startup = join("\n", compact([
|
|
"set -eu",
|
|
"export CODER_WORKSPACE_ID=${local.workspace_id}",
|
|
"# Fix RVM environment variables to suppress warnings",
|
|
"if printf '%s' \"$PATH\" | tr ':' '\n' | grep -q '/usr/share/rvm'; then",
|
|
" OLD_IFS=$IFS",
|
|
" IFS=':'",
|
|
" NEW_PATH=\"\"",
|
|
" for segment in $$PATH; do",
|
|
" case \"$segment\" in",
|
|
" *'/usr/share/rvm'* ) continue ;;",
|
|
" * ) if [ -z \"$$NEW_PATH\" ]; then NEW_PATH=\"$$segment\"; else NEW_PATH=\"$$NEW_PATH:$$segment\"; fi ;;",
|
|
" esac",
|
|
" done",
|
|
" IFS=$OLD_IFS",
|
|
" if [ -n \"$$NEW_PATH\" ]; then export PATH=\"$$NEW_PATH\"; fi",
|
|
"fi",
|
|
"export GEM_HOME=\"$HOME/.gem\"",
|
|
"export GEM_PATH=\"$HOME/.gem\"",
|
|
"export rvm_silence_path_mismatch_check_flag=1",
|
|
"export RVM_SILENCE_PATH_MISMATCH_CHECK_FLAG=1",
|
|
"export rvmsudo_secure_path=1",
|
|
"export RVMSUDO_SECURE_PATH=1",
|
|
"# Ensure required directories exist",
|
|
"mkdir -p /home/coder/code-tools/terraform/scripts",
|
|
"mkdir -p /home/coder/code-tools/terraform/scripts/agentapi",
|
|
"# Verify Python 3.12+ baked into the image (checking as coder user)",
|
|
"if ! sudo --preserve-env=CODER_WORKSPACE_ID,CODER_WORKSPACE_REPO,GITEA_PAT,GITHUB_PAT -u coder env -i HOME=/home/coder PATH=\"$PATH\" GEM_HOME=/home/coder/.gem GEM_PATH=/home/coder/.gem CODER_WORKSPACE_ID=\"$${CODER_WORKSPACE_ID-}\" CODER_WORKSPACE_REPO=\"$${CODER_WORKSPACE_REPO-}\" GITEA_PAT=\"$${GITEA_PAT-}\" GITHUB_PAT=\"$${GITHUB_PAT-}\" rvm_silence_path_mismatch_check_flag=1 RVM_SILENCE_PATH_MISMATCH_CHECK_FLAG=1 rvmsudo_secure_path=1 RVMSUDO_SECURE_PATH=1 bash -lc 'command -v python3 >/dev/null 2>&1 && python3 -c \"import sys; exit(0) if sys.version_info >= (3, 12) else exit(1)\"'; then",
|
|
" echo 'Python 3.12+ not detected for coder user; update the base image.'",
|
|
"fi",
|
|
"mkdir -p /workspaces",
|
|
"chown -R coder:coder /workspaces || echo 'Cannot change workspace ownership'",
|
|
"# SSL Certificate setup (running as root)",
|
|
"if [ -f /home/coder/lab-certs/lab.crt ]; then",
|
|
" echo 'Installing SSL certificate for lab.crt'",
|
|
" # Running as root, so direct access to system directories",
|
|
" cp /home/coder/lab-certs/lab.crt /usr/local/share/ca-certificates/lab.crt 2>/dev/null && echo 'SSL cert copied successfully' || echo 'Cannot copy SSL cert'",
|
|
" update-ca-certificates 2>/dev/null && echo 'CA certificates updated successfully' || echo 'Cannot update ca-certificates'",
|
|
" # Configure git globally for coder user",
|
|
" sudo --preserve-env=CODER_WORKSPACE_ID,CODER_WORKSPACE_REPO,GITEA_PAT,GITHUB_PAT -u coder env -i HOME=/home/coder PATH=\"$PATH\" GEM_HOME=/home/coder/.gem GEM_PATH=/home/coder/.gem CODER_WORKSPACE_ID=\"$${CODER_WORKSPACE_ID-}\" CODER_WORKSPACE_REPO=\"$${CODER_WORKSPACE_REPO-}\" GITEA_PAT=\"$${GITEA_PAT-}\" GITHUB_PAT=\"$${GITHUB_PAT-}\" rvm_silence_path_mismatch_check_flag=1 RVM_SILENCE_PATH_MISMATCH_CHECK_FLAG=1 rvmsudo_secure_path=1 RVMSUDO_SECURE_PATH=1 git config --global http.\"https://git.lab\".sslCAInfo /home/coder/lab-certs/lab.crt || echo 'Cannot configure git ssl'",
|
|
" sudo --preserve-env=CODER_WORKSPACE_ID,CODER_WORKSPACE_REPO,GITEA_PAT,GITHUB_PAT -u coder env -i HOME=/home/coder PATH=\"$PATH\" GEM_HOME=/home/coder/.gem GEM_PATH=/home/coder/.gem CODER_WORKSPACE_ID=\"$${CODER_WORKSPACE_ID-}\" CODER_WORKSPACE_REPO=\"$${CODER_WORKSPACE_REPO-}\" GITEA_PAT=\"$${GITEA_PAT-}\" GITHUB_PAT=\"$${GITHUB_PAT-}\" rvm_silence_path_mismatch_check_flag=1 RVM_SILENCE_PATH_MISMATCH_CHECK_FLAG=1 rvmsudo_secure_path=1 RVMSUDO_SECURE_PATH=1 git config --global http.\"https://git.lab\".sslVerify true || echo 'Cannot configure git ssl verify'",
|
|
"else",
|
|
" echo 'SSL cert not available at /home/coder/lab-certs/lab.crt'",
|
|
" # Configure git to skip SSL verification for git.lab if no cert available",
|
|
" sudo --preserve-env=CODER_WORKSPACE_ID,CODER_WORKSPACE_REPO,GITEA_PAT,GITHUB_PAT -u coder env -i HOME=/home/coder PATH=\"$PATH\" GEM_HOME=/home/coder/.gem GEM_PATH=/home/coder/.gem CODER_WORKSPACE_ID=\"$${CODER_WORKSPACE_ID-}\" CODER_WORKSPACE_REPO=\"$${CODER_WORKSPACE_REPO-}\" GITEA_PAT=\"$${GITEA_PAT-}\" GITHUB_PAT=\"$${GITHUB_PAT-}\" rvm_silence_path_mismatch_check_flag=1 RVM_SILENCE_PATH_MISMATCH_CHECK_FLAG=1 rvmsudo_secure_path=1 RVMSUDO_SECURE_PATH=1 git config --global http.\"https://git.lab\".sslVerify false || echo 'Cannot configure git ssl skip'",
|
|
"fi",
|
|
"# End SSL setup",
|
|
"export ENABLE_PGADMIN=${tostring(local.pgadmin_enabled)}",
|
|
"export ENABLE_MARIMO=${tostring(local.marimo_enabled)}",
|
|
local.port_forwarding ? "if [ -x /home/coder/code-tools/terraform/scripts/port-forward.sh ]; then bash /home/coder/code-tools/terraform/scripts/port-forward.sh; else echo \"Port forwarding script not found\"; fi" : "echo 'No service port forwarding requested'"
|
|
]))
|
|
}
|
|
|
|
# Workspace network keeps the workspace stack isolated from the host.
|
|
resource "docker_network" "workspace" {
|
|
name = "coder-${local.workspace_id}"
|
|
driver = "bridge"
|
|
|
|
labels {
|
|
label = "coder.workspace_id"
|
|
value = local.workspace_id
|
|
}
|
|
|
|
labels {
|
|
label = "coder.owner"
|
|
value = data.coder_workspace_owner.me.name
|
|
}
|
|
}
|
|
|
|
# Persistent workspace data volume mounted at /workspaces inside the container.
|
|
resource "docker_volume" "workspaces" {
|
|
name = "workspaces-${local.workspace_id}"
|
|
|
|
labels {
|
|
label = "coder.workspace_id"
|
|
value = local.workspace_id
|
|
}
|
|
|
|
labels {
|
|
label = "coder.type"
|
|
value = "workspace-data"
|
|
}
|
|
}
|
|
|
|
# Separate persistent home directory for the coder user.
|
|
# Base development container image (customise via terraform.tfvars).
|
|
resource "docker_image" "devcontainer" {
|
|
name = var.devcontainer_image
|
|
keep_locally = true
|
|
}
|