This commit is contained in:
2025-09-06 01:00:12 -04:00
parent ad9ed5a4e2
commit 84af75de5a
7 changed files with 210 additions and 58 deletions

View File

@@ -36,7 +36,7 @@ resource "coder_app" "terminal" {
}
# =============================================================================
# Service Port Forwarding for Docker Container Access
# Service Port Forwarding for Docker Container Access
# Note: Using direct container URLs since containers are on same Docker network
# =============================================================================
@@ -76,7 +76,7 @@ resource "coder_app" "qdrant" {
order = 11
healthcheck {
url = "http://qdrant-${local.workspace_id}:6333/api/cluster"
url = "http://qdrant-${local.workspace_id}:6333/health" # Use proper health endpoint
interval = 30
threshold = 10
}
@@ -270,4 +270,4 @@ resource "coder_app" "file_manager" {
icon = "/icon/folder.svg"
command = "bash -c 'export TERM=xterm-256color && cd /workspaces && ranger'"
order = 5
}
}

View File

@@ -103,7 +103,7 @@ data "coder_parameter" "enable_cursor_support" {
data "coder_parameter" "enable_windsurf_support" {
name = "enable_windsurf_support"
display_name = "Enable Windsurf IDE Support"
display_name = "Enable Windsurf IDE Support"
description = "Install Windsurf IDE configuration and settings"
type = "bool"
default = "true"
@@ -143,9 +143,11 @@ data "coder_parameter" "enable_pgadmin" {
# Local Variables
locals {
# Container and workspace naming
# Container and workspace naming - use ID for immutability
container_name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}"
workspace_id = data.coder_workspace.me.id
# Use workspace ID for volume naming to prevent destruction on rename
volume_suffix = substr(data.coder_workspace.me.id, 0, 8)
# Git configuration
git_author_name = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name)
@@ -175,9 +177,9 @@ locals {
"RUST_VERSION" = "stable"
# Service URLs (when services are enabled)
"POSTGRES_URL" = data.coder_parameter.enable_services.value ? "postgresql://postgres:${var.postgres_password}@postgres-${local.workspace_id}:5432/postgres" : ""
"REDIS_URL" = data.coder_parameter.enable_services.value ? "redis://:${var.redis_password}@redis-${local.workspace_id}:6379" : ""
"QDRANT_URL" = data.coder_parameter.enable_services.value ? "http://qdrant-${local.workspace_id}:6333" : ""
"POSTGRES_URL" = data.coder_parameter.enable_services.value ? "postgresql://postgres:${var.postgres_password}@postgres-${local.workspace_id}:5432/postgres" : ""
"REDIS_URL" = data.coder_parameter.enable_services.value ? "redis://:${var.redis_password}@redis-${local.workspace_id}:6379" : ""
"QDRANT_URL" = data.coder_parameter.enable_services.value ? "http://qdrant-${local.workspace_id}:6333" : ""
# Development configuration
"EDITOR" = "code"
@@ -233,4 +235,4 @@ resource "docker_volume" "workspaces" {
resource "docker_image" "devcontainer" {
name = local.devcontainer_image
keep_locally = true
}
}

View File

@@ -14,7 +14,25 @@ resource "coder_script" "claude_code_setup" {
icon = "/icon/ai.svg"
run_on_start = true
script = "bash /home/coder/resources/tf/scripts/claude-install.sh"
script = <<-EOT
#!/bin/bash
set -e
echo "Installing Claude Code CLI..."
# Check if Claude is already installed
if command -v claude &> /dev/null; then
echo "Claude Code CLI is already installed"
exit 0
fi
# Install Claude Code CLI
curl -fsSL https://claude.ai/install.sh | sh || {
echo "Warning: Claude Code CLI installation failed or not available"
exit 0
}
echo "Claude Code CLI installation complete"
EOT
}
# =============================================================================
@@ -28,7 +46,28 @@ resource "coder_script" "cursor_setup" {
icon = "/icon/code.svg"
run_on_start = true
script = "bash /home/coder/resources/tf/scripts/cursor-setup.sh"
script = <<-EOT
#!/bin/bash
set -e
echo "Configuring Cursor IDE support..."
# Create Cursor configuration directory
mkdir -p ~/.config/cursor
# Add Cursor-specific settings
cat > ~/.config/cursor/settings.json <<'EOF'
{
"workspaceFolder": "/workspaces",
"remote.SSH.defaultExtensions": [
"ms-python.python",
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint"
]
}
EOF
echo "Cursor IDE support configured"
EOT
}
# =============================================================================
@@ -42,7 +81,24 @@ resource "coder_script" "windsurf_setup" {
icon = "/icon/code.svg"
run_on_start = true
script = "bash /home/coder/resources/tf/scripts/windsurf-setup.sh"
script = <<-EOT
#!/bin/bash
set -e
echo "Configuring Windsurf IDE support..."
# Create Windsurf configuration directory
mkdir -p ~/.config/windsurf
# Add Windsurf-specific settings
cat > ~/.config/windsurf/settings.json <<'EOF'
{
"workspaceFolder": "/workspaces",
"codeium.enabled": true
}
EOF
echo "Windsurf IDE support configured"
EOT
}
# =============================================================================
@@ -55,7 +111,26 @@ resource "coder_script" "dev_extensions" {
icon = "/icon/tools.svg"
run_on_start = true
script = "bash /home/coder/resources/tf/scripts/dev-tools.sh"
script = <<-EOT
#!/bin/bash
set -e
echo "Installing development tools and extensions..."
# Install VS Code extensions if code-server is available
if command -v code-server &> /dev/null; then
code-server --install-extension ms-python.python || true
code-server --install-extension esbenp.prettier-vscode || true
code-server --install-extension dbaeumer.vscode-eslint || true
code-server --install-extension ms-azuretools.vscode-docker || true
fi
# Install additional development tools
if ! command -v ranger &> /dev/null; then
sudo apt-get update && sudo apt-get install -y ranger || true
fi
echo "Development tools installation complete"
EOT
}
# =============================================================================
@@ -68,5 +143,27 @@ resource "coder_script" "git_hooks_setup" {
icon = "/icon/git.svg"
run_on_start = true
script = "bash /home/coder/resources/tf/scripts/git-hooks.sh"
}
script = <<-EOT
#!/bin/bash
set -e
echo "Setting up Git hooks and configuration..."
# Configure git
git config --global user.name "${local.git_author_name}"
git config --global user.email "${local.git_author_email}"
git config --global init.defaultBranch main
# Create git hooks directory
mkdir -p ~/.git-hooks
# Add a simple pre-commit hook template
cat > ~/.git-hooks/pre-commit <<'EOF'
#!/bin/bash
# Pre-commit hook
echo "Running pre-commit checks..."
EOF
chmod +x ~/.git-hooks/pre-commit
echo "Git hooks setup complete"
EOT
}

View File

@@ -28,6 +28,10 @@ resource "docker_container" "postgres" {
image = "postgres:${var.postgres_version}-alpine"
name = "postgres-${local.workspace_id}"
# Resource limits for better isolation
memory = 2048 * 1024 * 1024 # 2GB
cpu_shares = 512 # Medium priority
# PostgreSQL configuration
env = [
"POSTGRES_DB=postgres",
@@ -49,12 +53,13 @@ resource "docker_container" "postgres" {
container_path = "/var/lib/postgresql/data"
}
# Health check
# Health check with proper timing for database startup
healthcheck {
test = ["CMD-SHELL", "pg_isready -U postgres"]
interval = "15s"
timeout = "5s"
retries = 5
test = ["CMD-SHELL", "pg_isready -U postgres"]
interval = "15s"
timeout = "5s"
retries = 5
start_period = "30s" # Give PostgreSQL time to initialize
}
restart = "unless-stopped"
@@ -94,6 +99,10 @@ resource "docker_container" "redis" {
image = "redis:${var.redis_version}-alpine"
name = "redis-${local.workspace_id}"
# Resource limits for better isolation
memory = 1024 * 1024 * 1024 # 1GB
cpu_shares = 256 # Lower priority
# Redis configuration with authentication
command = [
"redis-server",
@@ -114,12 +123,13 @@ resource "docker_container" "redis" {
container_path = "/data"
}
# Health check with authentication
# Health check with authentication and proper timing
healthcheck {
test = ["CMD", "redis-cli", "-a", var.redis_password, "ping"]
interval = "15s"
timeout = "3s"
retries = 5
test = ["CMD", "redis-cli", "-a", var.redis_password, "ping"]
interval = "15s"
timeout = "3s"
retries = 5
start_period = "10s" # Redis starts quickly
}
restart = "unless-stopped"
@@ -159,10 +169,14 @@ resource "docker_container" "qdrant" {
image = "qdrant/qdrant:${var.qdrant_version}"
name = "qdrant-${local.workspace_id}"
# Resource limits for better isolation
memory = 2048 * 1024 * 1024 # 2GB for vector operations
cpu_shares = 512 # Medium priority
# Qdrant configuration
env = [
"QDRANT__SERVICE__HTTP_PORT=6333",
"QDRANT__SERVICE__GRPC_PORT=6334",
"QDRANT__SERVICE__GRPC_PORT=6334",
"QDRANT__SERVICE__HOST=0.0.0.0",
"QDRANT__LOG_LEVEL=INFO"
]
@@ -177,12 +191,13 @@ resource "docker_container" "qdrant" {
container_path = "/qdrant/storage"
}
# Health check using simple file existence check (Qdrant creates lock files when running)
# Health check using Qdrant's HTTP endpoint with proper timing
healthcheck {
test = ["CMD-SHELL", "test -d /qdrant/storage && test -f /qdrant/qdrant || exit 1"]
interval = "20s"
timeout = "5s"
retries = 5
test = ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:6333/health || exit 1"]
interval = "20s"
timeout = "5s"
retries = 5
start_period = "40s" # Qdrant needs time to initialize storage
}
restart = "unless-stopped"
@@ -244,10 +259,10 @@ resource "docker_container" "pgadmin" {
# Health check for pgAdmin web interface
healthcheck {
test = ["CMD-SHELL", "curl -f http://localhost:80/misc/ping || wget --no-verbose --tries=1 --spider http://localhost:80/misc/ping || exit 1"]
interval = "30s"
timeout = "10s"
retries = 3
test = ["CMD-SHELL", "nc -z localhost 80 || exit 1"]
interval = "30s"
timeout = "10s"
retries = 3
start_period = "60s"
}
@@ -325,4 +340,4 @@ resource "docker_container" "jupyter" {
label = "coder.workspace_id"
value = local.workspace_id
}
}
}

View File

@@ -102,4 +102,4 @@ common_tags = {
Environment = "development"
ManagedBy = "terraform"
Purpose = "remote-development"
}
}

View File

@@ -307,6 +307,24 @@ variable "enable_jupyter" {
default = false
}
variable "enable_docker_in_docker" {
description = "Enable Docker-in-Docker by mounting the Docker socket (Consider using Sysbox for better security)"
type = bool
default = true
}
variable "use_sysbox_runtime" {
description = "Use Sysbox runtime for secure container isolation (requires Sysbox installed on host)"
type = bool
default = false
}
variable "block_file_transfer" {
description = "Block file transfer commands (scp, rsync, ftp, nc) to prevent data exfiltration"
type = bool
default = false
}
# =============================================================================
# Common Tags
# =============================================================================
@@ -319,4 +337,4 @@ variable "common_tags" {
ManagedBy = "terraform"
Purpose = "remote-development"
}
}
}

View File

@@ -24,17 +24,19 @@ resource "coder_agent" "main" {
"HOME" = "/home/coder"
"USER" = "coder"
# Service URLs for development
"POSTGRES_URL" = data.coder_parameter.enable_services.value ? "postgresql://postgres:${var.postgres_password}@postgres-${local.workspace_id}:5432/postgres" : ""
"REDIS_URL" = data.coder_parameter.enable_services.value ? "redis://:${var.redis_password}@redis-${local.workspace_id}:6379" : ""
"QDRANT_URL" = data.coder_parameter.enable_services.value ? "http://qdrant-${local.workspace_id}:6333" : ""
"POSTGRES_URL" = data.coder_parameter.enable_services.value ? "postgresql://postgres:${var.postgres_password}@postgres-${local.workspace_id}:5432/postgres" : ""
"REDIS_URL" = data.coder_parameter.enable_services.value ? "redis://:${var.redis_password}@redis-${local.workspace_id}:6379" : ""
"QDRANT_URL" = data.coder_parameter.enable_services.value ? "http://qdrant-${local.workspace_id}:6333" : ""
# Additional environment variables for scripts
"ENABLE_SERVICES" = tostring(data.coder_parameter.enable_services.value)
"ENABLE_SERVICES" = tostring(data.coder_parameter.enable_services.value)
# Security: Block file transfer commands to prevent data exfiltration
"CODER_AGENT_BLOCK_FILE_TRANSFER" = var.block_file_transfer ? "1" : ""
}
# Reference bind-mounted startup script plus service port forwarding
startup_script = <<-EOT
bash /home/coder/resources/tf/scripts/workspace-setup.sh
# Register JetBrains Gateway backend location if enabled
if [ "${data.coder_parameter.enable_jetbrains.value}" = "true" ] && [ -d ~/JetBrains ]; then
~/JetBrains/*/bin/remote-dev-server.sh registerBackendLocationForGateway 2>/dev/null || echo "JetBrains Gateway registration skipped"
@@ -75,6 +77,27 @@ resource "coder_agent" "main" {
}
}
# =============================================================================
# Persistent Home Volume for Development Container
# =============================================================================
resource "docker_volume" "coder_home" {
name = "coder-home-${local.workspace_id}"
labels {
label = "coder.workspace_id"
value = local.workspace_id
}
labels {
label = "coder.owner"
value = data.coder_workspace_owner.me.name
}
labels {
label = "coder.type"
value = "home-directory"
}
}
# =============================================================================
# Main Development Container
# =============================================================================
@@ -109,7 +132,7 @@ resource "docker_container" "workspace" {
host = "host.docker.internal"
ip = "host-gateway"
}
# No port mappings needed - reverse proxy will handle routing
# All services run within the isolated workspace network
# Coder's port forwarding and apps will provide access via reverse proxy
@@ -122,24 +145,20 @@ resource "docker_container" "workspace" {
read_only = false
}
# Mount the existing coder-home volume for user data persistence
# Mount a dynamically created home volume for user data persistence
volumes {
container_path = "/home/coder"
volume_name = "bwk8ckcok8o84cc0o4os4sso_coder-home"
volume_name = docker_volume.coder_home.name
read_only = false
}
# Bind mount code-tools directory for live script updates
volumes {
host_path = "/home/trav/code-tools"
container_path = "/home/coder/resources"
read_only = true
}
# Docker socket for Docker-in-Docker
volumes {
host_path = "/var/run/docker.sock"
container_path = "/var/run/docker.sock"
# Docker socket for Docker-in-Docker (optional)
dynamic "volumes" {
for_each = var.enable_docker_in_docker ? [1] : []
content {
host_path = "/var/run/docker.sock"
container_path = "/var/run/docker.sock"
}
}
# Working directory
@@ -166,6 +185,7 @@ resource "docker_container" "workspace" {
depends_on = [
docker_network.workspace,
docker_volume.workspaces,
docker_volume.coder_home,
docker_image.devcontainer
]
}
@@ -221,4 +241,4 @@ module "jetbrains_gateway" {
version = "2025.1"
}
}
}
}