Merge pull request 'master' (#1) from master into main
Reviewed-on: http://git.lab/vasceannie/code-tools/pulls/1
This commit was merged in pull request #1.
This commit is contained in:
115
coder-compose.yaml
Normal file
115
coder-compose.yaml
Normal file
@@ -0,0 +1,115 @@
|
||||
services:
|
||||
coder:
|
||||
user: root
|
||||
group_add:
|
||||
- 988
|
||||
image: 'ghcr.io/coder/coder:${CODER_VERSION:-latest}'
|
||||
expose:
|
||||
- '7080'
|
||||
environment:
|
||||
CODER_PG_CONNECTION_URL: 'postgresql://${POSTGRES_USER:-username}:${POSTGRES_PASSWORD:-password}@database/${POSTGRES_DB:-coder}?sslmode=disable'
|
||||
CODER_HTTP_ADDRESS: '0.0.0.0:7080'
|
||||
CODER_ACCESS_URL: '${CODER_ACCESS_URL}'
|
||||
CODER_WILDCARD_ACCESS_URL: '${CODER_WILDCARD_ACCESS_URL}'
|
||||
CODER_ADDRESS: '${CODER_ADDRESS}'
|
||||
COOLIFY_RESOURCE_UUID: bwk8ckcok8o84cc0o4os4sso
|
||||
COOLIFY_CONTAINER_NAME: coder-bwk8ckcok8o84cc0o4os4sso
|
||||
COOLIFY_URL: 'http://dev.lab'
|
||||
COOLIFY_FQDN: dev.lab
|
||||
volumes:
|
||||
- '/var/run/docker.sock:/var/run/docker.sock'
|
||||
- 'bwk8ckcok8o84cc0o4os4sso_coder-home:/home/coder'
|
||||
- /home/trav/code-tools:/home/coder/resources
|
||||
depends_on:
|
||||
database:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD-SHELL
|
||||
- 'curl -f -s http://localhost:7080/healthz || exit 1'
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 60s
|
||||
labels:
|
||||
- glance.name=Coder
|
||||
- 'glance.icon=https://cdn.jsdelivr.net/gh/selfhst/icons/webp/coder.webp'
|
||||
- 'glance.url=http://dev.lab'
|
||||
- 'glance.description=Mad Science Lab'
|
||||
- glance.id=coder
|
||||
- glance.category=dev
|
||||
- glance.hide=false
|
||||
- coolify.managed=true
|
||||
- coolify.version=4.0.0-beta.420.6
|
||||
- coolify.serviceId=41
|
||||
- coolify.type=service
|
||||
- coolify.name=coder-bwk8ckcok8o84cc0o4os4sso
|
||||
- coolify.resourceName=coder
|
||||
- coolify.projectName=development
|
||||
- coolify.serviceName=coder
|
||||
- coolify.environmentName=production
|
||||
- coolify.pullRequestId=0
|
||||
- coolify.service.subId=308
|
||||
- coolify.service.subType=application
|
||||
- coolify.service.subName=coder
|
||||
- traefik.enable=true
|
||||
- traefik.http.middlewares.gzip.compress=true
|
||||
- traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https
|
||||
- traefik.http.routers.http-0-bwk8ckcok8o84cc0o4os4sso-coder.entryPoints=http
|
||||
- traefik.http.routers.http-0-bwk8ckcok8o84cc0o4os4sso-coder.middlewares=gzip
|
||||
- 'traefik.http.routers.http-0-bwk8ckcok8o84cc0o4os4sso-coder.rule=Host(`dev.lab`) && PathPrefix(`/`)'
|
||||
- traefik.http.routers.http-0-bwk8ckcok8o84cc0o4os4sso-coder.service=http-0-bwk8ckcok8o84cc0o4os4sso-coder
|
||||
- traefik.http.services.http-0-bwk8ckcok8o84cc0o4os4sso-coder.loadbalancer.server.port=7080
|
||||
container_name: coder-bwk8ckcok8o84cc0o4os4sso
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
bwk8ckcok8o84cc0o4os4sso: null
|
||||
database:
|
||||
image: 'postgres:17'
|
||||
environment:
|
||||
POSTGRES_USER: '${POSTGRES_USER:-username}'
|
||||
POSTGRES_PASSWORD: '${POSTGRES_PASSWORD:-password}'
|
||||
POSTGRES_DB: '${POSTGRES_DB:-coder}'
|
||||
COOLIFY_RESOURCE_UUID: bwk8ckcok8o84cc0o4os4sso
|
||||
COOLIFY_CONTAINER_NAME: database-bwk8ckcok8o84cc0o4os4sso
|
||||
volumes:
|
||||
- 'bwk8ckcok8o84cc0o4os4sso_coder-data:/var/lib/postgresql/data'
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD-SHELL
|
||||
- 'pg_isready -U ${POSTGRES_USER:-username} -d ${POSTGRES_DB:-coder}'
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
labels:
|
||||
- glance.name=Postgres-Coder
|
||||
- glance.parent=coder
|
||||
- glance.hide=false
|
||||
- coolify.managed=true
|
||||
- coolify.version=4.0.0-beta.420.6
|
||||
- coolify.serviceId=41
|
||||
- coolify.type=service
|
||||
- coolify.name=database-bwk8ckcok8o84cc0o4os4sso
|
||||
- coolify.resourceName=coder
|
||||
- coolify.projectName=development
|
||||
- coolify.serviceName=database
|
||||
- coolify.environmentName=production
|
||||
- coolify.pullRequestId=0
|
||||
- coolify.service.subId=38
|
||||
- coolify.service.subType=database
|
||||
- coolify.service.subName=database
|
||||
container_name: database-bwk8ckcok8o84cc0o4os4sso
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
bwk8ckcok8o84cc0o4os4sso: null
|
||||
volumes:
|
||||
bwk8ckcok8o84cc0o4os4sso_coder-home:
|
||||
name: bwk8ckcok8o84cc0o4os4sso_coder-home
|
||||
bwk8ckcok8o84cc0o4os4sso_coder-data:
|
||||
name: bwk8ckcok8o84cc0o4os4sso_coder-data
|
||||
networks:
|
||||
bwk8ckcok8o84cc0o4os4sso:
|
||||
name: bwk8ckcok8o84cc0o4os4sso
|
||||
external: true
|
||||
configs: { }
|
||||
secrets: { }
|
||||
6
main.py
Normal file
6
main.py
Normal file
@@ -0,0 +1,6 @@
|
||||
def main():
|
||||
print("Hello from code-tools!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
7
pyproject.toml
Normal file
7
pyproject.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[project]
|
||||
name = "code-tools"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = []
|
||||
133
tf/.terraform/modules/jetbrains_gateway/README.md
Normal file
133
tf/.terraform/modules/jetbrains_gateway/README.md
Normal file
@@ -0,0 +1,133 @@
|
||||
---
|
||||
display_name: JetBrains Gateway
|
||||
description: Add a one-click button to launch JetBrains Gateway IDEs in the dashboard.
|
||||
icon: ../.icons/gateway.svg
|
||||
maintainer_github: coder
|
||||
verified: true
|
||||
tags: [ide, jetbrains, helper, parameter]
|
||||
---
|
||||
|
||||
# JetBrains Gateway
|
||||
|
||||
This module adds a JetBrains Gateway Button to open any workspace with a single click.
|
||||
|
||||
JetBrains recommends a minimum of 4 CPU cores and 8GB of RAM.
|
||||
Consult the [JetBrains documentation](https://www.jetbrains.com/help/idea/prerequisites.html#min_requirements) to confirm other system requirements.
|
||||
|
||||
```tf
|
||||
module "jetbrains_gateway" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/modules/jetbrains-gateway/coder"
|
||||
version = "1.0.28"
|
||||
agent_id = coder_agent.example.id
|
||||
folder = "/home/coder/example"
|
||||
jetbrains_ides = ["CL", "GO", "IU", "PY", "WS"]
|
||||
default = "GO"
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Examples
|
||||
|
||||
### Add GoLand and WebStorm as options with the default set to GoLand
|
||||
|
||||
```tf
|
||||
module "jetbrains_gateway" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/modules/jetbrains-gateway/coder"
|
||||
version = "1.0.28"
|
||||
agent_id = coder_agent.example.id
|
||||
folder = "/home/coder/example"
|
||||
jetbrains_ides = ["GO", "WS"]
|
||||
default = "GO"
|
||||
}
|
||||
```
|
||||
|
||||
### Use the latest version of each IDE
|
||||
|
||||
```tf
|
||||
module "jetbrains_gateway" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/modules/jetbrains-gateway/coder"
|
||||
version = "1.0.28"
|
||||
agent_id = coder_agent.example.id
|
||||
folder = "/home/coder/example"
|
||||
jetbrains_ides = ["IU", "PY"]
|
||||
default = "IU"
|
||||
latest = true
|
||||
}
|
||||
```
|
||||
|
||||
### Use fixed versions set by `jetbrains_ide_versions`
|
||||
|
||||
```tf
|
||||
module "jetbrains_gateway" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/modules/jetbrains-gateway/coder"
|
||||
version = "1.0.28"
|
||||
agent_id = coder_agent.example.id
|
||||
folder = "/home/coder/example"
|
||||
jetbrains_ides = ["IU", "PY"]
|
||||
default = "IU"
|
||||
latest = false
|
||||
jetbrains_ide_versions = {
|
||||
"IU" = {
|
||||
build_number = "243.21565.193"
|
||||
version = "2024.3"
|
||||
}
|
||||
"PY" = {
|
||||
build_number = "243.21565.199"
|
||||
version = "2024.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Use the latest EAP version
|
||||
|
||||
```tf
|
||||
module "jetbrains_gateway" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/modules/jetbrains-gateway/coder"
|
||||
version = "1.0.28"
|
||||
agent_id = coder_agent.example.id
|
||||
folder = "/home/coder/example"
|
||||
jetbrains_ides = ["GO", "WS"]
|
||||
default = "GO"
|
||||
latest = true
|
||||
channel = "eap"
|
||||
}
|
||||
```
|
||||
|
||||
### Custom base link
|
||||
|
||||
Due to the highest priority of the `ide_download_link` parameter in the `(jetbrains-gateway://...` within IDEA, the pre-configured download address will be overridden when using [IDEA's offline mode](https://www.jetbrains.com/help/idea/fully-offline-mode.html). Therefore, it is necessary to configure the `download_base_link` parameter for the `jetbrains_gateway` module to change the value of `ide_download_link`.
|
||||
|
||||
```tf
|
||||
module "jetbrains_gateway" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/modules/jetbrains-gateway/coder"
|
||||
version = "1.0.28"
|
||||
agent_id = coder_agent.example.id
|
||||
folder = "/home/coder/example"
|
||||
jetbrains_ides = ["GO", "WS"]
|
||||
releases_base_link = "https://releases.internal.site/"
|
||||
download_base_link = "https://download.internal.site/"
|
||||
default = "GO"
|
||||
}
|
||||
```
|
||||
|
||||
## Supported IDEs
|
||||
|
||||
This module and JetBrains Gateway support the following JetBrains IDEs:
|
||||
|
||||
- [GoLand (`GO`)](https://www.jetbrains.com/go/)
|
||||
- [WebStorm (`WS`)](https://www.jetbrains.com/webstorm/)
|
||||
- [IntelliJ IDEA Ultimate (`IU`)](https://www.jetbrains.com/idea/)
|
||||
- [PyCharm Professional (`PY`)](https://www.jetbrains.com/pycharm/)
|
||||
- [PhpStorm (`PS`)](https://www.jetbrains.com/phpstorm/)
|
||||
- [CLion (`CL`)](https://www.jetbrains.com/clion/)
|
||||
- [RubyMine (`RM`)](https://www.jetbrains.com/ruby/)
|
||||
- [Rider (`RD`)](https://www.jetbrains.com/rider/)
|
||||
- [RustRover (`RR`)](https://www.jetbrains.com/rust/)
|
||||
43
tf/.terraform/modules/jetbrains_gateway/main.test.ts
Normal file
43
tf/.terraform/modules/jetbrains_gateway/main.test.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { it, expect, describe } from "bun:test";
|
||||
import {
|
||||
runTerraformInit,
|
||||
testRequiredVariables,
|
||||
runTerraformApply,
|
||||
} from "../test";
|
||||
|
||||
describe("jetbrains-gateway", async () => {
|
||||
await runTerraformInit(import.meta.dir);
|
||||
|
||||
await testRequiredVariables(import.meta.dir, {
|
||||
agent_id: "foo",
|
||||
folder: "/home/foo",
|
||||
});
|
||||
|
||||
it("should create a link with the default values", async () => {
|
||||
const state = await runTerraformApply(import.meta.dir, {
|
||||
// These are all required.
|
||||
agent_id: "foo",
|
||||
folder: "/home/coder",
|
||||
});
|
||||
expect(state.outputs.url.value).toBe(
|
||||
"jetbrains-gateway://connect#type=coder&workspace=default&owner=default&folder=/home/coder&url=https://mydeployment.coder.com&token=$SESSION_TOKEN&ide_product_code=IU&ide_build_number=243.21565.193&ide_download_link=https://download.jetbrains.com/idea/ideaIU-2024.3.tar.gz",
|
||||
);
|
||||
|
||||
const coder_app = state.resources.find(
|
||||
(res) => res.type === "coder_app" && res.name === "gateway",
|
||||
);
|
||||
|
||||
expect(coder_app).not.toBeNull();
|
||||
expect(coder_app?.instances.length).toBe(1);
|
||||
expect(coder_app?.instances[0].attributes.order).toBeNull();
|
||||
});
|
||||
|
||||
it("default to first ide", async () => {
|
||||
const state = await runTerraformApply(import.meta.dir, {
|
||||
agent_id: "foo",
|
||||
folder: "/home/foo",
|
||||
jetbrains_ides: '["IU", "GO", "PY"]',
|
||||
});
|
||||
expect(state.outputs.identifier.value).toBe("IU");
|
||||
});
|
||||
});
|
||||
341
tf/.terraform/modules/jetbrains_gateway/main.tf
Normal file
341
tf/.terraform/modules/jetbrains_gateway/main.tf
Normal file
@@ -0,0 +1,341 @@
|
||||
terraform {
|
||||
required_version = ">= 1.0"
|
||||
|
||||
required_providers {
|
||||
coder = {
|
||||
source = "coder/coder"
|
||||
version = ">= 0.17"
|
||||
}
|
||||
http = {
|
||||
source = "hashicorp/http"
|
||||
version = ">= 3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
variable "agent_id" {
|
||||
type = string
|
||||
description = "The ID of a Coder agent."
|
||||
}
|
||||
|
||||
variable "slug" {
|
||||
type = string
|
||||
description = "The slug for the coder_app. Allows resuing the module with the same template."
|
||||
default = "gateway"
|
||||
}
|
||||
|
||||
variable "agent_name" {
|
||||
type = string
|
||||
description = "Agent name. (unused). Will be removed in a future version"
|
||||
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "folder" {
|
||||
type = string
|
||||
description = "The directory to open in the IDE. e.g. /home/coder/project"
|
||||
validation {
|
||||
condition = can(regex("^(?:/[^/]+)+$", var.folder))
|
||||
error_message = "The folder must be a full path and must not start with a ~."
|
||||
}
|
||||
}
|
||||
|
||||
variable "default" {
|
||||
default = ""
|
||||
type = string
|
||||
description = "Default IDE"
|
||||
}
|
||||
|
||||
variable "order" {
|
||||
type = number
|
||||
description = "The order determines the position of app in the UI presentation. The lowest order is shown first and apps with equal order are sorted by name (ascending order)."
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "coder_parameter_order" {
|
||||
type = number
|
||||
description = "The order determines the position of a template parameter in the UI/CLI presentation. The lowest order is shown first and parameters with equal order are sorted by name (ascending order)."
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "latest" {
|
||||
type = bool
|
||||
description = "Whether to fetch the latest version of the IDE."
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "channel" {
|
||||
type = string
|
||||
description = "JetBrains IDE release channel. Valid values are release and eap."
|
||||
default = "release"
|
||||
validation {
|
||||
condition = can(regex("^(release|eap)$", var.channel))
|
||||
error_message = "The channel must be either release or eap."
|
||||
}
|
||||
}
|
||||
|
||||
variable "jetbrains_ide_versions" {
|
||||
type = map(object({
|
||||
build_number = string
|
||||
version = string
|
||||
}))
|
||||
description = "The set of versions for each jetbrains IDE"
|
||||
default = {
|
||||
"IU" = {
|
||||
build_number = "243.21565.193"
|
||||
version = "2024.3"
|
||||
}
|
||||
"PS" = {
|
||||
build_number = "243.21565.202"
|
||||
version = "2024.3"
|
||||
}
|
||||
"WS" = {
|
||||
build_number = "243.21565.180"
|
||||
version = "2024.3"
|
||||
}
|
||||
"PY" = {
|
||||
build_number = "243.21565.199"
|
||||
version = "2024.3"
|
||||
}
|
||||
"CL" = {
|
||||
build_number = "243.21565.238"
|
||||
version = "2024.1"
|
||||
}
|
||||
"GO" = {
|
||||
build_number = "243.21565.208"
|
||||
version = "2024.3"
|
||||
}
|
||||
"RM" = {
|
||||
build_number = "243.21565.197"
|
||||
version = "2024.3"
|
||||
}
|
||||
"RD" = {
|
||||
build_number = "243.21565.191"
|
||||
version = "2024.3"
|
||||
}
|
||||
"RR" = {
|
||||
build_number = "243.22562.230"
|
||||
version = "2024.3"
|
||||
}
|
||||
}
|
||||
validation {
|
||||
condition = (
|
||||
alltrue([
|
||||
for code in keys(var.jetbrains_ide_versions) : contains(["IU", "PS", "WS", "PY", "CL", "GO", "RM", "RD", "RR"], code)
|
||||
])
|
||||
)
|
||||
error_message = "The jetbrains_ide_versions must contain a map of valid product codes. Valid product codes are ${join(",", ["IU", "PS", "WS", "PY", "CL", "GO", "RM", "RD", "RR"])}."
|
||||
}
|
||||
}
|
||||
|
||||
variable "jetbrains_ides" {
|
||||
type = list(string)
|
||||
description = "The list of IDE product codes."
|
||||
default = ["IU", "PS", "WS", "PY", "CL", "GO", "RM", "RD", "RR"]
|
||||
validation {
|
||||
condition = (
|
||||
alltrue([
|
||||
for code in var.jetbrains_ides : contains(["IU", "PS", "WS", "PY", "CL", "GO", "RM", "RD", "RR"], code)
|
||||
])
|
||||
)
|
||||
error_message = "The jetbrains_ides must be a list of valid product codes. Valid product codes are ${join(",", ["IU", "PS", "WS", "PY", "CL", "GO", "RM", "RD", "RR"])}."
|
||||
}
|
||||
# check if the list is empty
|
||||
validation {
|
||||
condition = length(var.jetbrains_ides) > 0
|
||||
error_message = "The jetbrains_ides must not be empty."
|
||||
}
|
||||
# check if the list contains duplicates
|
||||
validation {
|
||||
condition = length(var.jetbrains_ides) == length(toset(var.jetbrains_ides))
|
||||
error_message = "The jetbrains_ides must not contain duplicates."
|
||||
}
|
||||
}
|
||||
|
||||
variable "releases_base_link" {
|
||||
type = string
|
||||
description = ""
|
||||
default = "https://data.services.jetbrains.com"
|
||||
validation {
|
||||
condition = can(regex("^https?://.+$", var.releases_base_link))
|
||||
error_message = "The releases_base_link must be a valid HTTP/S address."
|
||||
}
|
||||
}
|
||||
|
||||
variable "download_base_link" {
|
||||
type = string
|
||||
description = ""
|
||||
default = "https://download.jetbrains.com"
|
||||
validation {
|
||||
condition = can(regex("^https?://.+$", var.download_base_link))
|
||||
error_message = "The download_base_link must be a valid HTTP/S address."
|
||||
}
|
||||
}
|
||||
|
||||
data "http" "jetbrains_ide_versions" {
|
||||
for_each = var.latest ? toset(var.jetbrains_ides) : toset([])
|
||||
url = "${var.releases_base_link}/products/releases?code=${each.key}&latest=true&type=${var.channel}"
|
||||
}
|
||||
|
||||
locals {
|
||||
jetbrains_ides = {
|
||||
"GO" = {
|
||||
icon = "/icon/goland.svg",
|
||||
name = "GoLand",
|
||||
identifier = "GO",
|
||||
build_number = var.jetbrains_ide_versions["GO"].build_number,
|
||||
download_link = "${var.download_base_link}/go/goland-${var.jetbrains_ide_versions["GO"].version}.tar.gz"
|
||||
version = var.jetbrains_ide_versions["GO"].version
|
||||
},
|
||||
"WS" = {
|
||||
icon = "/icon/webstorm.svg",
|
||||
name = "WebStorm",
|
||||
identifier = "WS",
|
||||
build_number = var.jetbrains_ide_versions["WS"].build_number,
|
||||
download_link = "${var.download_base_link}/webstorm/WebStorm-${var.jetbrains_ide_versions["WS"].version}.tar.gz"
|
||||
version = var.jetbrains_ide_versions["WS"].version
|
||||
},
|
||||
"IU" = {
|
||||
icon = "/icon/intellij.svg",
|
||||
name = "IntelliJ IDEA Ultimate",
|
||||
identifier = "IU",
|
||||
build_number = var.jetbrains_ide_versions["IU"].build_number,
|
||||
download_link = "${var.download_base_link}/idea/ideaIU-${var.jetbrains_ide_versions["IU"].version}.tar.gz"
|
||||
version = var.jetbrains_ide_versions["IU"].version
|
||||
},
|
||||
"PY" = {
|
||||
icon = "/icon/pycharm.svg",
|
||||
name = "PyCharm Professional",
|
||||
identifier = "PY",
|
||||
build_number = var.jetbrains_ide_versions["PY"].build_number,
|
||||
download_link = "${var.download_base_link}/python/pycharm-professional-${var.jetbrains_ide_versions["PY"].version}.tar.gz"
|
||||
version = var.jetbrains_ide_versions["PY"].version
|
||||
},
|
||||
"CL" = {
|
||||
icon = "/icon/clion.svg",
|
||||
name = "CLion",
|
||||
identifier = "CL",
|
||||
build_number = var.jetbrains_ide_versions["CL"].build_number,
|
||||
download_link = "${var.download_base_link}/cpp/CLion-${var.jetbrains_ide_versions["CL"].version}.tar.gz"
|
||||
version = var.jetbrains_ide_versions["CL"].version
|
||||
},
|
||||
"PS" = {
|
||||
icon = "/icon/phpstorm.svg",
|
||||
name = "PhpStorm",
|
||||
identifier = "PS",
|
||||
build_number = var.jetbrains_ide_versions["PS"].build_number,
|
||||
download_link = "${var.download_base_link}/webide/PhpStorm-${var.jetbrains_ide_versions["PS"].version}.tar.gz"
|
||||
version = var.jetbrains_ide_versions["PS"].version
|
||||
},
|
||||
"RM" = {
|
||||
icon = "/icon/rubymine.svg",
|
||||
name = "RubyMine",
|
||||
identifier = "RM",
|
||||
build_number = var.jetbrains_ide_versions["RM"].build_number,
|
||||
download_link = "${var.download_base_link}/ruby/RubyMine-${var.jetbrains_ide_versions["RM"].version}.tar.gz"
|
||||
version = var.jetbrains_ide_versions["RM"].version
|
||||
},
|
||||
"RD" = {
|
||||
icon = "/icon/rider.svg",
|
||||
name = "Rider",
|
||||
identifier = "RD",
|
||||
build_number = var.jetbrains_ide_versions["RD"].build_number,
|
||||
download_link = "${var.download_base_link}/rider/JetBrains.Rider-${var.jetbrains_ide_versions["RD"].version}.tar.gz"
|
||||
version = var.jetbrains_ide_versions["RD"].version
|
||||
},
|
||||
"RR" = {
|
||||
icon = "/icon/rustrover.svg",
|
||||
name = "RustRover",
|
||||
identifier = "RR",
|
||||
build_number = var.jetbrains_ide_versions["RR"].build_number,
|
||||
download_link = "${var.download_base_link}/rustrover/RustRover-${var.jetbrains_ide_versions["RR"].version}.tar.gz"
|
||||
version = var.jetbrains_ide_versions["RR"].version
|
||||
}
|
||||
}
|
||||
|
||||
icon = local.jetbrains_ides[data.coder_parameter.jetbrains_ide.value].icon
|
||||
json_data = var.latest ? jsondecode(data.http.jetbrains_ide_versions[data.coder_parameter.jetbrains_ide.value].response_body) : {}
|
||||
key = var.latest ? keys(local.json_data)[0] : ""
|
||||
display_name = local.jetbrains_ides[data.coder_parameter.jetbrains_ide.value].name
|
||||
identifier = data.coder_parameter.jetbrains_ide.value
|
||||
download_link = var.latest ? local.json_data[local.key][0].downloads.linux.link : local.jetbrains_ides[data.coder_parameter.jetbrains_ide.value].download_link
|
||||
build_number = var.latest ? local.json_data[local.key][0].build : local.jetbrains_ides[data.coder_parameter.jetbrains_ide.value].build_number
|
||||
version = var.latest ? local.json_data[local.key][0].version : var.jetbrains_ide_versions[data.coder_parameter.jetbrains_ide.value].version
|
||||
}
|
||||
|
||||
data "coder_parameter" "jetbrains_ide" {
|
||||
type = "string"
|
||||
name = "jetbrains_ide"
|
||||
display_name = "JetBrains IDE"
|
||||
icon = "/icon/gateway.svg"
|
||||
mutable = true
|
||||
default = var.default == "" ? var.jetbrains_ides[0] : var.default
|
||||
order = var.coder_parameter_order
|
||||
|
||||
dynamic "option" {
|
||||
for_each = var.jetbrains_ides
|
||||
content {
|
||||
icon = local.jetbrains_ides[option.value].icon
|
||||
name = local.jetbrains_ides[option.value].name
|
||||
value = option.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data "coder_workspace" "me" {}
|
||||
data "coder_workspace_owner" "me" {}
|
||||
|
||||
resource "coder_app" "gateway" {
|
||||
agent_id = var.agent_id
|
||||
slug = var.slug
|
||||
display_name = local.display_name
|
||||
icon = local.icon
|
||||
external = true
|
||||
order = var.order
|
||||
url = join("", [
|
||||
"jetbrains-gateway://connect#type=coder&workspace=",
|
||||
data.coder_workspace.me.name,
|
||||
"&owner=",
|
||||
data.coder_workspace_owner.me.name,
|
||||
"&folder=",
|
||||
var.folder,
|
||||
"&url=",
|
||||
data.coder_workspace.me.access_url,
|
||||
"&token=",
|
||||
"$SESSION_TOKEN",
|
||||
"&ide_product_code=",
|
||||
data.coder_parameter.jetbrains_ide.value,
|
||||
"&ide_build_number=",
|
||||
local.build_number,
|
||||
"&ide_download_link=",
|
||||
local.download_link,
|
||||
])
|
||||
}
|
||||
|
||||
output "identifier" {
|
||||
value = local.identifier
|
||||
}
|
||||
|
||||
output "display_name" {
|
||||
value = local.display_name
|
||||
}
|
||||
|
||||
output "icon" {
|
||||
value = local.icon
|
||||
}
|
||||
|
||||
output "download_link" {
|
||||
value = local.download_link
|
||||
}
|
||||
|
||||
output "build_number" {
|
||||
value = local.build_number
|
||||
}
|
||||
|
||||
output "version" {
|
||||
value = local.version
|
||||
}
|
||||
|
||||
output "url" {
|
||||
value = coder_app.gateway.url
|
||||
}
|
||||
1
tf/.terraform/modules/modules.json
Normal file
1
tf/.terraform/modules/modules.json
Normal file
@@ -0,0 +1 @@
|
||||
{"Modules":[{"Key":"","Source":"","Dir":"."},{"Key":"jetbrains_gateway","Source":"registry.coder.com/modules/jetbrains-gateway/coder","Version":"1.0.29","Dir":".terraform/modules/jetbrains_gateway"}]}
|
||||
@@ -0,0 +1,375 @@
|
||||
Copyright (c) 2017 HashiCorp, Inc.
|
||||
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
Binary file not shown.
446
tf/README.md
Normal file
446
tf/README.md
Normal file
@@ -0,0 +1,446 @@
|
||||
# Terraform Coder Development Environment
|
||||
|
||||
A comprehensive development environment deployment using Terraform and Coder, providing isolated workspaces with integrated development tools, databases, and AI-powered coding assistants.
|
||||
|
||||
## 🏗️ Architecture Overview
|
||||
|
||||
This configuration deploys self-contained development workspaces using Docker containers orchestrated by Coder. Each workspace includes:
|
||||
|
||||
- **Isolated Development Container** with VS Code, terminal access, and full development toolchain
|
||||
- **Database Services** (PostgreSQL, Redis, Qdrant) with persistent storage
|
||||
- **Management Interfaces** (pgAdmin, Qdrant Dashboard)
|
||||
- **AI Development Tools** (Claude Code, Cursor, Windsurf support)
|
||||
- **Reverse Proxy Integration** for seamless web access
|
||||
|
||||
### Network Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Reverse Proxy │
|
||||
│ http://dev.lab │
|
||||
│ *.dev.lab wildcard │
|
||||
└─────────────────────────┬───────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────────┼─────────────────────┐
|
||||
│ Workspace A │ Workspace B │
|
||||
│ network-{id-a} │ network-{id-b} │
|
||||
│ │ │
|
||||
│ ┌─────────────────┐ │ ┌─────────────────┐ │
|
||||
│ │ Dev Container │ │ │ Dev Container │ │
|
||||
│ │ VS Code:8080 │ │ │ VS Code:8080 │ │
|
||||
│ └─────────────────┘ │ └─────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌─────────────────┐ │ ┌─────────────────┐ │
|
||||
│ │ PostgreSQL:5432 │ │ │ PostgreSQL:5432 │ │
|
||||
│ │ Redis:6379 │ │ │ Redis:6379 │ │
|
||||
│ │ Qdrant:6333 │ │ │ Qdrant:6333 │ │
|
||||
│ │ pgAdmin:5050 │ │ │ pgAdmin:5050 │ │
|
||||
│ └─────────────────┘ │ └─────────────────┘ │
|
||||
└─────────────────────┼─────────────────────┘
|
||||
```
|
||||
|
||||
**Key Benefits:**
|
||||
- ✅ **Complete Network Isolation** between workspaces
|
||||
- ✅ **No Port Conflicts** - same ports used in different networks
|
||||
- ✅ **Scalable** - unlimited concurrent workspaces
|
||||
- ✅ **Secure** - services only accessible through authenticated Coder session
|
||||
|
||||
## 📁 File Structure
|
||||
|
||||
```
|
||||
tf/
|
||||
├── README.md # This file
|
||||
├── main.tf # Provider config, parameters, networks, volumes
|
||||
├── variables.tf # All configurable parameters
|
||||
├── terraform.tfvars # Variable assignments
|
||||
├── workspace.tf # Main development container and Coder agent
|
||||
├── services.tf # Database containers (PostgreSQL, Redis, Qdrant)
|
||||
├── apps.tf # Coder applications for service access
|
||||
├── scripts.tf # AI tools installation and configuration
|
||||
└── outputs.tf # Workspace information exports
|
||||
```
|
||||
|
||||
## 🛠️ Included Services & Tools
|
||||
|
||||
### Development Environment
|
||||
- **Container Base**: Microsoft DevContainers Universal image
|
||||
- **Languages**: Node.js 20, Python 3.12, Rust (latest stable)
|
||||
- **Package Managers**: npm, uv (Python), Cargo (Rust)
|
||||
- **System Tools**: make, tree, jq, curl, wget, build-essential
|
||||
|
||||
### Database Services
|
||||
- **PostgreSQL 17** with Alpine Linux base
|
||||
- Connection pooling and performance optimization
|
||||
- pg_stat_statements enabled for query analysis
|
||||
- PostgreSQL client tools included in workspace
|
||||
- **Redis 7** with Alpine Linux base
|
||||
- Authentication enabled with configurable password
|
||||
- AOF persistence with everysec fsync
|
||||
- LRU eviction policy with configurable memory limits
|
||||
- **Qdrant Vector Database** (latest)
|
||||
- HTTP API on port 6333, gRPC on port 6334
|
||||
- Persistent storage for vector collections
|
||||
- Web dashboard for collection management
|
||||
|
||||
### Management Interfaces
|
||||
- **pgAdmin 4** - PostgreSQL administration interface
|
||||
- **Qdrant Dashboard** - Vector database management
|
||||
- **VS Code Server** - Browser-based IDE
|
||||
- **Terminal Access** - Full bash shell access
|
||||
|
||||
### AI Development Tools
|
||||
- **Claude Code CLI** - Anthropic's official CLI
|
||||
- **Cursor Support** - AI-powered code editor integration
|
||||
- **Windsurf Support** - Codeium's development environment
|
||||
|
||||
### Development Packages
|
||||
|
||||
#### Node.js (Global)
|
||||
```javascript
|
||||
repomix, create-next-app, nodemon, concurrently
|
||||
@types/node, typescript, eslint, prettier
|
||||
```
|
||||
|
||||
#### Python (via uv)
|
||||
```python
|
||||
fastapi, uvicorn, requests, pandas, numpy
|
||||
psycopg2-binary, redis, qdrant-client, python-dotenv
|
||||
```
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### Prerequisites
|
||||
|
||||
1. **Coder Instance** running and accessible
|
||||
2. **Reverse Proxy** configured with wildcard subdomain support
|
||||
3. **Docker** daemon accessible to Coder
|
||||
4. **Terraform** >= 1.0 installed
|
||||
|
||||
### Environment Setup
|
||||
|
||||
Set these environment variables in your Coder deployment:
|
||||
```bash
|
||||
CODER_ACCESS_URL=http://dev.lab
|
||||
CODER_WILDCARD_ACCESS_URL=*.dev.lab
|
||||
```
|
||||
|
||||
### Deployment Steps
|
||||
|
||||
1. **Clone and Navigate**
|
||||
```bash
|
||||
git clone <your-repo>
|
||||
cd tf/
|
||||
```
|
||||
|
||||
2. **Review Configuration**
|
||||
```bash
|
||||
# Edit terraform.tfvars to match your needs
|
||||
vim terraform.tfvars
|
||||
```
|
||||
|
||||
3. **Deploy Infrastructure**
|
||||
```bash
|
||||
terraform init
|
||||
terraform plan
|
||||
terraform apply
|
||||
```
|
||||
|
||||
4. **Access Workspace**
|
||||
- Navigate to your Coder instance
|
||||
- Create new workspace using this template
|
||||
- Select your preferred Git repository
|
||||
- Choose whether to enable database services
|
||||
|
||||
## ⚙️ Configuration
|
||||
|
||||
### Key Variables (terraform.tfvars)
|
||||
|
||||
#### Resource Limits
|
||||
```hcl
|
||||
workspace_memory_limit = 16384 # 16GB RAM
|
||||
workspace_cpu_limit = 4 # 4 CPU cores
|
||||
```
|
||||
|
||||
#### Service Configuration
|
||||
```hcl
|
||||
# Database passwords (change in production!)
|
||||
postgres_password = "devpassword"
|
||||
redis_password = "devpassword"
|
||||
|
||||
# Database tuning
|
||||
postgres_max_connections = 100
|
||||
redis_max_memory = "512mb"
|
||||
```
|
||||
|
||||
#### Feature Toggles
|
||||
```hcl
|
||||
enable_pgadmin = true # PostgreSQL admin interface
|
||||
enable_monitoring = true # Resource monitoring
|
||||
enable_jupyter = false # Jupyter Lab for data science
|
||||
```
|
||||
|
||||
#### Tool Versions
|
||||
```hcl
|
||||
node_version = "20" # Node.js LTS
|
||||
python_version = "3.12" # Python latest stable
|
||||
postgres_version = "17" # PostgreSQL latest
|
||||
redis_version = "7" # Redis latest stable
|
||||
```
|
||||
|
||||
### Coder Parameters
|
||||
|
||||
When creating a workspace, you'll be prompted for:
|
||||
|
||||
1. **Git Repository** - Select from your available repositories
|
||||
2. **Enable Services** - Toggle database services on/off
|
||||
3. **Enable AI Tools** - Toggle AI development tool installation
|
||||
|
||||
## 🌐 Service Access
|
||||
|
||||
### Web Applications
|
||||
|
||||
All services are accessible through Coder's reverse proxy with subdomain routing:
|
||||
|
||||
| Service | URL Pattern | Description |
|
||||
|---------|-------------|-------------|
|
||||
| VS Code | `code-server-{workspace}.dev.lab` | Browser-based IDE |
|
||||
| Terminal | Available in Coder dashboard | Full bash shell |
|
||||
| pgAdmin | `pgadmin-{workspace}.dev.lab` | PostgreSQL management |
|
||||
| Qdrant | `qdrant-dashboard-{workspace}.dev.lab` | Vector DB dashboard |
|
||||
| Dev Server | `nextjs-3000-{workspace}.dev.lab` | Next.js dev server |
|
||||
| API Server | `api-8000-{workspace}.dev.lab` | FastAPI/Flask server |
|
||||
| Vite Dev | `vite-5173-{workspace}.dev.lab` | Vite development |
|
||||
|
||||
### Database Connections
|
||||
|
||||
From within your workspace container:
|
||||
|
||||
```bash
|
||||
# PostgreSQL
|
||||
export POSTGRES_URL="postgresql://postgres:devpassword@postgres-{workspace-id}:5432/postgres"
|
||||
psql $POSTGRES_URL
|
||||
|
||||
# Redis
|
||||
export REDIS_URL="redis://:devpassword@redis-{workspace-id}:6379"
|
||||
redis-cli -u $REDIS_URL
|
||||
|
||||
# Qdrant
|
||||
export QDRANT_URL="http://qdrant-{workspace-id}:6333"
|
||||
curl $QDRANT_URL/health
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
These are automatically set in your workspace:
|
||||
|
||||
```bash
|
||||
NODE_VERSION=20
|
||||
PYTHON_VERSION=3.12
|
||||
POSTGRES_URL=postgresql://postgres:***@postgres-{id}:5432/postgres
|
||||
REDIS_URL=redis://:***@redis-{id}:6379
|
||||
QDRANT_URL=http://qdrant-{id}:6333
|
||||
```
|
||||
|
||||
## 🔧 Development Workflow
|
||||
|
||||
### Initial Setup
|
||||
|
||||
1. **Access Workspace**
|
||||
```bash
|
||||
# Run environment info script
|
||||
devinfo
|
||||
```
|
||||
|
||||
2. **Verify Services**
|
||||
```bash
|
||||
# Check PostgreSQL
|
||||
pg_isready -h postgres-{workspace-id} -U postgres
|
||||
|
||||
# Check Redis
|
||||
redis-cli -h redis-{workspace-id} ping
|
||||
|
||||
# Check Qdrant
|
||||
curl http://qdrant-{workspace-id}:6333/health
|
||||
```
|
||||
|
||||
### Common Tasks
|
||||
|
||||
#### Next.js Project
|
||||
```bash
|
||||
# Create new Next.js app
|
||||
npx create-next-app@latest my-app
|
||||
cd my-app
|
||||
npm run dev # Accessible at nextjs-3000-{workspace}.dev.lab
|
||||
```
|
||||
|
||||
#### Python FastAPI Project
|
||||
```bash
|
||||
# Activate Python environment
|
||||
source /home/coder/.venv/bin/activate
|
||||
|
||||
# Create FastAPI app
|
||||
uv add fastapi uvicorn
|
||||
# Your app runs on port 8000, accessible via reverse proxy
|
||||
```
|
||||
|
||||
#### Database Development
|
||||
```bash
|
||||
# Connect to PostgreSQL
|
||||
psql $POSTGRES_URL
|
||||
|
||||
# Create tables, run migrations, etc.
|
||||
# Access pgAdmin for GUI management
|
||||
```
|
||||
|
||||
## 🔍 Monitoring & Debugging
|
||||
|
||||
### Built-in Monitoring
|
||||
|
||||
Coder automatically tracks:
|
||||
- **CPU Usage** - Updated every 60 seconds
|
||||
- **RAM Usage** - Updated every 60 seconds
|
||||
- **Disk Usage** - Updated every 5 minutes
|
||||
- **Git Branch** - Updated every 5 minutes
|
||||
|
||||
### Health Checks
|
||||
|
||||
All services include comprehensive health checks:
|
||||
- **PostgreSQL**: `pg_isready` command
|
||||
- **Redis**: Connection test with `redis-cli`
|
||||
- **Qdrant**: HTTP health endpoint
|
||||
- **Web Services**: HTTP response verification
|
||||
|
||||
### Logs and Debugging
|
||||
|
||||
```bash
|
||||
# Container logs
|
||||
docker logs postgres-{workspace-id}
|
||||
docker logs redis-{workspace-id}
|
||||
docker logs qdrant-{workspace-id}
|
||||
|
||||
# Service status
|
||||
docker ps --filter label=coder.workspace_id={workspace-id}
|
||||
|
||||
# Network inspection
|
||||
docker network inspect coder-{workspace-id}
|
||||
```
|
||||
|
||||
## 🔐 Security Considerations
|
||||
|
||||
### Network Security
|
||||
- **Isolated Networks** - Each workspace has its own Docker network
|
||||
- **No Host Exposure** - Services only accessible through authenticated Coder session
|
||||
- **Internal Communication** - Services communicate using internal DNS names
|
||||
|
||||
### Authentication
|
||||
- **Database Passwords** - Configurable via terraform.tfvars
|
||||
- **Coder Authentication** - All access requires Coder login
|
||||
- **sudo Access** - Granted to `coder` user for development flexibility
|
||||
|
||||
### Data Persistence
|
||||
- **Database Data** - Persistent Docker volumes per workspace
|
||||
- **Workspace Files** - Persistent across container restarts
|
||||
- **User Configuration** - Home directory persistence
|
||||
|
||||
## 🚨 Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### "Unable to find user coder"
|
||||
**Solution**: Container automatically creates `coder` user during startup. If issues persist, check container logs.
|
||||
|
||||
#### Port Already in Use
|
||||
**Solution**: This configuration uses no host port mappings. All routing is handled internally.
|
||||
|
||||
#### Services Not Accessible
|
||||
**Solutions**:
|
||||
1. Verify reverse proxy wildcard configuration
|
||||
2. Check Coder environment variables:
|
||||
```bash
|
||||
echo $CODER_ACCESS_URL
|
||||
echo $CODER_WILDCARD_ACCESS_URL
|
||||
```
|
||||
3. Confirm service health via Coder dashboard
|
||||
|
||||
#### Database Connection Issues
|
||||
**Solutions**:
|
||||
1. Verify service is enabled in workspace parameters
|
||||
2. Check container status: `docker ps`
|
||||
3. Test internal connectivity: `curl http://postgres-{id}:5432`
|
||||
|
||||
### Debug Commands
|
||||
|
||||
```bash
|
||||
# Environment information
|
||||
devinfo
|
||||
|
||||
# Network connectivity
|
||||
docker network ls | grep coder
|
||||
docker network inspect coder-{workspace-id}
|
||||
|
||||
# Service health
|
||||
curl http://qdrant-{workspace-id}:6333/health
|
||||
pg_isready -h postgres-{workspace-id} -U postgres
|
||||
redis-cli -h redis-{workspace-id} ping
|
||||
|
||||
# Container status
|
||||
docker ps --filter label=coder.workspace_id={workspace-id}
|
||||
```
|
||||
|
||||
## 🔄 Updates & Maintenance
|
||||
|
||||
### Updating Tool Versions
|
||||
|
||||
1. **Modify terraform.tfvars**
|
||||
```hcl
|
||||
node_version = "22" # Update Node.js version
|
||||
python_version = "3.13" # Update Python version
|
||||
```
|
||||
|
||||
2. **Apply Changes**
|
||||
```bash
|
||||
terraform plan
|
||||
terraform apply
|
||||
```
|
||||
|
||||
3. **Restart Workspaces** - Changes apply to new workspace instances
|
||||
|
||||
### Adding New Services
|
||||
|
||||
1. **Add to services.tf** - Define new container resource
|
||||
2. **Add to apps.tf** - Create Coder app for access
|
||||
3. **Update variables.tf** - Add configuration options
|
||||
4. **Update startup script** in workspace.tf if needed
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
### Adding New Features
|
||||
|
||||
1. Follow the existing file structure and naming conventions
|
||||
2. Add proper health checks for new services
|
||||
3. Update this README with new service documentation
|
||||
4. Test with multiple concurrent workspaces
|
||||
|
||||
### Best Practices
|
||||
|
||||
- **Resource Labels** - All resources should include `coder.workspace_id` label
|
||||
- **Network Isolation** - New services should join workspace network
|
||||
- **No Host Ports** - Use internal networking only
|
||||
- **Health Checks** - All web services need health check endpoints
|
||||
- **Persistent Data** - Use Docker volumes for data that should survive restarts
|
||||
|
||||
## 📚 References
|
||||
|
||||
- [Coder Documentation](https://coder.com/docs)
|
||||
- [Terraform Docker Provider](https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs)
|
||||
- [PostgreSQL Docker Hub](https://hub.docker.com/_/postgres)
|
||||
- [Redis Docker Hub](https://hub.docker.com/_/redis)
|
||||
- [Qdrant Documentation](https://qdrant.tech/documentation/)
|
||||
|
||||
## 📝 License
|
||||
|
||||
This configuration is provided as-is for development purposes. Modify passwords and security settings for production use.
|
||||
|
||||
---
|
||||
|
||||
**🚀 Happy Coding!** Your isolated development environment is ready for productive development with full database support and AI-powered tools.
|
||||
273
tf/apps.tf
Normal file
273
tf/apps.tf
Normal file
@@ -0,0 +1,273 @@
|
||||
# =============================================================================
|
||||
# Coder Applications - Service Access Points
|
||||
# Web interfaces and tools for development services
|
||||
# =============================================================================
|
||||
|
||||
# =============================================================================
|
||||
# IDE and Code Editor Access
|
||||
# =============================================================================
|
||||
|
||||
# VS Code Server
|
||||
resource "coder_app" "code_server" {
|
||||
agent_id = coder_agent.main.id
|
||||
slug = "code-server"
|
||||
display_name = "VS Code"
|
||||
url = "http://localhost:8080"
|
||||
icon = "/icon/code.svg"
|
||||
subdomain = true
|
||||
share = "owner"
|
||||
order = 1
|
||||
|
||||
healthcheck {
|
||||
url = "http://localhost:8080/healthz"
|
||||
interval = 10
|
||||
threshold = 5
|
||||
}
|
||||
}
|
||||
|
||||
# Terminal Access
|
||||
resource "coder_app" "terminal" {
|
||||
agent_id = coder_agent.main.id
|
||||
slug = "terminal"
|
||||
display_name = "Terminal"
|
||||
icon = "/icon/terminal.svg"
|
||||
command = "bash"
|
||||
order = 2
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Service Port Forwarding for Docker Container Access
|
||||
# Note: Using direct container URLs since containers are on same Docker network
|
||||
# =============================================================================
|
||||
|
||||
# =============================================================================
|
||||
# Database Management Interfaces
|
||||
# =============================================================================
|
||||
|
||||
# pgAdmin - PostgreSQL Administration
|
||||
resource "coder_app" "pgadmin" {
|
||||
count = data.coder_parameter.enable_services.value && data.coder_parameter.enable_pgadmin.value ? 1 : 0
|
||||
agent_id = coder_agent.main.id
|
||||
slug = "pgadmin"
|
||||
display_name = "pgAdmin"
|
||||
url = "http://pgadmin-${local.workspace_id}:80"
|
||||
icon = "/icon/postgresql.svg"
|
||||
subdomain = true
|
||||
share = "owner"
|
||||
order = 10
|
||||
|
||||
healthcheck {
|
||||
url = "http://pgadmin-${local.workspace_id}:80"
|
||||
interval = 15
|
||||
threshold = 5
|
||||
}
|
||||
}
|
||||
|
||||
# Qdrant Dashboard - Vector Database Management
|
||||
resource "coder_app" "qdrant" {
|
||||
count = data.coder_parameter.enable_services.value ? 1 : 0
|
||||
agent_id = coder_agent.main.id
|
||||
slug = "qdrant-dashboard"
|
||||
display_name = "Qdrant Dashboard"
|
||||
url = "http://qdrant-${local.workspace_id}:6333/dashboard"
|
||||
icon = "/icon/database.svg"
|
||||
subdomain = true
|
||||
share = "owner"
|
||||
order = 11
|
||||
|
||||
healthcheck {
|
||||
url = "http://qdrant-${local.workspace_id}:6333/api/cluster"
|
||||
interval = 30
|
||||
threshold = 10
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Development Server Ports
|
||||
# =============================================================================
|
||||
|
||||
# Next.js Development Server (default port 3000)
|
||||
resource "coder_app" "nextjs_dev" {
|
||||
agent_id = coder_agent.main.id
|
||||
slug = "nextjs-3000"
|
||||
display_name = "Next.js Dev Server"
|
||||
url = "http://localhost:3000"
|
||||
icon = "/icon/react.svg"
|
||||
subdomain = true
|
||||
share = "owner"
|
||||
|
||||
healthcheck {
|
||||
url = "http://localhost:3000"
|
||||
interval = 10
|
||||
threshold = 10
|
||||
}
|
||||
}
|
||||
|
||||
# Generic Development Server (port 3000)
|
||||
resource "coder_app" "dev_server_3000" {
|
||||
agent_id = coder_agent.main.id
|
||||
slug = "dev-3000"
|
||||
display_name = "Dev Server (3000)"
|
||||
url = "http://localhost:3000"
|
||||
icon = "/icon/web.svg"
|
||||
subdomain = true
|
||||
share = "owner"
|
||||
order = 21
|
||||
|
||||
healthcheck {
|
||||
url = "http://localhost:3000"
|
||||
interval = 10
|
||||
threshold = 10
|
||||
}
|
||||
}
|
||||
|
||||
# API Server - FastAPI/Flask (port 8000)
|
||||
resource "coder_app" "api_server_8000" {
|
||||
agent_id = coder_agent.main.id
|
||||
slug = "api-8000"
|
||||
display_name = "API Server (8000)"
|
||||
url = "http://localhost:8000"
|
||||
icon = "/icon/api.svg"
|
||||
subdomain = true
|
||||
share = "owner"
|
||||
order = 20
|
||||
|
||||
healthcheck {
|
||||
url = "http://localhost:8000/health"
|
||||
interval = 10
|
||||
threshold = 10
|
||||
}
|
||||
}
|
||||
|
||||
# Vite Development Server (port 5173)
|
||||
resource "coder_app" "vite_dev" {
|
||||
agent_id = coder_agent.main.id
|
||||
slug = "vite-5173"
|
||||
display_name = "Vite Dev Server"
|
||||
url = "http://localhost:5173"
|
||||
icon = "/icon/web.svg"
|
||||
subdomain = true
|
||||
share = "owner"
|
||||
|
||||
healthcheck {
|
||||
url = "http://localhost:5173"
|
||||
interval = 10
|
||||
threshold = 10
|
||||
}
|
||||
}
|
||||
|
||||
# Rust Development Server (port 8080)
|
||||
resource "coder_app" "rust_server" {
|
||||
agent_id = coder_agent.main.id
|
||||
slug = "rust-8080"
|
||||
display_name = "Rust Server (8080)"
|
||||
url = "http://localhost:8080"
|
||||
icon = "/icon/code.svg"
|
||||
subdomain = true
|
||||
share = "owner"
|
||||
|
||||
healthcheck {
|
||||
url = "http://localhost:8080/health"
|
||||
interval = 10
|
||||
threshold = 10
|
||||
}
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Data Science and Analytics Tools
|
||||
# =============================================================================
|
||||
|
||||
# Jupyter Lab (if enabled)
|
||||
resource "coder_app" "jupyter" {
|
||||
count = data.coder_parameter.enable_jupyter.value ? 1 : 0
|
||||
agent_id = coder_agent.main.id
|
||||
slug = "jupyter"
|
||||
display_name = "Jupyter Lab"
|
||||
url = "http://localhost:8888"
|
||||
icon = "/icon/python.svg"
|
||||
subdomain = true
|
||||
share = "owner"
|
||||
|
||||
healthcheck {
|
||||
url = "http://localhost:8888"
|
||||
interval = 15
|
||||
threshold = 10
|
||||
}
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Utility and Management Applications
|
||||
# =============================================================================
|
||||
|
||||
# Environment Information
|
||||
resource "coder_app" "env_info" {
|
||||
agent_id = coder_agent.main.id
|
||||
slug = "env-info"
|
||||
display_name = "Environment Info"
|
||||
icon = "/icon/info.svg"
|
||||
command = "devinfo"
|
||||
}
|
||||
|
||||
# Database Connection Tester
|
||||
resource "coder_app" "db_tester" {
|
||||
count = data.coder_parameter.enable_services.value ? 1 : 0
|
||||
agent_id = coder_agent.main.id
|
||||
slug = "db-tester"
|
||||
display_name = "Database Tester"
|
||||
icon = "/icon/terminal.svg"
|
||||
command = "bash -c 'echo \"=== Database Connection Test ===\"; echo \"PostgreSQL: postgres-${local.workspace_id}:5432\"; echo \"Redis: redis-${local.workspace_id}:6379\"; echo \"Qdrant: qdrant-${local.workspace_id}:6333\"; echo; echo \"Test PostgreSQL:\"; pg_isready -h postgres-${local.workspace_id} -p 5432 -U postgres || echo \"PostgreSQL not ready\"; echo; echo \"Test Redis:\"; redis-cli -h redis-${local.workspace_id} -p 6379 -a \"${var.redis_password}\" ping || echo \"Redis not ready\"; echo; echo \"Test Qdrant:\"; curl -f http://qdrant-${local.workspace_id}:6333 || echo \"Qdrant not ready\"; echo; read -p \"Press Enter to exit...\"'"
|
||||
}
|
||||
|
||||
# Development Logs Viewer
|
||||
resource "coder_app" "dev_logs" {
|
||||
agent_id = coder_agent.main.id
|
||||
slug = "dev-logs"
|
||||
display_name = "Development Logs"
|
||||
icon = "/icon/terminal.svg"
|
||||
command = "bash"
|
||||
}
|
||||
|
||||
# Git Repository Manager
|
||||
resource "coder_app" "git_manager" {
|
||||
agent_id = coder_agent.main.id
|
||||
slug = "git"
|
||||
display_name = "Git Repository"
|
||||
icon = "/icon/git.svg"
|
||||
command = "bash"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# AI Development Tools Access
|
||||
# =============================================================================
|
||||
|
||||
# Claude Code CLI Access
|
||||
resource "coder_app" "claude_code" {
|
||||
count = data.coder_parameter.enable_ai_tools.value && data.coder_parameter.enable_claude_code.value ? 1 : 0
|
||||
agent_id = coder_agent.main.id
|
||||
slug = "claude-code"
|
||||
display_name = "Claude Code"
|
||||
icon = "/icon/ai.svg"
|
||||
command = "claude"
|
||||
}
|
||||
|
||||
# JetBrains Gateway
|
||||
resource "coder_app" "jetbrains_gateway" {
|
||||
count = data.coder_parameter.enable_jetbrains.value ? 1 : 0
|
||||
agent_id = coder_agent.main.id
|
||||
slug = "jetbrains-gateway"
|
||||
display_name = "JetBrains Gateway"
|
||||
icon = "/icon/intellij.svg"
|
||||
command = "bash -c 'echo \"🚀 JetBrains Gateway Integration\"; echo \"========================\"; echo \"\"; echo \"📍 Project Folder: /workspaces\"; echo \"🔧 Available IDEs: IntelliJ IDEA Ultimate, WebStorm, PyCharm Professional, GoLand\"; echo \"🌐 Default IDE: IntelliJ IDEA Ultimate\"; echo \"\"; echo \"💡 To connect:\"; echo \" 1. Install JetBrains Gateway on your local machine\"; echo \" 2. Connect to this workspace using Coder Gateway plugin\"; echo \" 3. Select your preferred IDE from the available options\"; echo \"\"; echo \"📚 Documentation: https://coder.com/docs/ides/gateway\"; echo \"\"; read -p \"Press Enter to continue...\";'"
|
||||
order = 3
|
||||
}
|
||||
|
||||
# File Manager
|
||||
resource "coder_app" "file_manager" {
|
||||
agent_id = coder_agent.main.id
|
||||
slug = "files"
|
||||
display_name = "File Manager"
|
||||
icon = "/icon/folder.svg"
|
||||
command = "bash -c 'export TERM=xterm-256color && cd /workspaces && ranger'"
|
||||
order = 5
|
||||
}
|
||||
236
tf/main.tf
Normal file
236
tf/main.tf
Normal file
@@ -0,0 +1,236 @@
|
||||
terraform {
|
||||
required_version = ">= 1.0"
|
||||
required_providers {
|
||||
coder = {
|
||||
source = "coder/coder"
|
||||
version = "~> 2.0"
|
||||
}
|
||||
docker = {
|
||||
source = "kreuzwerker/docker"
|
||||
version = "~> 2.25"
|
||||
}
|
||||
envbuilder = {
|
||||
source = "coder/envbuilder"
|
||||
version = "~> 1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "coder" {}
|
||||
provider "docker" {
|
||||
host = var.docker_socket != "" ? var.docker_socket : null
|
||||
}
|
||||
provider "envbuilder" {}
|
||||
|
||||
# Data Sources
|
||||
data "coder_provisioner" "me" {}
|
||||
data "coder_workspace" "me" {}
|
||||
data "coder_workspace_owner" "me" {}
|
||||
|
||||
# Parameters
|
||||
data "coder_parameter" "repo" {
|
||||
name = "repo"
|
||||
display_name = "Repository"
|
||||
description = "Select a repository to clone"
|
||||
mutable = true
|
||||
order = 1
|
||||
|
||||
option {
|
||||
name = "Custom Development Environment"
|
||||
description = "Full-stack development with all services"
|
||||
value = "custom"
|
||||
}
|
||||
option {
|
||||
name = "vercel/next.js"
|
||||
description = "The React Framework"
|
||||
value = "https://github.com/vercel/next.js"
|
||||
}
|
||||
option {
|
||||
name = "Custom URL"
|
||||
description = "Specify a custom repo URL below"
|
||||
value = "custom-url"
|
||||
}
|
||||
}
|
||||
|
||||
data "coder_parameter" "custom_repo_url" {
|
||||
name = "custom_repo_url"
|
||||
display_name = "Custom Repository URL"
|
||||
description = "Enter a custom repository URL"
|
||||
default = ""
|
||||
mutable = true
|
||||
order = 2
|
||||
}
|
||||
|
||||
data "coder_parameter" "enable_services" {
|
||||
name = "enable_services"
|
||||
display_name = "Enable Database Services"
|
||||
description = "Enable PostgreSQL, Redis, Qdrant, and Docker Registry"
|
||||
type = "bool"
|
||||
default = "true"
|
||||
mutable = true
|
||||
order = 3
|
||||
}
|
||||
|
||||
data "coder_parameter" "enable_ai_tools" {
|
||||
name = "enable_ai_tools"
|
||||
display_name = "Enable AI Assistant Tools"
|
||||
description = "Install Claude Code and AI development tools"
|
||||
type = "bool"
|
||||
default = "true"
|
||||
mutable = true
|
||||
order = 4
|
||||
}
|
||||
|
||||
data "coder_parameter" "enable_claude_code" {
|
||||
name = "enable_claude_code"
|
||||
display_name = "Enable Claude Code CLI"
|
||||
description = "Install Claude Code command-line interface"
|
||||
type = "bool"
|
||||
default = "true"
|
||||
mutable = true
|
||||
order = 5
|
||||
}
|
||||
|
||||
data "coder_parameter" "enable_cursor_support" {
|
||||
name = "enable_cursor_support"
|
||||
display_name = "Enable Cursor IDE Support"
|
||||
description = "Install Cursor IDE configuration and settings"
|
||||
type = "bool"
|
||||
default = "true"
|
||||
mutable = true
|
||||
order = 6
|
||||
}
|
||||
|
||||
data "coder_parameter" "enable_windsurf_support" {
|
||||
name = "enable_windsurf_support"
|
||||
display_name = "Enable Windsurf IDE Support"
|
||||
description = "Install Windsurf IDE configuration and settings"
|
||||
type = "bool"
|
||||
default = "true"
|
||||
mutable = true
|
||||
order = 7
|
||||
}
|
||||
|
||||
data "coder_parameter" "enable_jetbrains" {
|
||||
name = "enable_jetbrains"
|
||||
display_name = "Enable JetBrains Gateway"
|
||||
description = "Enable JetBrains Gateway integration for remote development"
|
||||
type = "bool"
|
||||
default = "true"
|
||||
mutable = true
|
||||
order = 8
|
||||
}
|
||||
|
||||
data "coder_parameter" "enable_jupyter" {
|
||||
name = "enable_jupyter"
|
||||
display_name = "Enable Jupyter Lab"
|
||||
description = "Enable Jupyter Lab for data science and notebook development"
|
||||
type = "bool"
|
||||
default = "false"
|
||||
mutable = true
|
||||
order = 9
|
||||
}
|
||||
|
||||
data "coder_parameter" "enable_pgadmin" {
|
||||
name = "enable_pgadmin"
|
||||
display_name = "Enable pgAdmin"
|
||||
description = "Enable pgAdmin web interface for PostgreSQL management"
|
||||
type = "bool"
|
||||
default = "true"
|
||||
mutable = true
|
||||
order = 10
|
||||
}
|
||||
|
||||
# Local Variables
|
||||
locals {
|
||||
# Container and workspace naming
|
||||
container_name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}"
|
||||
workspace_id = data.coder_workspace.me.id
|
||||
|
||||
# Git configuration
|
||||
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
|
||||
|
||||
# Repository URL logic
|
||||
repo_url = (
|
||||
data.coder_parameter.repo.value == "custom" ? "https://github.com/coder/envbuilder" :
|
||||
data.coder_parameter.repo.value == "custom-url" ? data.coder_parameter.custom_repo_url.value :
|
||||
data.coder_parameter.repo.value
|
||||
)
|
||||
|
||||
# Development container image with all required tools
|
||||
devcontainer_image = var.devcontainer_image
|
||||
|
||||
# Environment variables for the development container
|
||||
dev_environment = {
|
||||
# Git configuration
|
||||
"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
|
||||
|
||||
# Development tools
|
||||
"NODE_VERSION" = var.node_version
|
||||
"PYTHON_VERSION" = var.python_version
|
||||
"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" : ""
|
||||
|
||||
# Development configuration
|
||||
"EDITOR" = "code"
|
||||
"PYTHONPATH" = "/workspaces"
|
||||
"CARGO_HOME" = "/home/coder/.cargo"
|
||||
"RUSTUP_HOME" = "/home/coder/.rustup"
|
||||
}
|
||||
|
||||
# Legacy service URLs for backward compatibility
|
||||
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"
|
||||
}
|
||||
|
||||
# Docker Network
|
||||
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
|
||||
}
|
||||
labels {
|
||||
label = "coder.project"
|
||||
value = var.project_name
|
||||
}
|
||||
}
|
||||
|
||||
# Workspace Volume
|
||||
resource "docker_volume" "workspaces" {
|
||||
name = "workspaces-${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 = "workspace-data"
|
||||
}
|
||||
}
|
||||
|
||||
# Development Container Image
|
||||
resource "docker_image" "devcontainer" {
|
||||
name = local.devcontainer_image
|
||||
keep_locally = true
|
||||
}
|
||||
118
tf/outputs.tf
Normal file
118
tf/outputs.tf
Normal file
@@ -0,0 +1,118 @@
|
||||
# =============================================================================
|
||||
# Terraform Outputs
|
||||
# Expose important values for reference and external use
|
||||
# =============================================================================
|
||||
|
||||
# =============================================================================
|
||||
# Workspace Information
|
||||
# =============================================================================
|
||||
|
||||
output "workspace_id" {
|
||||
description = "Unique identifier for the Coder workspace"
|
||||
value = local.workspace_id
|
||||
}
|
||||
|
||||
output "workspace_name" {
|
||||
description = "Name of the Coder workspace"
|
||||
value = data.coder_workspace.me.name
|
||||
}
|
||||
|
||||
output "workspace_owner" {
|
||||
description = "Owner of the Coder workspace"
|
||||
value = data.coder_workspace_owner.me.name
|
||||
}
|
||||
|
||||
output "container_name" {
|
||||
description = "Name of the main development container"
|
||||
value = local.container_name
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Git Configuration
|
||||
# =============================================================================
|
||||
|
||||
output "git_author_name" {
|
||||
description = "Git author name configured for the workspace"
|
||||
value = local.git_author_name
|
||||
}
|
||||
|
||||
output "git_author_email" {
|
||||
description = "Git author email configured for the workspace"
|
||||
value = local.git_author_email
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
output "repository_url" {
|
||||
description = "Repository URL that will be cloned"
|
||||
value = local.repo_url
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Service Connection Information
|
||||
# =============================================================================
|
||||
|
||||
output "postgres_connection_url" {
|
||||
description = "PostgreSQL connection URL for applications"
|
||||
value = data.coder_parameter.enable_services.value ? local.postgres_url : null
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
output "redis_connection_url" {
|
||||
description = "Redis connection URL for applications"
|
||||
value = data.coder_parameter.enable_services.value ? local.redis_url : null
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
output "qdrant_api_url" {
|
||||
description = "Qdrant vector database API URL"
|
||||
value = data.coder_parameter.enable_services.value ? local.qdrant_url : null
|
||||
}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Network Information
|
||||
# =============================================================================
|
||||
|
||||
output "docker_network_name" {
|
||||
description = "Name of the Docker network for service communication"
|
||||
value = docker_network.workspace.name
|
||||
}
|
||||
|
||||
output "workspace_volume_name" {
|
||||
description = "Name of the persistent workspace volume"
|
||||
value = docker_volume.workspaces.name
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Development Environment Information
|
||||
# =============================================================================
|
||||
|
||||
output "development_tools" {
|
||||
description = "Information about installed development tools"
|
||||
value = {
|
||||
node_version = var.node_version
|
||||
python_version = var.python_version
|
||||
container_image = local.devcontainer_image
|
||||
}
|
||||
}
|
||||
|
||||
output "service_status" {
|
||||
description = "Status of optional development services"
|
||||
value = {
|
||||
services_enabled = data.coder_parameter.enable_services.value
|
||||
ai_tools_enabled = data.coder_parameter.enable_ai_tools.value
|
||||
pgadmin_enabled = var.enable_pgadmin
|
||||
jupyter_enabled = var.enable_jupyter
|
||||
}
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Access Information
|
||||
# =============================================================================
|
||||
|
||||
output "external_ports" {
|
||||
description = "External ports exposed for services"
|
||||
value = data.coder_parameter.enable_services.value ? {
|
||||
pgadmin = var.enable_pgadmin ? var.pgadmin_port : null
|
||||
} : {}
|
||||
}
|
||||
72
tf/scripts.tf
Normal file
72
tf/scripts.tf
Normal file
@@ -0,0 +1,72 @@
|
||||
# =============================================================================
|
||||
# Provisioning Scripts - AI Development Tools and Extensions
|
||||
# Installation scripts for Cursor, Claude Code, Windsurf support
|
||||
# =============================================================================
|
||||
|
||||
# =============================================================================
|
||||
# Claude Code CLI Installation
|
||||
# =============================================================================
|
||||
|
||||
resource "coder_script" "claude_code_setup" {
|
||||
count = data.coder_parameter.enable_ai_tools.value && data.coder_parameter.enable_claude_code.value ? 1 : 0
|
||||
agent_id = coder_agent.main.id
|
||||
display_name = "Install Claude Code CLI"
|
||||
icon = "/icon/ai.svg"
|
||||
run_on_start = true
|
||||
|
||||
script = "bash /home/coder/resources/tf/scripts/claude-install.sh"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Cursor IDE Support Setup
|
||||
# =============================================================================
|
||||
|
||||
resource "coder_script" "cursor_setup" {
|
||||
count = data.coder_parameter.enable_ai_tools.value && data.coder_parameter.enable_cursor_support.value ? 1 : 0
|
||||
agent_id = coder_agent.main.id
|
||||
display_name = "Configure Cursor IDE Support"
|
||||
icon = "/icon/code.svg"
|
||||
run_on_start = true
|
||||
|
||||
script = "bash /home/coder/resources/tf/scripts/cursor-setup.sh"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Windsurf IDE Support Setup
|
||||
# =============================================================================
|
||||
|
||||
resource "coder_script" "windsurf_setup" {
|
||||
count = data.coder_parameter.enable_ai_tools.value && data.coder_parameter.enable_windsurf_support.value ? 1 : 0
|
||||
agent_id = coder_agent.main.id
|
||||
display_name = "Configure Windsurf IDE Support"
|
||||
icon = "/icon/code.svg"
|
||||
run_on_start = true
|
||||
|
||||
script = "bash /home/coder/resources/tf/scripts/windsurf-setup.sh"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Development Tools and Extensions
|
||||
# =============================================================================
|
||||
|
||||
resource "coder_script" "dev_extensions" {
|
||||
agent_id = coder_agent.main.id
|
||||
display_name = "Install Development Tools"
|
||||
icon = "/icon/tools.svg"
|
||||
run_on_start = true
|
||||
|
||||
script = "bash /home/coder/resources/tf/scripts/dev-tools.sh"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Git Hooks and Metadata Capture Setup
|
||||
# =============================================================================
|
||||
|
||||
resource "coder_script" "git_hooks_setup" {
|
||||
agent_id = coder_agent.main.id
|
||||
display_name = "Setup Git Hooks"
|
||||
icon = "/icon/git.svg"
|
||||
run_on_start = true
|
||||
|
||||
script = "bash /home/coder/resources/tf/scripts/git-hooks.sh"
|
||||
}
|
||||
328
tf/services.tf
Normal file
328
tf/services.tf
Normal file
@@ -0,0 +1,328 @@
|
||||
# =============================================================================
|
||||
# Service Containers - Database and Development Services
|
||||
# PostgreSQL, Redis, Qdrant, Docker Registry
|
||||
# =============================================================================
|
||||
|
||||
# =============================================================================
|
||||
# PostgreSQL Database Service
|
||||
# =============================================================================
|
||||
|
||||
# PostgreSQL data volume for persistence
|
||||
resource "docker_volume" "postgres_data" {
|
||||
count = data.coder_parameter.enable_services.value ? 1 : 0
|
||||
name = "postgres-data-${local.workspace_id}"
|
||||
|
||||
labels {
|
||||
label = "coder.service"
|
||||
value = "postgres"
|
||||
}
|
||||
labels {
|
||||
label = "coder.workspace_id"
|
||||
value = local.workspace_id
|
||||
}
|
||||
}
|
||||
|
||||
# PostgreSQL container
|
||||
resource "docker_container" "postgres" {
|
||||
count = data.coder_parameter.enable_services.value ? 1 : 0
|
||||
image = "postgres:${var.postgres_version}-alpine"
|
||||
name = "postgres-${local.workspace_id}"
|
||||
|
||||
# PostgreSQL configuration
|
||||
env = [
|
||||
"POSTGRES_DB=postgres",
|
||||
"POSTGRES_USER=postgres",
|
||||
"POSTGRES_PASSWORD=${var.postgres_password}",
|
||||
"POSTGRES_INITDB_ARGS=--auth-local=trust --auth-host=md5",
|
||||
"POSTGRES_SHARED_PRELOAD_LIBRARIES=pg_stat_statements",
|
||||
"POSTGRES_MAX_CONNECTIONS=${var.postgres_max_connections}"
|
||||
]
|
||||
|
||||
# Network configuration - internal only, accessible via Coder port forwarding
|
||||
networks_advanced {
|
||||
name = docker_network.workspace.name
|
||||
}
|
||||
|
||||
# Data persistence
|
||||
volumes {
|
||||
volume_name = docker_volume.postgres_data[0].name
|
||||
container_path = "/var/lib/postgresql/data"
|
||||
}
|
||||
|
||||
# Health check
|
||||
healthcheck {
|
||||
test = ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
interval = "15s"
|
||||
timeout = "5s"
|
||||
retries = 5
|
||||
}
|
||||
|
||||
restart = "unless-stopped"
|
||||
|
||||
labels {
|
||||
label = "coder.service"
|
||||
value = "postgres"
|
||||
}
|
||||
labels {
|
||||
label = "coder.workspace_id"
|
||||
value = local.workspace_id
|
||||
}
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Redis Cache Service
|
||||
# =============================================================================
|
||||
|
||||
# Redis data volume for persistence
|
||||
resource "docker_volume" "redis_data" {
|
||||
count = data.coder_parameter.enable_services.value ? 1 : 0
|
||||
name = "redis-data-${local.workspace_id}"
|
||||
|
||||
labels {
|
||||
label = "coder.service"
|
||||
value = "redis"
|
||||
}
|
||||
labels {
|
||||
label = "coder.workspace_id"
|
||||
value = local.workspace_id
|
||||
}
|
||||
}
|
||||
|
||||
# Redis container
|
||||
resource "docker_container" "redis" {
|
||||
count = data.coder_parameter.enable_services.value ? 1 : 0
|
||||
image = "redis:${var.redis_version}-alpine"
|
||||
name = "redis-${local.workspace_id}"
|
||||
|
||||
# Redis configuration with authentication
|
||||
command = [
|
||||
"redis-server",
|
||||
"--requirepass", var.redis_password,
|
||||
"--appendonly", "yes",
|
||||
"--appendfsync", "everysec",
|
||||
"--maxmemory", var.redis_max_memory,
|
||||
"--maxmemory-policy", "allkeys-lru"
|
||||
]
|
||||
|
||||
networks_advanced {
|
||||
name = docker_network.workspace.name
|
||||
}
|
||||
|
||||
# Data persistence
|
||||
volumes {
|
||||
volume_name = docker_volume.redis_data[0].name
|
||||
container_path = "/data"
|
||||
}
|
||||
|
||||
# Health check with authentication
|
||||
healthcheck {
|
||||
test = ["CMD", "redis-cli", "-a", var.redis_password, "ping"]
|
||||
interval = "15s"
|
||||
timeout = "3s"
|
||||
retries = 5
|
||||
}
|
||||
|
||||
restart = "unless-stopped"
|
||||
|
||||
labels {
|
||||
label = "coder.service"
|
||||
value = "redis"
|
||||
}
|
||||
labels {
|
||||
label = "coder.workspace_id"
|
||||
value = local.workspace_id
|
||||
}
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Qdrant Vector Database Service
|
||||
# =============================================================================
|
||||
|
||||
# Qdrant data volume for persistence
|
||||
resource "docker_volume" "qdrant_data" {
|
||||
count = data.coder_parameter.enable_services.value ? 1 : 0
|
||||
name = "qdrant-data-${local.workspace_id}"
|
||||
|
||||
labels {
|
||||
label = "coder.service"
|
||||
value = "qdrant"
|
||||
}
|
||||
labels {
|
||||
label = "coder.workspace_id"
|
||||
value = local.workspace_id
|
||||
}
|
||||
}
|
||||
|
||||
# Qdrant container
|
||||
resource "docker_container" "qdrant" {
|
||||
count = data.coder_parameter.enable_services.value ? 1 : 0
|
||||
image = "qdrant/qdrant:${var.qdrant_version}"
|
||||
name = "qdrant-${local.workspace_id}"
|
||||
|
||||
# Qdrant configuration
|
||||
env = [
|
||||
"QDRANT__SERVICE__HTTP_PORT=6333",
|
||||
"QDRANT__SERVICE__GRPC_PORT=6334",
|
||||
"QDRANT__SERVICE__HOST=0.0.0.0",
|
||||
"QDRANT__LOG_LEVEL=INFO"
|
||||
]
|
||||
|
||||
networks_advanced {
|
||||
name = docker_network.workspace.name
|
||||
}
|
||||
|
||||
# Data persistence
|
||||
volumes {
|
||||
volume_name = docker_volume.qdrant_data[0].name
|
||||
container_path = "/qdrant/storage"
|
||||
}
|
||||
|
||||
# Health check using simple file existence check (Qdrant creates lock files when running)
|
||||
healthcheck {
|
||||
test = ["CMD-SHELL", "test -d /qdrant/storage && test -f /qdrant/qdrant || exit 1"]
|
||||
interval = "20s"
|
||||
timeout = "5s"
|
||||
retries = 5
|
||||
}
|
||||
|
||||
restart = "unless-stopped"
|
||||
|
||||
labels {
|
||||
label = "coder.service"
|
||||
value = "qdrant"
|
||||
}
|
||||
labels {
|
||||
label = "coder.workspace_id"
|
||||
value = local.workspace_id
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# pgAdmin for PostgreSQL Management (Optional)
|
||||
# =============================================================================
|
||||
|
||||
# pgAdmin data volume
|
||||
resource "docker_volume" "pgadmin_data" {
|
||||
count = data.coder_parameter.enable_services.value && data.coder_parameter.enable_pgadmin.value ? 1 : 0
|
||||
name = "pgadmin-data-${local.workspace_id}"
|
||||
|
||||
labels {
|
||||
label = "coder.service"
|
||||
value = "pgadmin"
|
||||
}
|
||||
labels {
|
||||
label = "coder.workspace_id"
|
||||
value = local.workspace_id
|
||||
}
|
||||
}
|
||||
|
||||
# pgAdmin container
|
||||
resource "docker_container" "pgadmin" {
|
||||
count = data.coder_parameter.enable_services.value && data.coder_parameter.enable_pgadmin.value ? 1 : 0
|
||||
image = "dpage/pgadmin4:latest"
|
||||
name = "pgadmin-${local.workspace_id}"
|
||||
|
||||
# pgAdmin configuration
|
||||
env = [
|
||||
"PGADMIN_DEFAULT_EMAIL=${var.pgadmin_email}",
|
||||
"PGADMIN_DEFAULT_PASSWORD=${var.pgadmin_password}",
|
||||
"PGADMIN_CONFIG_SERVER_MODE=False",
|
||||
"PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED=False",
|
||||
"PGADMIN_LISTEN_PORT=80"
|
||||
]
|
||||
|
||||
networks_advanced {
|
||||
name = docker_network.workspace.name
|
||||
}
|
||||
|
||||
# Data persistence
|
||||
volumes {
|
||||
volume_name = docker_volume.pgadmin_data[0].name
|
||||
container_path = "/var/lib/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
|
||||
start_period = "60s"
|
||||
}
|
||||
|
||||
restart = "unless-stopped"
|
||||
|
||||
labels {
|
||||
label = "coder.service"
|
||||
value = "pgadmin"
|
||||
}
|
||||
labels {
|
||||
label = "coder.workspace_id"
|
||||
value = local.workspace_id
|
||||
}
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Jupyter Lab Service (Optional for Data Science)
|
||||
# =============================================================================
|
||||
|
||||
# Jupyter data volume
|
||||
resource "docker_volume" "jupyter_data" {
|
||||
count = data.coder_parameter.enable_jupyter.value ? 1 : 0
|
||||
name = "jupyter-data-${local.workspace_id}"
|
||||
|
||||
labels {
|
||||
label = "coder.service"
|
||||
value = "jupyter"
|
||||
}
|
||||
labels {
|
||||
label = "coder.workspace_id"
|
||||
value = local.workspace_id
|
||||
}
|
||||
}
|
||||
|
||||
# Jupyter Lab container
|
||||
resource "docker_container" "jupyter" {
|
||||
count = data.coder_parameter.enable_jupyter.value ? 1 : 0
|
||||
image = "jupyter/scipy-notebook:latest"
|
||||
name = "jupyter-${local.workspace_id}"
|
||||
|
||||
# Jupyter configuration
|
||||
env = [
|
||||
"JUPYTER_ENABLE_LAB=yes",
|
||||
"JUPYTER_TOKEN=",
|
||||
"RESTARTABLE=yes",
|
||||
"JUPYTER_PORT=8888"
|
||||
]
|
||||
|
||||
networks_advanced {
|
||||
name = docker_network.workspace.name
|
||||
}
|
||||
|
||||
# Port accessible within workspace network - no host exposure needed
|
||||
|
||||
# Data persistence
|
||||
volumes {
|
||||
volume_name = docker_volume.jupyter_data[0].name
|
||||
container_path = "/home/jovyan/work"
|
||||
}
|
||||
|
||||
# Share workspace volume
|
||||
volumes {
|
||||
volume_name = docker_volume.workspaces.name
|
||||
container_path = "/home/jovyan/workspaces"
|
||||
read_only = false
|
||||
}
|
||||
|
||||
restart = "unless-stopped"
|
||||
|
||||
labels {
|
||||
label = "coder.service"
|
||||
value = "jupyter"
|
||||
}
|
||||
labels {
|
||||
label = "coder.workspace_id"
|
||||
value = local.workspace_id
|
||||
}
|
||||
}
|
||||
105
tf/terraform.tfvars
Normal file
105
tf/terraform.tfvars
Normal file
@@ -0,0 +1,105 @@
|
||||
# =============================================================================
|
||||
# Terraform Variables Configuration
|
||||
# Variable assignments for the development environment
|
||||
# =============================================================================
|
||||
|
||||
# =============================================================================
|
||||
# Project Configuration
|
||||
# =============================================================================
|
||||
project_name = "dev-environment"
|
||||
environment = "dev"
|
||||
|
||||
# =============================================================================
|
||||
# Docker Configuration
|
||||
# =============================================================================
|
||||
docker_socket = ""
|
||||
devcontainer_image = "mcr.microsoft.com/devcontainers/universal:2-linux"
|
||||
|
||||
# =============================================================================
|
||||
# Development Tool Versions
|
||||
# =============================================================================
|
||||
node_version = "20"
|
||||
python_version = "3.12"
|
||||
postgres_version = "17"
|
||||
redis_version = "7"
|
||||
qdrant_version = "latest"
|
||||
|
||||
# =============================================================================
|
||||
# Service Configuration
|
||||
# =============================================================================
|
||||
postgres_password = "devpassword"
|
||||
redis_password = "devpassword"
|
||||
postgres_max_connections = 100
|
||||
redis_max_memory = "512mb"
|
||||
|
||||
# =============================================================================
|
||||
# Network Configuration
|
||||
# =============================================================================
|
||||
pgadmin_port = 5050
|
||||
pgadmin_email = "admin@dev.local"
|
||||
pgadmin_password = "adminpassword"
|
||||
|
||||
# =============================================================================
|
||||
# Development Packages
|
||||
# =============================================================================
|
||||
npm_packages = [
|
||||
"repomix",
|
||||
"create-next-app",
|
||||
"nodemon",
|
||||
"concurrently",
|
||||
"@types/node",
|
||||
"typescript",
|
||||
"eslint",
|
||||
"prettier"
|
||||
]
|
||||
|
||||
python_packages = [
|
||||
"fastapi",
|
||||
"uvicorn",
|
||||
"requests",
|
||||
"pandas",
|
||||
"numpy",
|
||||
"psycopg2-binary",
|
||||
"redis",
|
||||
"qdrant-client",
|
||||
"python-dotenv"
|
||||
]
|
||||
|
||||
system_packages = [
|
||||
"make",
|
||||
"tree",
|
||||
"jq",
|
||||
"curl",
|
||||
"wget",
|
||||
"unzip",
|
||||
"build-essential"
|
||||
]
|
||||
|
||||
# =============================================================================
|
||||
# AI Development Tools
|
||||
# =============================================================================
|
||||
install_claude_code = true
|
||||
install_cursor_support = true
|
||||
install_windsurf_support = true
|
||||
|
||||
# =============================================================================
|
||||
# Performance Configuration
|
||||
# =============================================================================
|
||||
workspace_memory_limit = 16384
|
||||
workspace_cpu_limit = 4
|
||||
|
||||
# =============================================================================
|
||||
# Feature Toggles
|
||||
# =============================================================================
|
||||
enable_pgadmin = true
|
||||
enable_monitoring = true
|
||||
enable_jupyter = false
|
||||
|
||||
# =============================================================================
|
||||
# Common Tags
|
||||
# =============================================================================
|
||||
common_tags = {
|
||||
Environment = "development"
|
||||
ManagedBy = "terraform"
|
||||
Purpose = "remote-development"
|
||||
}
|
||||
322
tf/variables.tf
Normal file
322
tf/variables.tf
Normal file
@@ -0,0 +1,322 @@
|
||||
# =============================================================================
|
||||
# Variable Definitions
|
||||
# Modular Development Environment Configuration
|
||||
# =============================================================================
|
||||
|
||||
# =============================================================================
|
||||
# Project Configuration
|
||||
# =============================================================================
|
||||
|
||||
variable "project_name" {
|
||||
description = "Name of the project for resource tagging and identification"
|
||||
type = string
|
||||
default = "dev-environment"
|
||||
|
||||
validation {
|
||||
condition = can(regex("^[a-z0-9-]+$", var.project_name))
|
||||
error_message = "Project name must contain only lowercase letters, numbers, and hyphens."
|
||||
}
|
||||
}
|
||||
|
||||
variable "environment" {
|
||||
description = "Environment designation (dev, staging, prod)"
|
||||
type = string
|
||||
default = "dev"
|
||||
|
||||
validation {
|
||||
condition = contains(["dev", "staging", "prod"], var.environment)
|
||||
error_message = "Environment must be one of: dev, staging, prod."
|
||||
}
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Docker Configuration
|
||||
# =============================================================================
|
||||
|
||||
variable "docker_socket" {
|
||||
description = "Docker daemon socket URI (empty for default)"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "devcontainer_image" {
|
||||
description = "Development container image with all required tools pre-installed"
|
||||
type = string
|
||||
default = "mcr.microsoft.com/devcontainers/universal:2-linux"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Development Tool Versions
|
||||
# =============================================================================
|
||||
|
||||
variable "node_version" {
|
||||
description = "Node.js version to install"
|
||||
type = string
|
||||
default = "20"
|
||||
|
||||
validation {
|
||||
condition = contains(["18", "20", "21"], var.node_version)
|
||||
error_message = "Node.js version must be one of: 18, 20, 21."
|
||||
}
|
||||
}
|
||||
|
||||
variable "python_version" {
|
||||
description = "Python version to install"
|
||||
type = string
|
||||
default = "3.12"
|
||||
|
||||
validation {
|
||||
condition = contains(["3.10", "3.11", "3.12"], var.python_version)
|
||||
error_message = "Python version must be 3.10, 3.11, or 3.12."
|
||||
}
|
||||
}
|
||||
|
||||
variable "postgres_version" {
|
||||
description = "PostgreSQL version"
|
||||
type = string
|
||||
default = "17"
|
||||
|
||||
validation {
|
||||
condition = contains(["13", "14", "15", "16", "17"], var.postgres_version)
|
||||
error_message = "PostgreSQL version must be one of: 13, 14, 15, 16, 17."
|
||||
}
|
||||
}
|
||||
|
||||
variable "redis_version" {
|
||||
description = "Redis version"
|
||||
type = string
|
||||
default = "7"
|
||||
|
||||
validation {
|
||||
condition = contains(["6", "7"], var.redis_version)
|
||||
error_message = "Redis version must be 6 or 7."
|
||||
}
|
||||
}
|
||||
|
||||
variable "qdrant_version" {
|
||||
description = "Qdrant vector database version"
|
||||
type = string
|
||||
default = "latest"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Service Configuration
|
||||
# =============================================================================
|
||||
|
||||
variable "postgres_password" {
|
||||
description = "PostgreSQL postgres user password"
|
||||
type = string
|
||||
default = "devpassword"
|
||||
sensitive = true
|
||||
|
||||
validation {
|
||||
condition = length(var.postgres_password) >= 8
|
||||
error_message = "PostgreSQL password must be at least 8 characters long."
|
||||
}
|
||||
}
|
||||
|
||||
variable "redis_password" {
|
||||
description = "Redis authentication password"
|
||||
type = string
|
||||
default = "devpassword"
|
||||
sensitive = true
|
||||
|
||||
validation {
|
||||
condition = length(var.redis_password) >= 8
|
||||
error_message = "Redis password must be at least 8 characters long."
|
||||
}
|
||||
}
|
||||
|
||||
variable "postgres_max_connections" {
|
||||
description = "Maximum PostgreSQL connections"
|
||||
type = number
|
||||
default = 100
|
||||
|
||||
validation {
|
||||
condition = var.postgres_max_connections >= 20 && var.postgres_max_connections <= 1000
|
||||
error_message = "PostgreSQL max connections must be between 20 and 1000."
|
||||
}
|
||||
}
|
||||
|
||||
variable "redis_max_memory" {
|
||||
description = "Redis maximum memory (e.g., '256mb', '1gb')"
|
||||
type = string
|
||||
default = "512mb"
|
||||
|
||||
validation {
|
||||
condition = can(regex("^[0-9]+[kmg]b$", var.redis_max_memory))
|
||||
error_message = "Redis max memory must be in format like '256mb' or '1gb'."
|
||||
}
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Network Configuration
|
||||
# =============================================================================
|
||||
|
||||
|
||||
variable "pgadmin_port" {
|
||||
description = "pgAdmin web interface port"
|
||||
type = number
|
||||
default = 5050
|
||||
|
||||
validation {
|
||||
condition = var.pgadmin_port >= 1024 && var.pgadmin_port <= 65535
|
||||
error_message = "pgAdmin port must be between 1024 and 65535."
|
||||
}
|
||||
}
|
||||
|
||||
variable "pgadmin_email" {
|
||||
description = "pgAdmin login email"
|
||||
type = string
|
||||
default = "admin@example.com"
|
||||
|
||||
validation {
|
||||
condition = can(regex("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", var.pgadmin_email))
|
||||
error_message = "pgAdmin email must be a valid email address."
|
||||
}
|
||||
}
|
||||
|
||||
variable "pgadmin_password" {
|
||||
description = "pgAdmin login password"
|
||||
type = string
|
||||
default = "adminpassword"
|
||||
sensitive = true
|
||||
|
||||
validation {
|
||||
condition = length(var.pgadmin_password) >= 8
|
||||
error_message = "pgAdmin password must be at least 8 characters long."
|
||||
}
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Development Packages
|
||||
# =============================================================================
|
||||
|
||||
variable "npm_packages" {
|
||||
description = "Global npm packages to install"
|
||||
type = list(string)
|
||||
default = [
|
||||
"repomix", # Repository packaging tool
|
||||
"create-next-app", # Next.js app generator
|
||||
"nodemon", # Development server auto-reload
|
||||
"concurrently", # Run multiple commands
|
||||
"@types/node", # Node.js TypeScript types
|
||||
"typescript", # TypeScript compiler
|
||||
"eslint", # JavaScript linter
|
||||
"prettier" # Code formatter
|
||||
]
|
||||
}
|
||||
|
||||
variable "python_packages" {
|
||||
description = "Python packages to install via uv"
|
||||
type = list(string)
|
||||
default = [
|
||||
"fastapi", # Modern web framework
|
||||
"uvicorn", # ASGI server
|
||||
"requests", # HTTP library
|
||||
"pandas", # Data manipulation
|
||||
"numpy", # Numerical computing
|
||||
"psycopg2-binary", # PostgreSQL adapter
|
||||
"redis", # Redis client
|
||||
"qdrant-client", # Qdrant vector database client
|
||||
"python-dotenv" # Environment variable loading
|
||||
]
|
||||
}
|
||||
|
||||
variable "system_packages" {
|
||||
description = "Additional system packages to install"
|
||||
type = list(string)
|
||||
default = [
|
||||
"make", # Build tool
|
||||
"tree", # Directory tree viewer
|
||||
"jq", # JSON processor
|
||||
"curl", # HTTP client
|
||||
"wget", # File downloader
|
||||
"unzip", # Archive extractor
|
||||
"build-essential" # Compilation tools
|
||||
]
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# AI Development Tools
|
||||
# =============================================================================
|
||||
|
||||
variable "install_claude_code" {
|
||||
description = "Install Claude Code CLI for AI assistance"
|
||||
type = bool
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "install_cursor_support" {
|
||||
description = "Install Cursor IDE support and extensions"
|
||||
type = bool
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "install_windsurf_support" {
|
||||
description = "Install Windsurf IDE support"
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Performance Configuration
|
||||
# =============================================================================
|
||||
|
||||
variable "workspace_memory_limit" {
|
||||
description = "Memory limit for workspace container (MB)"
|
||||
type = number
|
||||
default = 8192
|
||||
|
||||
validation {
|
||||
condition = var.workspace_memory_limit >= 2048 && var.workspace_memory_limit <= 32768
|
||||
error_message = "Workspace memory limit must be between 2048MB (2GB) and 32768MB (32GB)."
|
||||
}
|
||||
}
|
||||
|
||||
variable "workspace_cpu_limit" {
|
||||
description = "CPU limit for workspace container (cores)"
|
||||
type = number
|
||||
default = 4
|
||||
|
||||
validation {
|
||||
condition = var.workspace_cpu_limit >= 1 && var.workspace_cpu_limit <= 16
|
||||
error_message = "Workspace CPU limit must be between 1 and 16 cores."
|
||||
}
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Feature Toggles
|
||||
# =============================================================================
|
||||
|
||||
variable "enable_pgadmin" {
|
||||
description = "Enable pgAdmin web interface (resource intensive)"
|
||||
type = bool
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "enable_monitoring" {
|
||||
description = "Enable container monitoring and metrics collection"
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "enable_jupyter" {
|
||||
description = "Enable Jupyter Lab for data science workflows"
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Common Tags
|
||||
# =============================================================================
|
||||
|
||||
variable "common_tags" {
|
||||
description = "Common tags to apply to all resources"
|
||||
type = map(string)
|
||||
default = {
|
||||
Environment = "development"
|
||||
ManagedBy = "terraform"
|
||||
Purpose = "remote-development"
|
||||
}
|
||||
}
|
||||
224
tf/workspace.tf
Normal file
224
tf/workspace.tf
Normal file
@@ -0,0 +1,224 @@
|
||||
# =============================================================================
|
||||
# Development Workspace Container
|
||||
# Main development environment with all required tools
|
||||
# =============================================================================
|
||||
|
||||
# =============================================================================
|
||||
# Coder Agent - Workspace Management
|
||||
# =============================================================================
|
||||
|
||||
resource "coder_agent" "main" {
|
||||
arch = data.coder_provisioner.me.arch
|
||||
os = "linux"
|
||||
dir = "/workspaces"
|
||||
|
||||
# Environment variables for development
|
||||
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
|
||||
"NODE_VERSION" = var.node_version
|
||||
"PYTHON_VERSION" = var.python_version
|
||||
"PATH" = "$PATH:/home/coder/.cargo/bin:/home/coder/.local/bin:/usr/local/bin"
|
||||
"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" : ""
|
||||
# Additional environment variables for scripts
|
||||
"ENABLE_SERVICES" = tostring(data.coder_parameter.enable_services.value)
|
||||
}
|
||||
|
||||
# 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"
|
||||
fi
|
||||
EOT
|
||||
|
||||
# Performance and resource monitoring
|
||||
metadata {
|
||||
display_name = "CPU Usage"
|
||||
key = "cpu_usage"
|
||||
script = "coder stat cpu"
|
||||
interval = 60
|
||||
timeout = 10
|
||||
}
|
||||
|
||||
metadata {
|
||||
display_name = "RAM Usage"
|
||||
key = "ram_usage"
|
||||
script = "coder stat mem"
|
||||
interval = 60
|
||||
timeout = 10
|
||||
}
|
||||
|
||||
metadata {
|
||||
display_name = "Disk Usage"
|
||||
key = "disk_usage"
|
||||
script = "coder stat disk --path /workspaces"
|
||||
interval = 300
|
||||
timeout = 10
|
||||
}
|
||||
|
||||
metadata {
|
||||
display_name = "Git Branch"
|
||||
key = "git_branch"
|
||||
script = "cd /workspaces && git branch --show-current 2>/dev/null || echo 'no-repo'"
|
||||
interval = 300
|
||||
timeout = 5
|
||||
}
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Main Development Container
|
||||
# =============================================================================
|
||||
|
||||
resource "docker_container" "workspace" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
image = docker_image.devcontainer.image_id
|
||||
name = local.container_name
|
||||
hostname = data.coder_workspace.me.name
|
||||
|
||||
# Container resource limits
|
||||
memory = var.workspace_memory_limit * 1024 * 1024 # Convert MB to bytes
|
||||
|
||||
# Environment variables
|
||||
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}",
|
||||
"NODE_VERSION=${var.node_version}",
|
||||
"PYTHON_VERSION=${var.python_version}",
|
||||
"CODER_AGENT_TOKEN=${coder_agent.main.token}"
|
||||
]
|
||||
|
||||
# Network configuration
|
||||
networks_advanced {
|
||||
name = docker_network.workspace.name
|
||||
}
|
||||
|
||||
# Host networking for Docker-in-Docker and reverse proxy support
|
||||
host {
|
||||
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
|
||||
|
||||
|
||||
# Volume mounts
|
||||
volumes {
|
||||
container_path = "/workspaces"
|
||||
volume_name = docker_volume.workspaces.name
|
||||
read_only = false
|
||||
}
|
||||
|
||||
# Mount the existing coder-home volume for user data persistence
|
||||
volumes {
|
||||
container_path = "/home/coder"
|
||||
volume_name = "bwk8ckcok8o84cc0o4os4sso_coder-home"
|
||||
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"
|
||||
}
|
||||
|
||||
# Working directory
|
||||
working_dir = "/workspaces"
|
||||
|
||||
# Keep container running
|
||||
command = ["/bin/bash", "-c", "${coder_agent.main.init_script} && sleep infinity"]
|
||||
|
||||
# Container labels for management
|
||||
labels {
|
||||
label = "coder.owner"
|
||||
value = data.coder_workspace_owner.me.name
|
||||
}
|
||||
labels {
|
||||
label = "coder.workspace_id"
|
||||
value = local.workspace_id
|
||||
}
|
||||
labels {
|
||||
label = "coder.project"
|
||||
value = var.project_name
|
||||
}
|
||||
|
||||
# Dependencies
|
||||
depends_on = [
|
||||
docker_network.workspace,
|
||||
docker_volume.workspaces,
|
||||
docker_image.devcontainer
|
||||
]
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# JetBrains Gateway Integration
|
||||
# =============================================================================
|
||||
|
||||
module "jetbrains_gateway" {
|
||||
count = data.coder_parameter.enable_jetbrains.value && data.coder_workspace.me.start_count > 0 ? 1 : 0
|
||||
source = "registry.coder.com/modules/jetbrains-gateway/coder"
|
||||
version = "1.0.29"
|
||||
agent_id = coder_agent.main.id
|
||||
folder = "/workspaces"
|
||||
jetbrains_ides = ["IU", "WS", "PY", "GO"]
|
||||
default = "IU"
|
||||
latest = false
|
||||
jetbrains_ide_versions = {
|
||||
"IU" = {
|
||||
build_number = "251.25410.129"
|
||||
version = "2025.1"
|
||||
}
|
||||
"WS" = {
|
||||
build_number = "251.25410.129"
|
||||
version = "2025.1"
|
||||
}
|
||||
"PY" = {
|
||||
build_number = "251.25410.129"
|
||||
version = "2025.1"
|
||||
}
|
||||
"GO" = {
|
||||
build_number = "251.25410.129"
|
||||
version = "2025.1"
|
||||
}
|
||||
"CL" = {
|
||||
build_number = "251.25410.129"
|
||||
version = "2025.1"
|
||||
}
|
||||
"PS" = {
|
||||
build_number = "251.25410.129"
|
||||
version = "2025.1"
|
||||
}
|
||||
"RR" = {
|
||||
build_number = "251.25410.129"
|
||||
version = "2025.1"
|
||||
}
|
||||
"RM" = {
|
||||
build_number = "251.25410.129"
|
||||
version = "2025.1"
|
||||
}
|
||||
"RD" = {
|
||||
build_number = "251.25410.129"
|
||||
version = "2025.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user