Copy and paste the following into main.tf
and run coder template push
:
1terraform {
2 required_providers {
3 coder = {
4 source = "coder/coder"
5 version = "~> 1.0.0"
6 }
7 kubernetes = {
8 source = "hashicorp/kubernetes"
9 }
10 }
11}
12
13provider "coder" {}
14provider "kubernetes" {
15 # Authenticate via ~/.kube/config or a Coder-specific ServiceAccount, depending on admin preferences
16 config_path = var.use_kubeconfig == true ? "~/.kube/config" : null
17}
18
19data "coder_provisioner" "me" {}
20data "coder_workspace" "me" {}
21data "coder_workspace_owner" "me" {}
22
23variable "use_kubeconfig" {
24 type = bool
25 description = <<-EOF
26 Use host kubeconfig? (true/false)
27
28 Set this to false if the Coder host is itself running as a Pod on the same
29 Kubernetes cluster as you are deploying workspaces to.
30
31 Set this to true if the Coder host is running outside the Kubernetes cluster
32 for workspaces. A valid "~/.kube/config" must be present on the Coder host.
33 EOF
34 default = false
35}
36
37variable "namespace" {
38 type = string
39 default = "default"
40 description = "The Kubernetes namespace to create workspaces in (must exist prior to creating workspaces). If the Coder host is itself running as a Pod on the same Kubernetes cluster as you are deploying workspaces to, set this to the same namespace."
41}
42
43variable "cache_repo" {
44 default = ""
45 description = "Use a container registry as a cache to speed up builds."
46 sensitive = true
47 type = string
48}
49
50data "coder_parameter" "cpu" {
51 type = "number"
52 name = "cpu"
53 display_name = "CPU"
54 description = "CPU limit (cores)."
55 default = "2"
56 icon = "/emojis/1f5a5.png"
57 mutable = true
58 validation {
59 min = 1
60 max = 99999
61 }
62 order = 1
63}
64
65data "coder_parameter" "memory" {
66 type = "number"
67 name = "memory"
68 display_name = "Memory"
69 description = "Memory limit (GiB)."
70 default = "2"
71 icon = "/icon/memory.svg"
72 mutable = true
73 validation {
74 min = 1
75 max = 99999
76 }
77 order = 2
78}
79
80data "coder_parameter" "workspaces_volume_size" {
81 name = "workspaces_volume_size"
82 display_name = "Workspaces volume size"
83 description = "Size of the `/workspaces` volume (GiB)."
84 default = "10"
85 type = "number"
86 icon = "/emojis/1f4be.png"
87 mutable = false
88 validation {
89 min = 1
90 max = 99999
91 }
92 order = 3
93}
94
95data "coder_parameter" "repo" {
96 description = "Select a repository to automatically clone and start working with a devcontainer."
97 display_name = "Repository (auto)"
98 mutable = true
99 name = "repo"
100 order = 4
101 type = "string"
102}
103
104data "coder_parameter" "fallback_image" {
105 default = "codercom/enterprise-base:ubuntu"
106 description = "This image runs if the devcontainer fails to build."
107 display_name = "Fallback Image"
108 mutable = true
109 name = "fallback_image"
110 order = 6
111}
112
113data "coder_parameter" "devcontainer_builder" {
114 description = <<-EOF
115Image that will build the devcontainer.
116We highly recommend using a specific release as the `:latest` tag will change.
117Find the latest version of Envbuilder here: https://github.com/coder/envbuilder/pkgs/container/envbuilder
118EOF
119 display_name = "Devcontainer Builder"
120 mutable = true
121 name = "devcontainer_builder"
122 default = "ghcr.io/coder/envbuilder:latest"
123 order = 7
124}
125
126variable "cache_repo_secret_name" {
127 default = ""
128 description = "Path to a docker config.json containing credentials to the provided cache repo, if required."
129 sensitive = true
130 type = string
131}
132
133data "kubernetes_secret" "cache_repo_dockerconfig_secret" {
134 count = var.cache_repo_secret_name == "" ? 0 : 1
135 metadata {
136 name = var.cache_repo_secret_name
137 namespace = var.namespace
138 }
139}
140
141locals {
142 deployment_name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}"
143 devcontainer_builder_image = data.coder_parameter.devcontainer_builder.value
144 git_author_name = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name)
145 git_author_email = data.coder_workspace_owner.me.email
146 repo_url = data.coder_parameter.repo.value
147}
148
149resource "kubernetes_persistent_volume_claim" "home" {
150 metadata {
151 name = "coder-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}-home"
152 namespace = var.namespace
153 labels = {
154 "app.kubernetes.io/name" = "coder-pvc"
155 "app.kubernetes.io/instance" = "coder-pvc-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}"
156 "app.kubernetes.io/part-of" = "coder"
157 //Coder-specific labels.
158 "com.coder.resource" = "true"
159 "com.coder.workspace.id" = data.coder_workspace.me.id
160 "com.coder.workspace.name" = data.coder_workspace.me.name
161 "com.coder.user.id" = data.coder_workspace_owner.me.id
162 "com.coder.user.username" = data.coder_workspace_owner.me.name
163 }
164 annotations = {
165 "com.coder.user.email" = data.coder_workspace_owner.me.email
166 }
167 }
168 wait_until_bound = false
169 spec {
170 access_modes = ["ReadWriteOnce"]
171 resources {
172 requests = {
173 storage = "${data.coder_parameter.workspaces_volume_size.value}Gi"
174 }
175 }
176 }
177}
178
179resource "kubernetes_deployment" "main" {
180 count = data.coder_workspace.me.start_count
181 depends_on = [
182 kubernetes_persistent_volume_claim.home
183 ]
184 wait_for_rollout = false
185 metadata {
186 name = local.deployment_name
187 namespace = var.namespace
188 labels = {
189 "app.kubernetes.io/name" = "coder-workspace"
190 "app.kubernetes.io/instance" = local.deployment_name
191 "app.kubernetes.io/part-of" = "coder"
192 "com.coder.resource" = "true"
193 "com.coder.workspace.id" = data.coder_workspace.me.id
194 "com.coder.workspace.name" = data.coder_workspace.me.name
195 "com.coder.user.id" = data.coder_workspace_owner.me.id
196 "com.coder.user.username" = data.coder_workspace_owner.me.name
197 }
198 annotations = {
199 "com.coder.user.email" = data.coder_workspace_owner.me.email
200 }
201 }
202
203 spec {
204 replicas = 1
205 selector {
206 match_labels = {
207 "app.kubernetes.io/name" = "coder-workspace"
208 }
209 }
210 strategy {
211 type = "Recreate"
212 }
213
214 template {
215 metadata {
216 labels = {
217 "app.kubernetes.io/name" = "coder-workspace"
218 }
219 }
220 spec {
221 security_context {}
222
223 container {
224 name = "dev"
225 image = local.devcontainer_builder_image
226 image_pull_policy = "Always"
227 security_context {}
228 env {
229 name = "CODER_AGENT_TOKEN"
230 value = coder_agent.main.token
231 }
232 env {
233 name = "CODER_AGENT_URL"
234 value = data.coder_workspace.me.access_url
235 }
236 env {
237 name = "ENVBUILDER_GIT_URL"
238 value = local.repo_url
239 }
240 env {
241 name = "ENVBUILDER_INIT_SCRIPT"
242 value = coder_agent.main.init_script
243 }
244 env {
245 name = "ENVBUILDER_FALLBACK_IMAGE"
246 value = data.coder_parameter.fallback_image.value
247 }
248 env {
249 name = "ENVBUILDER_CACHE_REPO"
250 value = var.cache_repo
251 }
252 env {
253 name = "ENVBUILDER_DOCKER_CONFIG_BASE64"
254 value = try(data.kubernetes_secret.cache_repo_dockerconfig_secret[0].data[".dockerconfigjson"], "")
255 }
256 # You may need to adjust this if you get an error regarding deleting files when building the workspace.
257 # For example, when testing in KinD, it was necessary to set `/product_name` and `/product_uuid` in
258 # addition to `/var/run`.
259 # env {
260 # name = "ENVBUILDER_IGNORE_PATHS"
261 # value = "/product_name,/product_uuid,/var/run"
262 # }
263 resources {
264 requests = {
265 "cpu" = "250m"
266 "memory" = "512Mi"
267 }
268 limits = {
269 "cpu" = "${data.coder_parameter.cpu.value}"
270 "memory" = "${data.coder_parameter.memory.value}Gi"
271 }
272 }
273 volume_mount {
274 mount_path = "/home/coder"
275 name = "home"
276 read_only = false
277 }
278 }
279
280 volume {
281 name = "home"
282 persistent_volume_claim {
283 claim_name = kubernetes_persistent_volume_claim.home.metadata.0.name
284 read_only = false
285 }
286 }
287
288 affinity {
289 // This affinity attempts to spread out all workspace pods evenly across
290 // nodes.
291 pod_anti_affinity {
292 preferred_during_scheduling_ignored_during_execution {
293 weight = 1
294 pod_affinity_term {
295 topology_key = "kubernetes.io/hostname"
296 label_selector {
297 match_expressions {
298 key = "app.kubernetes.io/name"
299 operator = "In"
300 values = ["coder-workspace"]
301 }
302 }
303 }
304 }
305 }
306 }
307 }
308 }
309 }
310}
311
312resource "coder_agent" "main" {
313 arch = data.coder_provisioner.me.arch
314 os = "linux"
315 startup_script = <<-EOT
316 set -e
317
318 # install and start code-server
319 curl -fsSL https://code-server.dev/install.sh | sh -s -- --method=standalone --prefix=/tmp/code-server --version 4.11.0
320 /tmp/code-server/bin/code-server --auth none --port 13337 >/tmp/code-server.log 2>&1 &
321 EOT
322 dir = "/workspaces"
323
324 # These environment variables allow you to make Git commits right away after creating a
325 # workspace. Note that they take precedence over configuration defined in ~/.gitconfig!
326 # You can remove this block if you'd prefer to configure Git manually or using
327 # dotfiles. (see docs/dotfiles.md)
328 env = {
329 GIT_AUTHOR_NAME = local.git_author_name
330 GIT_AUTHOR_EMAIL = local.git_author_email
331 GIT_COMMITTER_NAME = local.git_author_name
332 GIT_COMMITTER_EMAIL = local.git_author_email
333 }
334
335 # The following metadata blocks are optional. They are used to display
336 # information about your workspace in the dashboard. You can remove them
337 # if you don't want to display any information.
338 # For basic resources, you can use the `coder stat` command.
339 # If you need more control, you can write your own script.
340 metadata {
341 display_name = "CPU Usage"
342 key = "0_cpu_usage"
343 script = "coder stat cpu"
344 interval = 10
345 timeout = 1
346 }
347
348 metadata {
349 display_name = "RAM Usage"
350 key = "1_ram_usage"
351 script = "coder stat mem"
352 interval = 10
353 timeout = 1
354 }
355
356 metadata {
357 display_name = "Home Disk"
358 key = "3_home_disk"
359 script = "coder stat disk --path $HOME"
360 interval = 60
361 timeout = 1
362 }
363
364 metadata {
365 display_name = "CPU Usage (Host)"
366 key = "4_cpu_usage_host"
367 script = "coder stat cpu --host"
368 interval = 10
369 timeout = 1
370 }
371
372 metadata {
373 display_name = "Memory Usage (Host)"
374 key = "5_mem_usage_host"
375 script = "coder stat mem --host"
376 interval = 10
377 timeout = 1
378 }
379
380 metadata {
381 display_name = "Load Average (Host)"
382 key = "6_load_host"
383 # get load avg scaled by number of cores
384 script = <<EOT
385 echo "`cat /proc/loadavg | awk '{ print $1 }'` `nproc`" | awk '{ printf "%0.2f", $1/$2 }'
386 EOT
387 interval = 60
388 timeout = 1
389 }
390
391 metadata {
392 display_name = "Swap Usage (Host)"
393 key = "7_swap_host"
394 script = <<EOT
395 free -b | awk '/^Swap/ { printf("%.1f/%.1f", $3/1024.0/1024.0/1024.0, $2/1024.0/1024.0/1024.0) }'
396 EOT
397 interval = 10
398 timeout = 1
399 }
400}
401
402resource "coder_app" "code-server" {
403 agent_id = coder_agent.main.id
404 slug = "code-server"
405 display_name = "code-server"
406 url = "http://localhost:13337/?folder=/workspaces"
407 icon = "/icon/code.svg"
408 subdomain = false
409 share = "owner"
410
411 healthcheck {
412 url = "http://localhost:13337/healthz"
413 interval = 5
414 threshold = 6
415 }
416}
417