mirror of
https://github.com/coder/coder.git
synced 2025-07-13 21:36:50 +00:00
docs: restructure docs (#14421)
Closes #13434 Supersedes #14182 --------- Co-authored-by: Ethan <39577870+ethanndickson@users.noreply.github.com> Co-authored-by: Ethan Dickson <ethan@coder.com> Co-authored-by: Ben Potter <ben@coder.com> Co-authored-by: Stephen Kirby <58410745+stirby@users.noreply.github.com> Co-authored-by: Stephen Kirby <me@skirby.dev> Co-authored-by: EdwardAngert <17991901+EdwardAngert@users.noreply.github.com> Co-authored-by: Edward Angert <EdwardAngert@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
288df75686
commit
419eba5fb6
148
docs/admin/templates/extending-templates/agent-metadata.md
Normal file
148
docs/admin/templates/extending-templates/agent-metadata.md
Normal file
@ -0,0 +1,148 @@
|
||||
# Agent metadata
|
||||
|
||||

|
||||
|
||||
You can show live operational metrics to workspace users with agent metadata. It
|
||||
is the dynamic complement of [resource metadata](./resource-metadata.md).
|
||||
|
||||
You specify agent metadata in the
|
||||
[`coder_agent`](https://registry.terraform.io/providers/coder/coder/latest/docs/resources/agent).
|
||||
|
||||
## Examples
|
||||
|
||||
All of these examples use
|
||||
[heredoc strings](https://developer.hashicorp.com/terraform/language/expressions/strings#heredoc-strings)
|
||||
for the script declaration. With heredoc strings, you can script without messy
|
||||
escape codes, just as if you were working in your terminal.
|
||||
|
||||
Some of the examples use the [`coder stat`](../../../reference/cli/stat.md)
|
||||
command. This is useful for determining CPU and memory usage of the VM or
|
||||
container that the workspace is running in, which is more accurate than resource
|
||||
usage about the workspace's host.
|
||||
|
||||
Here's a standard set of metadata snippets for Linux agents:
|
||||
|
||||
```tf
|
||||
resource "coder_agent" "main" {
|
||||
os = "linux"
|
||||
...
|
||||
metadata {
|
||||
display_name = "CPU Usage"
|
||||
key = "cpu"
|
||||
# Uses the coder stat command to get container CPU usage.
|
||||
script = "coder stat cpu"
|
||||
interval = 1
|
||||
timeout = 1
|
||||
}
|
||||
|
||||
metadata {
|
||||
display_name = "Memory Usage"
|
||||
key = "mem"
|
||||
# Uses the coder stat command to get container memory usage in GiB.
|
||||
script = "coder stat mem --prefix Gi"
|
||||
interval = 1
|
||||
timeout = 1
|
||||
}
|
||||
|
||||
metadata {
|
||||
display_name = "CPU Usage (Host)"
|
||||
key = "cpu_host"
|
||||
# calculates CPU usage by summing the "us", "sy" and "id" columns of
|
||||
# top.
|
||||
script = <<EOT
|
||||
top -bn1 | awk 'FNR==3 {printf "%2.0f%%", $2+$3+$4}'
|
||||
EOT
|
||||
interval = 1
|
||||
timeout = 1
|
||||
}
|
||||
|
||||
metadata {
|
||||
display_name = "Memory Usage (Host)"
|
||||
key = "mem_host"
|
||||
script = <<EOT
|
||||
free | awk '/^Mem/ { printf("%.0f%%", $4/$2 * 100.0) }'
|
||||
EOT
|
||||
interval = 1
|
||||
timeout = 1
|
||||
}
|
||||
|
||||
metadata {
|
||||
display_name = "Disk Usage"
|
||||
key = "disk"
|
||||
script = "df -h | awk '$6 ~ /^\\/$/ { print $5 }'"
|
||||
interval = 1
|
||||
timeout = 1
|
||||
}
|
||||
|
||||
metadata {
|
||||
display_name = "Load Average"
|
||||
key = "load"
|
||||
script = <<EOT
|
||||
awk '{print $1,$2,$3}' /proc/loadavg
|
||||
EOT
|
||||
interval = 1
|
||||
timeout = 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Useful utilities
|
||||
|
||||
You can also show agent metadata for information about the workspace's host.
|
||||
|
||||
[top](https://manpages.ubuntu.com/manpages/jammy/en/man1/top.1.html) is
|
||||
available in most Linux distributions and provides virtual memory, CPU and IO
|
||||
statistics. Running `top` produces output that looks like:
|
||||
|
||||
```text
|
||||
%Cpu(s): 65.8 us, 4.4 sy, 0.0 ni, 29.3 id, 0.3 wa, 0.0 hi, 0.2 si, 0.0 st
|
||||
MiB Mem : 16009.0 total, 493.7 free, 4624.8 used, 10890.5 buff/cache
|
||||
MiB Swap: 0.0 total, 0.0 free, 0.0 used. 11021.3 avail Mem
|
||||
```
|
||||
|
||||
[vmstat](https://manpages.ubuntu.com/manpages/jammy/en/man8/vmstat.8.html) is
|
||||
available in most Linux distributions and provides virtual memory, CPU and IO
|
||||
statistics. Running `vmstat` produces output that looks like:
|
||||
|
||||
```text
|
||||
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
|
||||
r b swpd free buff cache si so bi bo in cs us sy id wa st
|
||||
0 0 19580 4781680 12133692 217646944 0 2 4 32 1 0 1 1 98 0 0
|
||||
```
|
||||
|
||||
[dstat](https://manpages.ubuntu.com/manpages/jammy/man1/dstat.1.html) is
|
||||
considerably more parseable than `vmstat` but often not included in base images.
|
||||
It is easily installed by most package managers under the name `dstat`. The
|
||||
output of running `dstat 1 1` looks like:
|
||||
|
||||
```text
|
||||
--total-cpu-usage-- -dsk/total- -net/total- ---paging-- ---system--
|
||||
usr sys idl wai stl| read writ| recv send| in out | int csw
|
||||
1 1 98 0 0|3422k 25M| 0 0 | 153k 904k| 123k 174k
|
||||
```
|
||||
|
||||
## Managing the database load
|
||||
|
||||
Agent metadata can generate a significant write load and overwhelm your Coder
|
||||
database if you're not careful. The approximate writes per second can be
|
||||
calculated using the formula:
|
||||
|
||||
```text
|
||||
(metadata_count * num_running_agents * 2) / metadata_avg_interval
|
||||
```
|
||||
|
||||
For example, let's say you have
|
||||
|
||||
- 10 running agents
|
||||
- each with 6 metadata snippets
|
||||
- with an average interval of 4 seconds
|
||||
|
||||
You can expect `(10 * 6 * 2) / 4`, or 30 writes per second.
|
||||
|
||||
One of the writes is to the `UNLOGGED` `workspace_agent_metadata` table and the
|
||||
other to the `NOTIFY` query that enables live stats streaming in the UI.
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Resource metadata](./resource-metadata.md)
|
||||
- [Parameters](./parameters.md)
|
461
docs/admin/templates/extending-templates/docker-in-workspaces.md
Normal file
461
docs/admin/templates/extending-templates/docker-in-workspaces.md
Normal file
@ -0,0 +1,461 @@
|
||||
# Docker in Workspaces
|
||||
|
||||
There are a few ways to run Docker within container-based Coder workspaces.
|
||||
|
||||
| Method | Description | Limitations |
|
||||
| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [Sysbox container runtime](#sysbox-container-runtime) | Install the Sysbox runtime on your Kubernetes nodes or Docker host(s) for secure docker-in-docker and systemd-in-docker. Works with GKE, EKS, AKS, Docker. | Requires [compatible nodes](https://github.com/nestybox/sysbox#host-requirements). [Limitations](https://github.com/nestybox/sysbox/blob/master/docs/user-guide/limitations.md) |
|
||||
| [Envbox](#envbox) | A container image with all the packages necessary to run an inner Sysbox container. Removes the need to setup sysbox-runc on your nodes. Works with GKE, EKS, AKS. | Requires running the outer container as privileged (the inner container that acts as the workspace is locked down). Requires compatible [nodes](https://github.com/nestybox/sysbox/blob/master/docs/distro-compat.md#sysbox-distro-compatibility). |
|
||||
| [Rootless Podman](#rootless-podman) | Run Podman inside Coder workspaces. Does not require a custom runtime or privileged containers. Works with GKE, EKS, AKS, RKE, OpenShift | Requires smarter-device-manager for FUSE mounts. [See all](https://github.com/containers/podman/blob/main/rootless.md#shortcomings-of-rootless-podman) |
|
||||
| [Privileged docker sidecar](#privileged-sidecar-container) | Run Docker as a privileged sidecar container. | Requires a privileged container. Workspaces can break out to root on the host machine. |
|
||||
|
||||
## Sysbox container runtime
|
||||
|
||||
The [Sysbox](https://github.com/nestybox/sysbox) container runtime allows
|
||||
unprivileged users to run system-level applications, such as Docker, securely
|
||||
from the workspace containers. Sysbox requires a
|
||||
[compatible Linux distribution](https://github.com/nestybox/sysbox/blob/master/docs/distro-compat.md)
|
||||
to implement these security features. Sysbox can also be used to run systemd
|
||||
inside Coder workspaces. See [Systemd in Docker](#systemd-in-docker).
|
||||
|
||||
### Use Sysbox in Docker-based templates
|
||||
|
||||
After [installing Sysbox](https://github.com/nestybox/sysbox#installation) on
|
||||
the Coder host, modify your template to use the sysbox-runc runtime:
|
||||
|
||||
```tf
|
||||
resource "docker_container" "workspace" {
|
||||
# ...
|
||||
name = "coder-${data.coder_workspace.me.owner}-${lower(data.coder_workspace.me.name)}"
|
||||
image = "codercom/enterprise-base:ubuntu"
|
||||
env = ["CODER_AGENT_TOKEN=${coder_agent.main.token}"]
|
||||
command = ["sh", "-c", coder_agent.main.init_script]
|
||||
# Use the Sysbox container runtime (required)
|
||||
runtime = "sysbox-runc"
|
||||
}
|
||||
|
||||
resource "coder_agent" "main" {
|
||||
arch = data.coder_provisioner.me.arch
|
||||
os = "linux"
|
||||
startup_script = <<EOF
|
||||
#!/bin/sh
|
||||
|
||||
# Start Docker
|
||||
sudo dockerd &
|
||||
|
||||
# ...
|
||||
EOF
|
||||
}
|
||||
```
|
||||
|
||||
### Use Sysbox in Kubernetes-based templates
|
||||
|
||||
After
|
||||
[installing Sysbox on Kubernetes](https://github.com/nestybox/sysbox/blob/master/docs/user-guide/install-k8s.md),
|
||||
modify your template to use the sysbox-runc RuntimeClass. This requires the
|
||||
Kubernetes Terraform provider version 2.16.0 or greater.
|
||||
|
||||
```tf
|
||||
terraform {
|
||||
required_providers {
|
||||
coder = {
|
||||
source = "coder/coder"
|
||||
}
|
||||
kubernetes = {
|
||||
source = "hashicorp/kubernetes"
|
||||
version = "2.16.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
variable "workspaces_namespace" {
|
||||
default = "coder-namespace"
|
||||
}
|
||||
|
||||
data "coder_workspace" "me" {}
|
||||
|
||||
resource "coder_agent" "main" {
|
||||
os = "linux"
|
||||
arch = "amd64"
|
||||
dir = "/home/coder"
|
||||
startup_script = <<EOF
|
||||
#!/bin/sh
|
||||
|
||||
# Start Docker
|
||||
sudo dockerd &
|
||||
|
||||
# ...
|
||||
EOF
|
||||
}
|
||||
|
||||
resource "kubernetes_pod" "dev" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
metadata {
|
||||
name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}"
|
||||
namespace = var.workspaces_namespace
|
||||
annotations = {
|
||||
"io.kubernetes.cri-o.userns-mode" = "auto:size=65536"
|
||||
}
|
||||
}
|
||||
|
||||
spec {
|
||||
runtime_class_name = "sysbox-runc"
|
||||
# Use the Sysbox container runtime (required)
|
||||
security_context {
|
||||
run_as_user = 1000
|
||||
fs_group = 1000
|
||||
}
|
||||
container {
|
||||
name = "dev"
|
||||
env {
|
||||
name = "CODER_AGENT_TOKEN"
|
||||
value = coder_agent.main.token
|
||||
}
|
||||
image = "codercom/enterprise-base:ubuntu"
|
||||
command = ["sh", "-c", coder_agent.main.init_script]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Envbox
|
||||
|
||||
[Envbox](https://github.com/coder/envbox) is an image developed and maintained
|
||||
by Coder that bundles the sysbox runtime. It works by starting an outer
|
||||
container that manages the various sysbox daemons and spawns an unprivileged
|
||||
inner container that acts as the user's workspace. The inner container is able
|
||||
to run system-level software similar to a regular virtual machine (e.g.
|
||||
`systemd`, `dockerd`, etc). Envbox offers the following benefits over running
|
||||
sysbox directly on the nodes:
|
||||
|
||||
- No custom runtime installation or management on your Kubernetes nodes.
|
||||
- No limit to the number of pods that run envbox.
|
||||
|
||||
Some drawbacks include:
|
||||
|
||||
- The outer container must be run as privileged
|
||||
- Note: the inner container is _not_ privileged. For more information on the
|
||||
security of sysbox containers see sysbox's
|
||||
[official documentation](https://github.com/nestybox/sysbox/blob/master/docs/user-guide/security.md).
|
||||
- Initial workspace startup is slower than running `sysbox-runc` directly on the
|
||||
nodes. This is due to `envbox` having to pull the image to its own Docker
|
||||
cache on its initial startup. Once the image is cached in `envbox`, startup
|
||||
performance is similar.
|
||||
|
||||
Envbox requires the same kernel requirements as running sysbox directly on the
|
||||
nodes. Refer to sysbox's
|
||||
[compatibility matrix](https://github.com/nestybox/sysbox/blob/master/docs/distro-compat.md#sysbox-distro-compatibility)
|
||||
to ensure your nodes are compliant.
|
||||
|
||||
To get started with `envbox` check out the
|
||||
[starter template](https://github.com/coder/coder/tree/main/examples/templates/envbox)
|
||||
or visit the [repo](https://github.com/coder/envbox).
|
||||
|
||||
### Authenticating with a Private Registry
|
||||
|
||||
Authenticating with a private container registry can be done by referencing the
|
||||
credentials via the `CODER_IMAGE_PULL_SECRET` environment variable. It is
|
||||
encouraged to populate this
|
||||
[environment variable](https://kubernetes.io/docs/tasks/inject-data-application/distribute-credentials-secure/#define-container-environment-variables-using-secret-data)
|
||||
by using a Kubernetes
|
||||
[secret](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#registry-secret-existing-credentials).
|
||||
|
||||
Refer to your container registry documentation to understand how to best create
|
||||
this secret.
|
||||
|
||||
The following shows a minimal example using a the JSON API key from a GCP
|
||||
service account to pull a private image:
|
||||
|
||||
```bash
|
||||
# Create the secret
|
||||
$ kubectl create secret docker-registry <name> \
|
||||
--docker-server=us.gcr.io \
|
||||
--docker-username=_json_key \
|
||||
--docker-password="$(cat ./json-key-file.yaml)" \
|
||||
--docker-email=<service-account-email>
|
||||
```
|
||||
|
||||
```tf
|
||||
env {
|
||||
name = "CODER_IMAGE_PULL_SECRET"
|
||||
value_from {
|
||||
secret_key_ref {
|
||||
name = "<name>"
|
||||
key = ".dockerconfigjson"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Rootless podman
|
||||
|
||||
[Podman](https://docs.podman.io/en/latest/) is Docker alternative that is
|
||||
compatible with OCI containers specification. which can run rootless inside
|
||||
Kubernetes pods. No custom RuntimeClass is required.
|
||||
|
||||
Before using Podman, please review the following documentation:
|
||||
|
||||
- [Basic setup and use of Podman in a rootless environment](https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md)
|
||||
|
||||
- [Shortcomings of Rootless Podman](https://github.com/containers/podman/blob/main/rootless.md#shortcomings-of-rootless-podman)
|
||||
|
||||
1. Enable
|
||||
[smart-device-manager](https://gitlab.com/arm-research/smarter/smarter-device-manager#enabling-access)
|
||||
to securely expose a FUSE devices to pods.
|
||||
|
||||
```shell
|
||||
cat <<EOF | kubectl create -f -
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: fuse-device-plugin-daemonset
|
||||
namespace: kube-system
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
name: fuse-device-plugin-ds
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
name: fuse-device-plugin-ds
|
||||
spec:
|
||||
hostNetwork: true
|
||||
containers:
|
||||
- name: fuse-device-plugin-ctr
|
||||
image: soolaugust/fuse-device-plugin:v1.0
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop: ["ALL"]
|
||||
volumeMounts:
|
||||
- name: device-plugin
|
||||
mountPath: /var/lib/kubelet/device-plugins
|
||||
volumes:
|
||||
- name: device-plugin
|
||||
hostPath:
|
||||
path: /var/lib/kubelet/device-plugins
|
||||
imagePullSecrets:
|
||||
- name: registry-secret
|
||||
EOF
|
||||
```
|
||||
|
||||
2. Be sure to label your nodes to enable smarter-device-manager:
|
||||
|
||||
```shell
|
||||
kubectl get nodes
|
||||
kubectl label nodes --all smarter-device-manager=enabled
|
||||
```
|
||||
|
||||
> ⚠️ **Warning**: If you are using a managed Kubernetes distribution (e.g.
|
||||
> AKS, EKS, GKE), be sure to set node labels via your cloud provider.
|
||||
> Otherwise, your nodes may drop the labels and break podman functionality.
|
||||
|
||||
3. For systems running SELinux (typically Fedora-, CentOS-, and Red Hat-based
|
||||
systems), you might need to disable SELinux or set it to permissive mode.
|
||||
|
||||
4. Use this
|
||||
[kubernetes-with-podman](https://github.com/coder/community-templates/tree/main/kubernetes-podman)
|
||||
example template, or make your own.
|
||||
|
||||
```shell
|
||||
echo "kubernetes-with-podman" | coder templates init
|
||||
cd ./kubernetes-with-podman
|
||||
coder templates create
|
||||
```
|
||||
|
||||
> For more information around the requirements of rootless podman pods, see:
|
||||
> [How to run Podman inside of Kubernetes](https://www.redhat.com/sysadmin/podman-inside-kubernetes)
|
||||
|
||||
## Privileged sidecar container
|
||||
|
||||
A
|
||||
[privileged container](https://docs.docker.com/engine/containers/run/#runtime-privilege-and-linux-capabilities)
|
||||
can be added to your templates to add docker support. This may come in handy if
|
||||
your nodes cannot run Sysbox.
|
||||
|
||||
> ⚠️ **Warning**: This is insecure. Workspaces will be able to gain root access
|
||||
> to the host machine.
|
||||
|
||||
### Use a privileged sidecar container in Docker-based templates
|
||||
|
||||
```tf
|
||||
resource "coder_agent" "main" {
|
||||
os = "linux"
|
||||
arch = "amd64"
|
||||
}
|
||||
|
||||
resource "docker_network" "private_network" {
|
||||
name = "network-${data.coder_workspace.me.id}"
|
||||
}
|
||||
|
||||
resource "docker_container" "dind" {
|
||||
image = "docker:dind"
|
||||
privileged = true
|
||||
name = "dind-${data.coder_workspace.me.id}"
|
||||
entrypoint = ["dockerd", "-H", "tcp://0.0.0.0:2375"]
|
||||
networks_advanced {
|
||||
name = docker_network.private_network.name
|
||||
}
|
||||
}
|
||||
|
||||
resource "docker_container" "workspace" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
image = "codercom/enterprise-base:ubuntu"
|
||||
name = "dev-${data.coder_workspace.me.id}"
|
||||
command = ["sh", "-c", coder_agent.main.init_script]
|
||||
env = [
|
||||
"CODER_AGENT_TOKEN=${coder_agent.main.token}",
|
||||
"DOCKER_HOST=${docker_container.dind.name}:2375"
|
||||
]
|
||||
networks_advanced {
|
||||
name = docker_network.private_network.name
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Use a privileged sidecar container in Kubernetes-based templates
|
||||
|
||||
```tf
|
||||
terraform {
|
||||
required_providers {
|
||||
coder = {
|
||||
source = "coder/coder"
|
||||
}
|
||||
kubernetes = {
|
||||
source = "hashicorp/kubernetes"
|
||||
version = "2.16.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
variable "workspaces_namespace" {
|
||||
default = "coder-namespace"
|
||||
}
|
||||
|
||||
data "coder_workspace" "me" {}
|
||||
|
||||
resource "coder_agent" "main" {
|
||||
os = "linux"
|
||||
arch = "amd64"
|
||||
}
|
||||
|
||||
resource "kubernetes_pod" "main" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
metadata {
|
||||
name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}"
|
||||
namespace = var.namespace
|
||||
}
|
||||
spec {
|
||||
# Run a privileged dind (Docker in Docker) container
|
||||
container {
|
||||
name = "docker-sidecar"
|
||||
image = "docker:dind"
|
||||
security_context {
|
||||
privileged = true
|
||||
run_as_user = 0
|
||||
}
|
||||
command = ["dockerd", "-H", "tcp://127.0.0.1:2375"]
|
||||
}
|
||||
container {
|
||||
name = "dev"
|
||||
image = "codercom/enterprise-base:ubuntu"
|
||||
command = ["sh", "-c", coder_agent.main.init_script]
|
||||
security_context {
|
||||
run_as_user = "1000"
|
||||
}
|
||||
env {
|
||||
name = "CODER_AGENT_TOKEN"
|
||||
value = coder_agent.main.token
|
||||
}
|
||||
# Use the Docker daemon in the "docker-sidecar" container
|
||||
env {
|
||||
name = "DOCKER_HOST"
|
||||
value = "localhost:2375"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Systemd in Docker
|
||||
|
||||
Additionally, [Sysbox](https://github.com/nestybox/sysbox) can be used to give
|
||||
workspaces full `systemd` capabilities.
|
||||
|
||||
After
|
||||
[installing Sysbox on Kubernetes](https://github.com/nestybox/sysbox/blob/master/docs/user-guide/install-k8s.md),
|
||||
modify your template to use the sysbox-runc RuntimeClass. This requires the
|
||||
Kubernetes Terraform provider version 2.16.0 or greater.
|
||||
|
||||
```tf
|
||||
terraform {
|
||||
required_providers {
|
||||
coder = {
|
||||
source = "coder/coder"
|
||||
}
|
||||
kubernetes = {
|
||||
source = "hashicorp/kubernetes"
|
||||
version = "2.16.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
variable "workspaces_namespace" {
|
||||
default = "coder-namespace"
|
||||
}
|
||||
|
||||
data "coder_workspace" "me" {}
|
||||
|
||||
resource "coder_agent" "main" {
|
||||
os = "linux"
|
||||
arch = "amd64"
|
||||
dir = "/home/coder"
|
||||
}
|
||||
|
||||
resource "kubernetes_pod" "dev" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
metadata {
|
||||
name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}"
|
||||
namespace = var.workspaces_namespace
|
||||
annotations = {
|
||||
"io.kubernetes.cri-o.userns-mode" = "auto:size=65536"
|
||||
}
|
||||
}
|
||||
|
||||
spec {
|
||||
|
||||
# Use Sysbox container runtime (required)
|
||||
runtime_class_name = "sysbox-runc"
|
||||
|
||||
# Run as root in order to start systemd (required)
|
||||
security_context {
|
||||
run_as_user = 0
|
||||
fs_group = 0
|
||||
}
|
||||
|
||||
container {
|
||||
name = "dev"
|
||||
env {
|
||||
name = "CODER_AGENT_TOKEN"
|
||||
value = coder_agent.main.token
|
||||
}
|
||||
image = "codercom/enterprise-base:ubuntu"
|
||||
command = ["sh", "-c", <<EOF
|
||||
# Start the Coder agent as the "coder" user
|
||||
# once systemd has started up
|
||||
sudo -u coder --preserve-env=CODER_AGENT_TOKEN /bin/bash -- <<-' EOT' &
|
||||
while [[ ! $(systemctl is-system-running) =~ ^(running|degraded) ]]
|
||||
do
|
||||
echo "Waiting for system to start... $(systemctl is-system-running)"
|
||||
sleep 2
|
||||
done
|
||||
${coder_agent.main.init_script}
|
||||
EOT
|
||||
|
||||
exec /sbin/init
|
||||
EOF
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
96
docs/admin/templates/extending-templates/external-auth.md
Normal file
96
docs/admin/templates/extending-templates/external-auth.md
Normal file
@ -0,0 +1,96 @@
|
||||
# External Authentication
|
||||
|
||||
Coder integrates with any OpenID Connect provider to automate away the need for
|
||||
developers to authenticate with external services within their workspace. This
|
||||
can be used to authenticate with git providers, private registries, or any other
|
||||
service that requires authentication.
|
||||
|
||||
## External Auth Providers
|
||||
|
||||
External auth providers are configured using environment variables in the Coder
|
||||
Control Plane. See
|
||||
|
||||
## Git Providers
|
||||
|
||||
When developers use `git` inside their workspace, they are prompted to
|
||||
authenticate. After that, Coder will store and refresh tokens for future
|
||||
operations.
|
||||
|
||||
<video autoplay playsinline loop>
|
||||
<source src="https://github.com/coder/coder/blob/main/site/static/external-auth.mp4?raw=true" type="video/mp4">
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
|
||||
### Require git authentication in templates
|
||||
|
||||
If your template requires git authentication (e.g. running `git clone` in the
|
||||
[startup_script](https://registry.terraform.io/providers/coder/coder/latest/docs/resources/agent#startup_script)),
|
||||
you can require users authenticate via git prior to creating a workspace:
|
||||
|
||||

|
||||
|
||||
### Native git authentication will auto-refresh tokens
|
||||
|
||||
<blockquote class="info">
|
||||
<p>
|
||||
This is the preferred authentication method.
|
||||
</p>
|
||||
</blockquote>
|
||||
|
||||
By default, the coder agent will configure native `git` authentication via the
|
||||
`GIT_ASKPASS` environment variable. Meaning, with no additional configuration,
|
||||
external authentication will work with native `git` commands.
|
||||
|
||||
To check the auth token being used **from inside a running workspace**, run:
|
||||
|
||||
```shell
|
||||
# If the exit code is non-zero, then the user is not authenticated with the
|
||||
# external provider.
|
||||
coder external-auth access-token <external-auth-id>
|
||||
```
|
||||
|
||||
Note: Some IDE's override the `GIT_ASKPASS` environment variable and need to be
|
||||
configured.
|
||||
|
||||
**VSCode**
|
||||
|
||||
Use the
|
||||
[Coder](https://marketplace.visualstudio.com/items?itemName=coder.coder-remote)
|
||||
extension to automatically configure these settings for you!
|
||||
|
||||
Otherwise, you can manually configure the following settings:
|
||||
|
||||
- Set `git.terminalAuthentication` to `false`
|
||||
- Set `git.useIntegratedAskPass` to `false`
|
||||
|
||||
### Hard coded tokens do not auto-refresh
|
||||
|
||||
If the token is required to be inserted into the workspace, for example
|
||||
[GitHub cli](https://cli.github.com/), the auth token can be inserted from the
|
||||
template. This token will not auto-refresh. The following example will
|
||||
authenticate via GitHub and auto-clone a repo into the `~/coder` directory.
|
||||
|
||||
```tf
|
||||
data "coder_external_auth" "github" {
|
||||
# Matches the ID of the external auth provider in Coder.
|
||||
id = "github"
|
||||
}
|
||||
|
||||
resource "coder_agent" "dev" {
|
||||
os = "linux"
|
||||
arch = "amd64"
|
||||
dir = "~/coder"
|
||||
env = {
|
||||
GITHUB_TOKEN : data.coder_external_auth.github.access_token
|
||||
}
|
||||
startup_script = <<EOF
|
||||
if [ ! -d ~/coder ]; then
|
||||
git clone https://github.com/coder/coder
|
||||
fi
|
||||
EOF
|
||||
}
|
||||
```
|
||||
|
||||
See the
|
||||
[Terraform provider documentation](https://registry.terraform.io/providers/coder/coder/latest/docs/data-sources/external_auth)
|
||||
for all available options.
|
80
docs/admin/templates/extending-templates/icons.md
Normal file
80
docs/admin/templates/extending-templates/icons.md
Normal file
@ -0,0 +1,80 @@
|
||||
# Icons
|
||||
|
||||
Coder uses icons in several places, including ones that can be configured
|
||||
throughout the app, or specified in your Terraform. They're specified by a URL,
|
||||
which can be to an image hosted on a CDN of your own, or one of the icons that
|
||||
come bundled with your Coder deployment.
|
||||
|
||||
- **Template Icons**:
|
||||
|
||||
- Make templates and workspaces visually recognizable with a relevant or
|
||||
memorable icon
|
||||
|
||||
- [**Terraform**](https://registry.terraform.io/providers/coder/coder/latest/docs):
|
||||
|
||||
- [`coder_app`](https://registry.terraform.io/providers/coder/coder/latest/docs/resources/app#icon)
|
||||
- [`coder_parameter`](https://registry.terraform.io/providers/coder/coder/latest/docs/data-sources/parameter#icon)
|
||||
and
|
||||
[`option`](https://registry.terraform.io/providers/coder/coder/latest/docs/data-sources/parameter#nested-schema-for-option)
|
||||
blocks
|
||||
- [`coder_script`](https://registry.terraform.io/providers/coder/coder/latest/docs/resources/script#icon)
|
||||
- [`coder_metadata`](https://registry.terraform.io/providers/coder/coder/latest/docs/resources/metadata#icon)
|
||||
|
||||
These can all be configured to use an icon by setting the `icon` field.
|
||||
|
||||
```tf
|
||||
data "coder_parameter" "my_parameter" {
|
||||
icon = "/icon/coder.svg"
|
||||
|
||||
option {
|
||||
icon = "/emojis/1f3f3-fe0f-200d-26a7-fe0f.png"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- [**Authentication Providers**](https://coder.com/docs/admin/external-auth):
|
||||
|
||||
- Use icons for external authentication providers to make them recognizable.
|
||||
You can set an icon for each provider by setting the
|
||||
`CODER_EXTERNAL_AUTH_X_ICON` environment variable, where `X` is the number
|
||||
of the provider.
|
||||
|
||||
```env
|
||||
CODER_EXTERNAL_AUTH_0_ICON=/icon/github.svg
|
||||
CODER_EXTERNAL_AUTH_1_ICON=/icon/google.svg
|
||||
```
|
||||
|
||||
- [**Support Links**](../../setup/appearance.md#support-links):
|
||||
|
||||
- Use icons for support links to make them recognizable. You can set the
|
||||
`icon` field for each link in `CODER_SUPPORT_LINKS` array.
|
||||
|
||||
## Bundled icons
|
||||
|
||||
Coder is distributed with a bundle of icons for popular cloud providers and
|
||||
programming languages. You can see all of the icons (or suggest new ones) in our
|
||||
repository on
|
||||
[GitHub](https://github.com/coder/coder/tree/main/site/static/icon).
|
||||
|
||||
You can also view the entire list, with search and previews, by navigating to
|
||||
/icons on your Coder deployment. E.g. [https://coder.example.com/icons](#). This
|
||||
can be particularly useful in airgapped deployments.
|
||||
|
||||

|
||||
|
||||
## External icons
|
||||
|
||||
You can use any image served over HTTPS as an icon, by specifying the full URL
|
||||
of the image. We recommend that you use a CDN that you control, but it can be
|
||||
served from any source that you trust.
|
||||
|
||||
You can also embed an image by using data: URLs.
|
||||
|
||||
- Only the https: and data: protocols are supported in icon URLs (not http:)
|
||||
|
||||
- Be careful when using images hosted by someone else; they might disappear or
|
||||
change!
|
||||
|
||||
- Be careful when using data: URLs. They can get rather large, and can
|
||||
negatively impact loading times for pages and queries they appear in. Only use
|
||||
them for very small icons that compress well.
|
93
docs/admin/templates/extending-templates/index.md
Normal file
93
docs/admin/templates/extending-templates/index.md
Normal file
@ -0,0 +1,93 @@
|
||||
# Extending templates
|
||||
|
||||
There are a variety of Coder-native features to extend the configuration of your
|
||||
development environments. Many of the following features are defined in your
|
||||
templates using the
|
||||
[Coder Terraform provider](https://registry.terraform.io/providers/coder/coder/latest/docs).
|
||||
The provider docs will provide code examples for usage; alternatively, you can
|
||||
view our
|
||||
[example templates](https://github.com/coder/coder/tree/main/examples/templates)
|
||||
to get started.
|
||||
|
||||
## Workspace agents
|
||||
|
||||
For users to connect to a workspace, the template must include a
|
||||
[`coder_agent`](https://registry.terraform.io/providers/coder/coder/latest/docs/resources/agent).
|
||||
The associated agent will facilitate
|
||||
[workspace connections](../../../user-guides/workspace-access/index.md) via SSH,
|
||||
port forwarding, and IDEs. The agent may also display real-time
|
||||
[workspace metadata](./agent-metadata.md) like resource usage.
|
||||
|
||||
```tf
|
||||
resource "coder_agent" "dev" {
|
||||
os = "linux"
|
||||
arch = "amd64"
|
||||
dir = "/workspace"
|
||||
display_apps {
|
||||
vscode = true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can also leverage [resource metadata](./resource-metadata.md) to display
|
||||
static resource information from your template.
|
||||
|
||||
Templates must include some computational resource to start the agent. All
|
||||
processes on the workspace are then spawned from the agent. It also provides all
|
||||
information displayed in the dashboard's workspace view.
|
||||
|
||||

|
||||
|
||||
Multiple agents may be used in a single template or even a single resource. Each
|
||||
agent may have it's own apps, startup script, and metadata. This can be used to
|
||||
associate multiple containers or VMs with a workspace.
|
||||
|
||||
## Resource persistence
|
||||
|
||||
The resources you define in a template may be _ephemeral_ or _persistent_.
|
||||
Persistent resources stay provisioned when workspaces are stopped, where as
|
||||
ephemeral resources are destroyed and recreated on restart. All resources are
|
||||
destroyed when a workspace is deleted.
|
||||
|
||||
> You can read more about how resource behavior and workspace state in the
|
||||
> [workspace lifecycle documentation](../../../user-guides/workspace-lifecycle.md).
|
||||
|
||||
Template resources follow the
|
||||
[behavior of Terraform resources](https://developer.hashicorp.com/terraform/language/resources/behavior#how-terraform-applies-a-configuration)
|
||||
and can be further configured using the
|
||||
[lifecycle argument](https://developer.hashicorp.com/terraform/language/meta-arguments/lifecycle).
|
||||
|
||||
A common configuration is a template whose only persistent resource is the home
|
||||
directory. This allows the developer to retain their work while ensuring the
|
||||
rest of their environment is consistently up-to-date on each workspace restart.
|
||||
|
||||
When a workspace is deleted, the Coder server essentially runs a
|
||||
[terraform destroy](https://www.terraform.io/cli/commands/destroy) to remove all
|
||||
resources associated with the workspace.
|
||||
|
||||
> Terraform's
|
||||
> [prevent-destroy](https://www.terraform.io/language/meta-arguments/lifecycle#prevent_destroy)
|
||||
> and
|
||||
> [ignore-changes](https://www.terraform.io/language/meta-arguments/lifecycle#ignore_changes)
|
||||
> meta-arguments can be used to prevent accidental data loss.
|
||||
|
||||
## Coder apps
|
||||
|
||||
Additional IDEs, documentation, or services can be associated to your workspace
|
||||
using the
|
||||
[`coder_app`](https://registry.terraform.io/providers/coder/coder/latest/docs/resources/app)
|
||||
resource.
|
||||
|
||||

|
||||
|
||||
Note that some apps are associated to the agent by default as
|
||||
[`display_apps`](https://registry.terraform.io/providers/coder/coder/latest/docs/resources/agent#nested-schema-for-display_apps)
|
||||
and can be hidden directly in the
|
||||
[`coder_agent`](https://registry.terraform.io/providers/coder/coder/latest/docs/resources/agent)
|
||||
resource. You can arrange the display orientation of Coder apps in your template
|
||||
using [resource ordering](./resource-ordering.md).
|
||||
|
||||
Check out our [module registry](https://registry.coder.com/modules) for
|
||||
additional Coder apps from the team and our OSS community.
|
||||
|
||||
<children></children>
|
198
docs/admin/templates/extending-templates/modules.md
Normal file
198
docs/admin/templates/extending-templates/modules.md
Normal file
@ -0,0 +1,198 @@
|
||||
# Reusing template code
|
||||
|
||||
To reuse code across different Coder templates, such as common scripts or
|
||||
resource definitions, we suggest using
|
||||
[Terraform Modules](https://developer.hashicorp.com/terraform/language/modules).
|
||||
|
||||
You can store these modules externally from your Coder deployment, like in a git
|
||||
repository or a Terraform registry. This example shows how to reference a module
|
||||
from your template:
|
||||
|
||||
```tf
|
||||
data "coder_workspace" "me" {}
|
||||
|
||||
module "coder-base" {
|
||||
source = "github.com/my-organization/coder-base"
|
||||
|
||||
# Modules take in variables and can provision infrastructure
|
||||
vpc_name = "devex-3"
|
||||
subnet_tags = { "name": data.coder_workspace.me.name }
|
||||
code_server_version = 4.14.1
|
||||
}
|
||||
|
||||
resource "coder_agent" "dev" {
|
||||
# Modules can provide outputs, such as helper scripts
|
||||
startup_script=<<EOF
|
||||
#!/bin/sh
|
||||
${module.coder-base.code_server_install_command}
|
||||
EOF
|
||||
}
|
||||
```
|
||||
|
||||
Learn more about
|
||||
[creating modules](https://developer.hashicorp.com/terraform/language/modules)
|
||||
and
|
||||
[module sources](https://developer.hashicorp.com/terraform/language/modules/sources)
|
||||
in the Terraform documentation.
|
||||
|
||||
## Coder modules
|
||||
|
||||
Coder publishes plenty of modules that can be used to simplify some common tasks
|
||||
across templates. Some of the modules we publish are,
|
||||
|
||||
1. [`code-server`](https://registry.coder.com/modules/code-server) and
|
||||
[`vscode-web`](https://registry.coder.com/modules/vscode-web)
|
||||
2. [`git-clone`](https://registry.coder.com/modules/git-clone)
|
||||
3. [`dotfiles`](https://registry.coder.com/modules/dotfiles)
|
||||
4. [`jetbrains-gateway`](https://registry.coder.com/modules/jetbrains-gateway)
|
||||
5. [`jfrog-oauth`](https://registry.coder.com/modules/jfrog-oauth) and
|
||||
[`jfrog-token`](https://registry.coder.com/modules/jfrog-token)
|
||||
6. [`vault-github`](https://registry.coder.com/modules/vault-github)
|
||||
|
||||
For a full list of available modules please check
|
||||
[Coder module registry](https://registry.coder.com/modules).
|
||||
|
||||
## Offline installations
|
||||
|
||||
In offline and restricted deploymnets, there are 2 ways to fetch modules.
|
||||
|
||||
1. Artifactory
|
||||
2. Private git repository
|
||||
|
||||
### Artifactory
|
||||
|
||||
Air gapped users can clone the [coder/modules](htpps://github.com/coder/modules)
|
||||
repo and publish a
|
||||
[local terraform module repository](https://jfrog.com/help/r/jfrog-artifactory-documentation/set-up-a-terraform-module/provider-registry)
|
||||
to resolve modules via [Artifactory](https://jfrog.com/artifactory/).
|
||||
|
||||
1. Create a local-terraform-repository with name `coder-modules-local`
|
||||
2. Create a virtual repository with name `tf`
|
||||
3. Follow the below instructions to publish coder modules to Artifactory
|
||||
|
||||
```shell
|
||||
git clone https://github.com/coder/modules
|
||||
cd modules
|
||||
jf tfc
|
||||
jf tf p --namespace="coder" --provider="coder" --tag="1.0.0"
|
||||
```
|
||||
|
||||
4. Generate a token with access to the `tf` repo and set an `ENV` variable
|
||||
`TF_TOKEN_example.jfrog.io="XXXXXXXXXXXXXXX"` on the Coder provisioner.
|
||||
5. Create a file `.terraformrc` with following content and mount at
|
||||
`/home/coder/.terraformrc` within the Coder provisioner.
|
||||
|
||||
```tf
|
||||
provider_installation {
|
||||
direct {
|
||||
exclude = ["registry.terraform.io/*/*"]
|
||||
}
|
||||
network_mirror {
|
||||
url = "https://example.jfrog.io/artifactory/api/terraform/tf/providers/"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
6. Update module source as,
|
||||
|
||||
```tf
|
||||
module "module-name" {
|
||||
source = "https://example.jfrog.io/tf__coder/module-name/coder"
|
||||
version = "1.0.0"
|
||||
agent_id = coder_agent.example.id
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
> Do not forget to replace example.jfrog.io with your Artifactory URL
|
||||
|
||||
Based on the instructions
|
||||
[here](https://jfrog.com/blog/tour-terraform-registries-in-artifactory/).
|
||||
|
||||
#### Example template
|
||||
|
||||
We have an example template
|
||||
[here](https://github.com/coder/coder/blob/main/examples/jfrog/remote/main.tf)
|
||||
that uses our
|
||||
[JFrog Docker](https://github.com/coder/coder/blob/main/examples/jfrog/docker/main.tf)
|
||||
template as the underlying module.
|
||||
|
||||
### Private git repository
|
||||
|
||||
If you are importing a module from a private git repository, the Coder server or
|
||||
[provisioner](../../provisioners.md) needs git credentials. Since this token
|
||||
will only be used for cloning your repositories with modules, it is best to
|
||||
create a token with access limited to the repository and no extra permissions.
|
||||
In GitHub, you can generate a
|
||||
[fine-grained token](https://docs.github.com/en/rest/overview/permissions-required-for-fine-grained-personal-access-tokens?apiVersion=2022-11-28)
|
||||
with read only access to the necessary repos.
|
||||
|
||||
If you are running Coder on a VM, make sure that you have `git` installed and
|
||||
the `coder` user has access to the following files:
|
||||
|
||||
```shell
|
||||
# /home/coder/.gitconfig
|
||||
[credential]
|
||||
helper = store
|
||||
```
|
||||
|
||||
```shell
|
||||
# /home/coder/.git-credentials
|
||||
|
||||
# GitHub example:
|
||||
https://your-github-username:your-github-pat@github.com
|
||||
```
|
||||
|
||||
If you are running Coder on Docker or Kubernetes, `git` is pre-installed in the
|
||||
Coder image. However, you still need to mount credentials. This can be done via
|
||||
a Docker volume mount or Kubernetes secrets.
|
||||
|
||||
#### Passing git credentials in Kubernetes
|
||||
|
||||
First, create a `.gitconfig` and `.git-credentials` file on your local machine.
|
||||
You might want to do this in a temporary directory to avoid conflicting with
|
||||
your own git credentials.
|
||||
|
||||
Next, create the secret in Kubernetes. Be sure to do this in the same namespace
|
||||
that Coder is installed in.
|
||||
|
||||
```shell
|
||||
export NAMESPACE=coder
|
||||
kubectl apply -f - <<EOF
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: git-secrets
|
||||
namespace: $NAMESPACE
|
||||
type: Opaque
|
||||
data:
|
||||
.gitconfig: $(cat .gitconfig | base64 | tr -d '\n')
|
||||
.git-credentials: $(cat .git-credentials | base64 | tr -d '\n')
|
||||
EOF
|
||||
```
|
||||
|
||||
Then, modify Coder's Helm values to mount the secret.
|
||||
|
||||
```yaml
|
||||
coder:
|
||||
volumes:
|
||||
- name: git-secrets
|
||||
secret:
|
||||
secretName: git-secrets
|
||||
volumeMounts:
|
||||
- name: git-secrets
|
||||
mountPath: "/home/coder/.gitconfig"
|
||||
subPath: .gitconfig
|
||||
readOnly: true
|
||||
- name: git-secrets
|
||||
mountPath: "/home/coder/.git-credentials"
|
||||
subPath: .git-credentials
|
||||
readOnly: true
|
||||
```
|
||||
|
||||
### Next steps
|
||||
|
||||
- JFrog's
|
||||
[Terraform Registry support](https://jfrog.com/help/r/jfrog-artifactory-documentation/terraform-registry)
|
||||
- [Configuring the JFrog toolchain inside a workspace](../../integrations/jfrog-artifactory.md)
|
||||
- [Coder Module Registry](https://registry.coder.com/modules)
|
300
docs/admin/templates/extending-templates/parameters.md
Normal file
300
docs/admin/templates/extending-templates/parameters.md
Normal file
@ -0,0 +1,300 @@
|
||||
# Parameters
|
||||
|
||||
A template can prompt the user for additional information when creating
|
||||
workspaces with
|
||||
[_parameters_](https://registry.terraform.io/providers/coder/coder/latest/docs/data-sources/parameter).
|
||||
|
||||

|
||||
|
||||
The user can set parameters in the dashboard UI and CLI.
|
||||
|
||||
You'll likely want to hardcode certain template properties for workspaces, such
|
||||
as security group. But you can let developers specify other properties with
|
||||
parameters like instance size, geographical location, repository URL, etc.
|
||||
|
||||
This example lets a developer choose a Docker host for the workspace:
|
||||
|
||||
```tf
|
||||
data "coder_parameter" "docker_host" {
|
||||
name = "Region"
|
||||
description = "Which region would you like to deploy to?"
|
||||
icon = "/emojis/1f30f.png"
|
||||
type = "string"
|
||||
default = "tcp://100.94.74.63:2375"
|
||||
|
||||
option {
|
||||
name = "Pittsburgh, USA"
|
||||
value = "tcp://100.94.74.63:2375"
|
||||
icon = "/emojis/1f1fa-1f1f8.png"
|
||||
}
|
||||
|
||||
option {
|
||||
name = "Helsinki, Finland"
|
||||
value = "tcp://100.117.102.81:2375"
|
||||
icon = "/emojis/1f1eb-1f1ee.png"
|
||||
}
|
||||
|
||||
option {
|
||||
name = "Sydney, Australia"
|
||||
value = "tcp://100.127.2.1:2375"
|
||||
icon = "/emojis/1f1e6-1f1f9.png"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
From there, a template can refer to a parameter's value:
|
||||
|
||||
```tf
|
||||
provider "docker" {
|
||||
host = data.coder_parameter.docker_host.value
|
||||
}
|
||||
```
|
||||
|
||||
## Types
|
||||
|
||||
A Coder parameter can have one of these types:
|
||||
|
||||
- `string`
|
||||
- `bool`
|
||||
- `number`
|
||||
- `list(string)`
|
||||
|
||||
To specify a default value for a parameter with the `list(string)` type, use a
|
||||
JSON array and the Terraform
|
||||
[jsonencode](https://developer.hashicorp.com/terraform/language/functions/jsonencode)
|
||||
function. For example:
|
||||
|
||||
```tf
|
||||
data "coder_parameter" "security_groups" {
|
||||
name = "Security groups"
|
||||
icon = "/icon/aws.png"
|
||||
type = "list(string)"
|
||||
description = "Select appropriate security groups."
|
||||
mutable = true
|
||||
default = jsonencode([
|
||||
"Web Server Security Group",
|
||||
"Database Security Group",
|
||||
"Backend Security Group"
|
||||
])
|
||||
}
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
A `string` parameter can provide a set of options to limit the user's choices:
|
||||
|
||||
```tf
|
||||
data "coder_parameter" "docker_host" {
|
||||
name = "Region"
|
||||
description = "Which region would you like to deploy to?"
|
||||
type = "string"
|
||||
default = "tcp://100.94.74.63:2375"
|
||||
|
||||
option {
|
||||
name = "Pittsburgh, USA"
|
||||
value = "tcp://100.94.74.63:2375"
|
||||
icon = "/emojis/1f1fa-1f1f8.png"
|
||||
}
|
||||
|
||||
option {
|
||||
name = "Helsinki, Finland"
|
||||
value = "tcp://100.117.102.81:2375"
|
||||
icon = "/emojis/1f1eb-1f1ee.png"
|
||||
}
|
||||
|
||||
option {
|
||||
name = "Sydney, Australia"
|
||||
value = "tcp://100.127.2.1:2375"
|
||||
icon = "/emojis/1f1e6-1f1f9.png"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Incompatibility in Parameter Options for Workspace Builds
|
||||
|
||||
When creating Coder templates, authors have the flexibility to modify parameter
|
||||
options associated with rich parameters. Such modifications can involve adding,
|
||||
substituting, or removing a parameter option. It's important to note that making
|
||||
these changes can lead to discrepancies in parameter values utilized by ongoing
|
||||
workspace builds.
|
||||
|
||||
Consequently, workspace users will be prompted to select the new value from a
|
||||
pop-up window or by using the command-line interface. While this additional
|
||||
interactive step might seem like an interruption, it serves a crucial purpose.
|
||||
It prevents workspace users from becoming trapped with outdated template
|
||||
versions, ensuring they can smoothly update their workspace without any
|
||||
hindrances.
|
||||
|
||||
Example:
|
||||
|
||||
- Bob creates a workspace using the `python-dev` template. This template has a
|
||||
parameter `image_tag`, and Bob selects `1.12`.
|
||||
- Later, the template author Alice is notified of a critical vulnerability in a
|
||||
package installed in the `python-dev` template, which affects the image tag
|
||||
`1.12`.
|
||||
- Alice remediates this vulnerability, and pushes an updated template version
|
||||
that replaces option `1.12` with `1.13` for the `image_tag` parameter. She
|
||||
then notifies all users of that template to update their workspace
|
||||
immediately.
|
||||
- Bob saves their work, and selects the `Update` option in the UI. As their
|
||||
workspace uses the now-invalid option `1.12`, for the `image_tag` parameter,
|
||||
they are prompted to select a new value for `image_tag`.
|
||||
|
||||
## Required and optional parameters
|
||||
|
||||
A parameter is _required_ if it doesn't have the `default` property. The user
|
||||
**must** provide a value to this parameter before creating a workspace:
|
||||
|
||||
```tf
|
||||
data "coder_parameter" "account_name" {
|
||||
name = "Account name"
|
||||
description = "Cloud account name"
|
||||
mutable = true
|
||||
}
|
||||
```
|
||||
|
||||
If a parameter contains the `default` property, Coder will use this value if the
|
||||
user does not specify any:
|
||||
|
||||
```tf
|
||||
data "coder_parameter" "base_image" {
|
||||
name = "Base image"
|
||||
description = "Base machine image to download"
|
||||
default = "ubuntu:latest"
|
||||
}
|
||||
```
|
||||
|
||||
Admins can also set the `default` property to an empty value so that the
|
||||
parameter field can remain empty:
|
||||
|
||||
```tf
|
||||
data "coder_parameter" "dotfiles_url" {
|
||||
name = "dotfiles URL"
|
||||
description = "Git repository with dotfiles"
|
||||
mutable = true
|
||||
default = ""
|
||||
}
|
||||
```
|
||||
|
||||
## Mutability
|
||||
|
||||
Immutable parameters can only be set in these situations:
|
||||
|
||||
- Creating a workspace for the first time.
|
||||
- Updating a workspace to a new template version. This sets the initial value
|
||||
for required parameters.
|
||||
|
||||
The idea is to prevent users from modifying fragile or persistent workspace
|
||||
resources like volumes, regions, and so on.
|
||||
|
||||
Example:
|
||||
|
||||
```tf
|
||||
data "coder_parameter" "region" {
|
||||
name = "Region"
|
||||
description = "Region where the workspace is hosted"
|
||||
mutable = false
|
||||
default = "us-east-1"
|
||||
}
|
||||
```
|
||||
|
||||
You can modify a parameter's `mutable` attribute state anytime. In case of
|
||||
emergency, you can temporarily allow for changing immutable parameters to fix an
|
||||
operational issue, but it is not advised to overuse this opportunity.
|
||||
|
||||
## Ephemeral parameters
|
||||
|
||||
Ephemeral parameters are introduced to users in the form of "build options." Use
|
||||
ephemeral parameters to model specific behaviors in a Coder workspace, such as
|
||||
reverting to a previous image, restoring from a volume snapshot, or building a
|
||||
project without using cache.
|
||||
|
||||
Since these parameters are ephemeral in nature, subsequent builds proceed in the
|
||||
standard manner:
|
||||
|
||||
```tf
|
||||
data "coder_parameter" "force_rebuild" {
|
||||
name = "force_rebuild"
|
||||
type = "bool"
|
||||
description = "Rebuild the Docker image rather than use the cached one."
|
||||
mutable = true
|
||||
default = false
|
||||
ephemeral = true
|
||||
}
|
||||
```
|
||||
|
||||
## Validating parameters
|
||||
|
||||
Coder supports rich parameters with multiple validation modes: min, max,
|
||||
monotonic numbers, and regular expressions.
|
||||
|
||||
### Number
|
||||
|
||||
You can limit a `number` parameter to `min` and `max` boundaries.
|
||||
|
||||
You can also specify its monotonicity as `increasing` or `decreasing` to verify
|
||||
the current and new values. Use the `monotonic` attribute for resources that
|
||||
can't be shrunk or grown without implications, like disk volume size.
|
||||
|
||||
```tf
|
||||
data "coder_parameter" "instances" {
|
||||
name = "Instances"
|
||||
type = "number"
|
||||
description = "Number of compute instances"
|
||||
validation {
|
||||
min = 1
|
||||
max = 8
|
||||
monotonic = "increasing"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
It is possible to override the default `error` message for a `number` parameter,
|
||||
along with its associated `min` and/or `max` properties. The following message
|
||||
placeholders are available `{min}`, `{max}`, and `{value}`.
|
||||
|
||||
```tf
|
||||
data "coder_parameter" "instances" {
|
||||
name = "Instances"
|
||||
type = "number"
|
||||
description = "Number of compute instances"
|
||||
validation {
|
||||
min = 1
|
||||
max = 4
|
||||
error = "Sorry, we can't provision too many instances - maximum limit: {max}, wanted: {value}."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**NOTE:** as of
|
||||
[`terraform-provider-coder` v0.19.0](https://registry.terraform.io/providers/coder/coder/0.19.0/docs),
|
||||
`options` can be specified in `number` parameters; this also works with
|
||||
validations such as `monotonic`.
|
||||
|
||||
### String
|
||||
|
||||
You can validate a `string` parameter to match a regular expression. The `regex`
|
||||
property requires a corresponding `error` property.
|
||||
|
||||
```tf
|
||||
data "coder_parameter" "project_id" {
|
||||
name = "Project ID"
|
||||
description = "Alpha-numeric project ID"
|
||||
validation {
|
||||
regex = "^[a-z0-9]+$"
|
||||
error = "Unfortunately, this isn't a valid project ID"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Create Autofill
|
||||
|
||||
When the template doesn't specify default values, Coder may still autofill
|
||||
parameters.
|
||||
|
||||
1. Coder will look for URL query parameters with form `param.<name>=<value>`.
|
||||
This feature enables platform teams to create pre-filled template creation
|
||||
links.
|
||||
2. Coder will populate recently used parameter key-value pairs for the user.
|
||||
This feature helps reduce repetition when filling common parameters such as
|
||||
`dotfiles_url` or `region`.
|
315
docs/admin/templates/extending-templates/process-logging.md
Normal file
315
docs/admin/templates/extending-templates/process-logging.md
Normal file
@ -0,0 +1,315 @@
|
||||
# Workspace Process Logging
|
||||
|
||||
The workspace process logging feature allows you to log all system-level
|
||||
processes executing in the workspace.
|
||||
|
||||
> **Note:** This feature is only available on Linux in Kubernetes. There are
|
||||
> additional requirements outlined further in this document.
|
||||
|
||||
Workspace process logging adds a sidecar container to workspace pods that will
|
||||
log all processes started in the workspace container (e.g., commands executed in
|
||||
the terminal or processes created in the background by other processes).
|
||||
Processes launched inside containers or nested containers within the workspace
|
||||
are also logged. You can view the output from the sidecar or send it to a
|
||||
monitoring stack, such as CloudWatch, for further analysis or long-term storage.
|
||||
|
||||
Please note that these logs are not recorded or captured by the Coder
|
||||
organization in any way, shape, or form.
|
||||
|
||||
> This is an [Premium or Enterprise](https://coder.com/pricing) feature. To
|
||||
> learn more about Coder Enterprise, please
|
||||
> [contact sales](https://coder.com/contact).
|
||||
|
||||
## How this works
|
||||
|
||||
Coder uses [eBPF](https://ebpf.io/) (which we chose for its minimal performance
|
||||
impact) to perform in-kernel logging and filtering of all exec system calls
|
||||
originating from the workspace container.
|
||||
|
||||
The core of this feature is also open source and can be found in the
|
||||
[exectrace](https://github.com/coder/exectrace) GitHub repo. The enterprise
|
||||
component (in the `enterprise/` directory of the repo) is responsible for
|
||||
starting the eBPF program with the correct filtering options for the specific
|
||||
workspace.
|
||||
|
||||
## Requirements
|
||||
|
||||
The host machine must be running a Linux kernel >= 5.8 with the kernel config
|
||||
`CONFIG_DEBUG_INFO_BTF=y` enabled.
|
||||
|
||||
To check your kernel version, run:
|
||||
|
||||
```shell
|
||||
uname -r
|
||||
```
|
||||
|
||||
To validate the required kernel config is enabled, run either of the following
|
||||
commands on your nodes directly (_not_ from a workspace terminal):
|
||||
|
||||
```shell
|
||||
cat /proc/config.gz | gunzip | grep CONFIG_DEBUG_INFO_BTF
|
||||
```
|
||||
|
||||
```shell
|
||||
cat "/boot/config-$(uname -r)" | grep CONFIG_DEBUG_INFO_BTF
|
||||
```
|
||||
|
||||
If these requirements are not met, workspaces will fail to start for security
|
||||
reasons.
|
||||
|
||||
Your template must be a Kubernetes template. Workspace process logging is not
|
||||
compatible with the `sysbox-runc` runtime due to technical limitations, but it
|
||||
is compatible with our `envbox` template family.
|
||||
|
||||
## Example templates
|
||||
|
||||
We provide working example templates for Kubernetes, and Kubernetes with
|
||||
`envbox` (for [Docker support in workspaces](./docker-in-workspaces.md)). You
|
||||
can view these templates in the
|
||||
[exectrace repo](https://github.com/coder/exectrace/tree/main/enterprise/templates).
|
||||
|
||||
## Configuring custom templates to use workspace process logging
|
||||
|
||||
If you have an existing Kubernetes or Kubernetes with `envbox` template that you
|
||||
would like to add workspace process logging to, follow these steps:
|
||||
|
||||
1. Ensure the image used in your template has `curl` installed.
|
||||
|
||||
1. Add the following section to your template's `main.tf` file:
|
||||
|
||||
<!--
|
||||
If you are updating this section, please also update the example templates
|
||||
in the exectrace repo.
|
||||
-->
|
||||
|
||||
```hcl
|
||||
locals {
|
||||
# This is the init script for the main workspace container that runs before the
|
||||
# agent starts to configure workspace process logging.
|
||||
exectrace_init_script = <<EOT
|
||||
set -eu
|
||||
pidns_inum=$(readlink /proc/self/ns/pid | sed 's/[^0-9]//g')
|
||||
if [ -z "$pidns_inum" ]; then
|
||||
echo "Could not determine process ID namespace inum"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Before we start the script, does curl exist?
|
||||
if ! command -v curl >/dev/null 2>&1; then
|
||||
echo "curl is required to download the Coder binary"
|
||||
echo "Please install curl to your image and try again"
|
||||
# 127 is command not found.
|
||||
exit 127
|
||||
fi
|
||||
|
||||
echo "Sending process ID namespace inum to exectrace sidecar"
|
||||
rc=0
|
||||
max_retry=5
|
||||
counter=0
|
||||
until [ $counter -ge $max_retry ]; do
|
||||
set +e
|
||||
curl \
|
||||
--fail \
|
||||
--silent \
|
||||
--connect-timeout 5 \
|
||||
-X POST \
|
||||
-H "Content-Type: text/plain" \
|
||||
--data "$pidns_inum" \
|
||||
http://127.0.0.1:56123
|
||||
rc=$?
|
||||
set -e
|
||||
if [ $rc -eq 0 ]; then
|
||||
break
|
||||
fi
|
||||
|
||||
counter=$((counter+1))
|
||||
echo "Curl failed with exit code $${rc}, attempt $${counter}/$${max_retry}; Retrying in 3 seconds..."
|
||||
sleep 3
|
||||
done
|
||||
if [ $rc -ne 0 ]; then
|
||||
echo "Failed to send process ID namespace inum to exectrace sidecar"
|
||||
exit $rc
|
||||
fi
|
||||
|
||||
EOT
|
||||
}
|
||||
```
|
||||
|
||||
1. Update the `command` of your workspace container like the following:
|
||||
|
||||
<!--
|
||||
If you are updating this section, please also update the example templates
|
||||
in the exectrace repo.
|
||||
-->
|
||||
|
||||
```hcl
|
||||
resource "kubernetes_pod" "main" {
|
||||
...
|
||||
spec {
|
||||
...
|
||||
container {
|
||||
...
|
||||
// NOTE: this command is changed compared to the upstream kubernetes
|
||||
// template
|
||||
command = [
|
||||
"sh",
|
||||
"-c",
|
||||
"${local.exectrace_init_script}\n\n${coder_agent.main.init_script}",
|
||||
]
|
||||
...
|
||||
}
|
||||
...
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
> **Note:** If you are using the `envbox` template, you will need to update
|
||||
> the third argument to be
|
||||
> `"${local.exectrace_init_script}\n\nexec /envbox docker"` instead.
|
||||
|
||||
1. Add the following container to your workspace pod spec.
|
||||
|
||||
<!--
|
||||
If you are updating this section, please also update the example templates
|
||||
in the exectrace repo.
|
||||
-->
|
||||
|
||||
```hcl
|
||||
resource "kubernetes_pod" "main" {
|
||||
...
|
||||
spec {
|
||||
...
|
||||
// NOTE: this container is added compared to the upstream kubernetes
|
||||
// template
|
||||
container {
|
||||
name = "exectrace"
|
||||
image = "ghcr.io/coder/exectrace:latest"
|
||||
image_pull_policy = "Always"
|
||||
command = [
|
||||
"/opt/exectrace",
|
||||
"--init-address", "127.0.0.1:56123",
|
||||
"--label", "workspace_id=${data.coder_workspace.me.id}",
|
||||
"--label", "workspace_name=${data.coder_workspace.me.name}",
|
||||
"--label", "user_id=${data.coder_workspace_owner.me.id}",
|
||||
"--label", "username=${data.coder_workspace_owner.me.name}",
|
||||
"--label", "user_email=${data.coder_workspace_owner.me.email}",
|
||||
]
|
||||
security_context {
|
||||
// exectrace must be started as root so it can attach probes into the
|
||||
// kernel to record process events with high throughput.
|
||||
run_as_user = "0"
|
||||
run_as_group = "0"
|
||||
// exectrace requires a privileged container so it can control mounts
|
||||
// and perform privileged syscalls against the host kernel to attach
|
||||
// probes.
|
||||
privileged = true
|
||||
}
|
||||
}
|
||||
...
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
> **Note:** `exectrace` requires root privileges and a privileged container
|
||||
> to attach probes to the kernel. This is a requirement of eBPF.
|
||||
|
||||
1. Add the following environment variable to your workspace pod:
|
||||
|
||||
<!--
|
||||
If you are updating this section, please also update the example templates
|
||||
in the exectrace repo.
|
||||
-->
|
||||
|
||||
```hcl
|
||||
resource "kubernetes_pod" "main" {
|
||||
...
|
||||
spec {
|
||||
...
|
||||
env {
|
||||
name = "CODER_AGENT_SUBSYSTEM"
|
||||
value = "exectrace"
|
||||
}
|
||||
...
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Once you have made these changes, you can push a new version of your template
|
||||
and workspace process logging will be enabled for all workspaces once they are
|
||||
restarted.
|
||||
|
||||
## Viewing workspace process logs
|
||||
|
||||
To view the process logs for a specific workspace you can use `kubectl` to print
|
||||
the logs:
|
||||
|
||||
```bash
|
||||
kubectl logs pod-name --container exectrace
|
||||
```
|
||||
|
||||
The raw logs will look something like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"ts": "2022-02-28T20:29:38.038452202Z",
|
||||
"level": "INFO",
|
||||
"msg": "exec",
|
||||
"fields": {
|
||||
"labels": {
|
||||
"user_email": "jessie@coder.com",
|
||||
"user_id": "5e876e9a-121663f01ebd1522060d5270",
|
||||
"username": "jessie",
|
||||
"workspace_id": "621d2e52-a6987ef6c56210058ee2593c",
|
||||
"workspace_name": "main"
|
||||
},
|
||||
"cmdline": "uname -a",
|
||||
"event": {
|
||||
"filename": "/usr/bin/uname",
|
||||
"argv": ["uname", "-a"],
|
||||
"truncated": false,
|
||||
"pid": 920684,
|
||||
"uid": 101000,
|
||||
"gid": 101000,
|
||||
"comm": "bash"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### View logs in AWS EKS
|
||||
|
||||
If you're using AWS' Elastic Kubernetes Service, you can
|
||||
[configure your cluster](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Container-Insights-EKS-logs.html)
|
||||
to send logs to CloudWatch. This allows you to view the logs for a specific user
|
||||
or workspace.
|
||||
|
||||
To view your logs, go to the CloudWatch dashboard (which is available on the
|
||||
**Log Insights** tab) and run a query similar to the following:
|
||||
|
||||
```text
|
||||
fields @timestamp, log_processed.fields.cmdline
|
||||
| sort @timestamp asc
|
||||
| filter kubernetes.container_name="exectrace"
|
||||
| filter log_processed.fields.labels.username="zac"
|
||||
| filter log_processed.fields.labels.workspace_name="code"
|
||||
```
|
||||
|
||||
## Usage considerations
|
||||
|
||||
- The sidecar attached to each workspace is a privileged container, so you may
|
||||
need to review your organization's security policies before enabling this
|
||||
feature. Enabling workspace process logging does _not_ grant extra privileges
|
||||
to the workspace container itself, however.
|
||||
- `exectrace` will log processes from nested Docker containers (including deeply
|
||||
nested containers) correctly, but Coder does not distinguish between processes
|
||||
started in the workspace and processes started in a child container in the
|
||||
logs.
|
||||
- With `envbox` workspaces, this feature will detect and log startup processes
|
||||
begun in the outer container (including container initialization processes).
|
||||
- Because this feature logs **all** processes in the workspace, high levels of
|
||||
usage (e.g., during a `make` run) will result in an abundance of output in the
|
||||
sidecar container. Depending on how your Kubernetes cluster is configured, you
|
||||
may incur extra charges from your cloud provider to store the additional logs.
|
@ -0,0 +1,48 @@
|
||||
# Provider Authentication
|
||||
|
||||
<blockquote class="danger">
|
||||
<p>
|
||||
Do not store secrets in templates. Assume every user has cleartext access
|
||||
to every template.
|
||||
</p>
|
||||
</blockquote>
|
||||
|
||||
The Coder server's
|
||||
[provisioner](https://registry.terraform.io/providers/coder/coder/latest/docs/data-sources/provisioner)
|
||||
process needs to authenticate with other provider APIs to provision workspaces.
|
||||
There are two approaches to do this:
|
||||
|
||||
- Pass credentials to the provisioner as parameters.
|
||||
- Preferred: Execute the Coder server in an environment that is authenticated
|
||||
with the provider.
|
||||
|
||||
We encourage the latter approach where supported:
|
||||
|
||||
- Simplifies the template.
|
||||
- Keeps provider credentials out of Coder's database, making it a less valuable
|
||||
target for attackers.
|
||||
- Compatible with agent-based authentication schemes, which handle credential
|
||||
rotation or ensure the credentials are not written to disk.
|
||||
|
||||
Generally, you can set up an environment to provide credentials to Coder in
|
||||
these ways:
|
||||
|
||||
- A well-known location on disk. For example, `~/.aws/credentials` for AWS on
|
||||
POSIX systems.
|
||||
- Environment variables.
|
||||
|
||||
It is usually sufficient to authenticate using the CLI or SDK for the provider
|
||||
before running Coder, but check the Terraform provider's documentation for
|
||||
details.
|
||||
|
||||
These platforms have Terraform providers that support authenticated
|
||||
environments:
|
||||
|
||||
- [Google Cloud](https://registry.terraform.io/providers/hashicorp/google/latest/docs)
|
||||
- [Amazon Web Services](https://registry.terraform.io/providers/hashicorp/aws/latest/docs)
|
||||
- [Microsoft Azure](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs)
|
||||
- [Kubernetes](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs)
|
||||
|
||||
Other providers might also support authenticated environments. Check the
|
||||
[documentation of the Terraform provider](https://registry.terraform.io/browse/providers)
|
||||
for details.
|
111
docs/admin/templates/extending-templates/resource-metadata.md
Normal file
111
docs/admin/templates/extending-templates/resource-metadata.md
Normal file
@ -0,0 +1,111 @@
|
||||
# Resource Metadata
|
||||
|
||||
Expose key workspace information to your users with
|
||||
[`coder_metadata`](https://registry.terraform.io/providers/coder/coder/latest/docs/resources/metadata)
|
||||
resources in your template code.
|
||||
|
||||
You can use `coder_metadata` to show Terraform resource attributes like these:
|
||||
|
||||
- Compute resources
|
||||
- IP addresses
|
||||
- [Secrets](../../security/secrets.md#displaying-secrets)
|
||||
- Important file paths
|
||||
|
||||

|
||||
|
||||
<blockquote class="info">
|
||||
Coder automatically generates the <code>type</code> metadata.
|
||||
</blockquote>
|
||||
|
||||
You can also present automatically updating, dynamic values with
|
||||
[agent metadata](./agent-metadata.md).
|
||||
|
||||
## Example
|
||||
|
||||
Expose the disk size, deployment name, and persistent directory in a Kubernetes
|
||||
template with:
|
||||
|
||||
```tf
|
||||
resource "kubernetes_persistent_volume_claim" "root" {
|
||||
...
|
||||
}
|
||||
|
||||
resource "kubernetes_deployment" "coder" {
|
||||
# My deployment is ephemeral
|
||||
count = data.coder_workspace.me.start_count
|
||||
...
|
||||
}
|
||||
|
||||
resource "coder_metadata" "pvc" {
|
||||
resource_id = kubernetes_persistent_volume_claim.root.id
|
||||
item {
|
||||
key = "size"
|
||||
value = kubernetes_persistent_volume_claim.root.spec[0].resources[0].requests.storage
|
||||
}
|
||||
item {
|
||||
key = "dir"
|
||||
value = "/home/coder"
|
||||
}
|
||||
}
|
||||
|
||||
resource "coder_metadata" "deployment" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
resource_id = kubernetes_deployment.coder[0].id
|
||||
item {
|
||||
key = "name"
|
||||
value = kubernetes_deployment.coder[0].metadata[0].name
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Hiding resources in the dashboard
|
||||
|
||||
Some resources don't need to be exposed in the dashboard's UI. This helps keep
|
||||
the workspace view clean for developers. To hide a resource, use the `hide`
|
||||
attribute:
|
||||
|
||||
```tf
|
||||
resource "coder_metadata" "hide_serviceaccount" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
resource_id = kubernetes_service_account.user_data.id
|
||||
hide = true
|
||||
item {
|
||||
key = "name"
|
||||
value = kubernetes_deployment.coder[0].metadata[0].name
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Using a custom resource icon
|
||||
|
||||
To use custom icons for your resource metadata, use the `icon` attribute. It
|
||||
must be a valid path or URL.
|
||||
|
||||
```tf
|
||||
resource "coder_metadata" "resource_with_icon" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
resource_id = kubernetes_service_account.user_data.id
|
||||
icon = "/icon/database.svg"
|
||||
item {
|
||||
key = "name"
|
||||
value = kubernetes_deployment.coder[0].metadata[0].name
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To make it easier for you to customize your resource we added some built-in
|
||||
icons:
|
||||
|
||||
- Folder `/icon/folder.svg`
|
||||
- Memory `/icon/memory.svg`
|
||||
- Image `/icon/image.svg`
|
||||
- Widgets `/icon/widgets.svg`
|
||||
- Database `/icon/database.svg`
|
||||
|
||||
We also have other icons related to the IDEs. You can see more information on
|
||||
how to use the builtin icons [here](./icons.md).
|
||||
|
||||
## Up next
|
||||
|
||||
- [Secrets](../../security/secrets.md)
|
||||
- [Agent metadata](./agent-metadata.md)
|
183
docs/admin/templates/extending-templates/resource-ordering.md
Normal file
183
docs/admin/templates/extending-templates/resource-ordering.md
Normal file
@ -0,0 +1,183 @@
|
||||
# UI Resource Ordering
|
||||
|
||||
In Coder templates, managing the order of UI elements is crucial for a seamless
|
||||
user experience. This page outlines how resources can be aligned using the
|
||||
`order` Terraform property or inherit the natural order from the file.
|
||||
|
||||
The resource with the lower `order` is presented before the one with greater
|
||||
value. A missing `order` property defaults to 0. If two resources have the same
|
||||
`order` property, the resources will be ordered by property `name` (or `key`).
|
||||
|
||||
## Using "order" property
|
||||
|
||||
### Coder parameters
|
||||
|
||||
The `order` property of `coder_parameter` resource allows specifying the order
|
||||
of parameters in UI forms. In the below example, `project_id` will appear
|
||||
_before_ `account_id`:
|
||||
|
||||
```tf
|
||||
data "coder_parameter" "project_id" {
|
||||
name = "project_id"
|
||||
display_name = "Project ID"
|
||||
description = "Specify cloud provider project ID."
|
||||
order = 2
|
||||
}
|
||||
|
||||
data "coder_parameter" "account_id" {
|
||||
name = "account_id"
|
||||
display_name = "Account ID"
|
||||
description = "Specify cloud provider account ID."
|
||||
order = 1
|
||||
}
|
||||
```
|
||||
|
||||
### Agents
|
||||
|
||||
Agent resources within the UI left pane are sorted based on the `order`
|
||||
property, followed by `name`, ensuring a consistent and intuitive arrangement.
|
||||
|
||||
```tf
|
||||
resource "coder_agent" "primary" {
|
||||
...
|
||||
|
||||
order = 1
|
||||
}
|
||||
|
||||
resource "coder_agent" "secondary" {
|
||||
...
|
||||
|
||||
order = 2
|
||||
}
|
||||
```
|
||||
|
||||
The agent with the lowest order is presented at the top in the workspace view.
|
||||
|
||||
### Agent metadata
|
||||
|
||||
The `coder_agent` exposes metadata to present operational metrics in the UI.
|
||||
Metrics defined with Terraform `metadata` blocks can be ordered using additional
|
||||
`order` property; otherwise, they are sorted by `key`.
|
||||
|
||||
```tf
|
||||
resource "coder_agent" "main" {
|
||||
...
|
||||
|
||||
metadata {
|
||||
display_name = "CPU Usage"
|
||||
key = "cpu_usage"
|
||||
script = "coder stat cpu"
|
||||
interval = 10
|
||||
timeout = 1
|
||||
order = 1
|
||||
}
|
||||
metadata {
|
||||
display_name = "CPU Usage (Host)"
|
||||
key = "cpu_usage_host"
|
||||
script = "coder stat cpu --host"
|
||||
interval = 10
|
||||
timeout = 1
|
||||
order = 2
|
||||
}
|
||||
metadata {
|
||||
display_name = "RAM Usage"
|
||||
key = "ram_usage"
|
||||
script = "coder stat mem"
|
||||
interval = 10
|
||||
timeout = 1
|
||||
order = 1
|
||||
}
|
||||
metadata {
|
||||
display_name = "RAM Usage (Host)"
|
||||
key = "ram_usage_host"
|
||||
script = "coder stat mem --host"
|
||||
interval = 10
|
||||
timeout = 1
|
||||
order = 2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Applications
|
||||
|
||||
Similarly to Coder agents, `coder_app` resources incorporate the `order`
|
||||
property to organize button apps in the app bar within a `coder_agent` in the
|
||||
workspace view.
|
||||
|
||||
Only template defined applications can be arranged. _VS Code_ or _Terminal_
|
||||
buttons are static.
|
||||
|
||||
```tf
|
||||
resource "coder_app" "code-server" {
|
||||
agent_id = coder_agent.main.id
|
||||
slug = "code-server"
|
||||
display_name = "code-server"
|
||||
...
|
||||
|
||||
order = 2
|
||||
}
|
||||
|
||||
resource "coder_app" "filebrowser" {
|
||||
agent_id = coder_agent.main.id
|
||||
display_name = "File Browser"
|
||||
slug = "filebrowser"
|
||||
...
|
||||
|
||||
order = 1
|
||||
}
|
||||
```
|
||||
|
||||
## Inherit order from file
|
||||
|
||||
### Coder parameter options
|
||||
|
||||
The options for Coder parameters maintain the same order as in the file
|
||||
structure. This simplifies management and ensures consistency between
|
||||
configuration files and UI presentation.
|
||||
|
||||
```tf
|
||||
data "coder_parameter" "database_region" {
|
||||
name = "database_region"
|
||||
display_name = "Database Region"
|
||||
|
||||
icon = "/icon/database.svg"
|
||||
description = "These are options."
|
||||
mutable = true
|
||||
default = "us-east1-a"
|
||||
|
||||
// The order of options is stable and inherited from .tf file.
|
||||
option {
|
||||
name = "US Central"
|
||||
description = "Select for central!"
|
||||
value = "us-central1-a"
|
||||
}
|
||||
option {
|
||||
name = "US East"
|
||||
description = "Select for east!"
|
||||
value = "us-east1-a"
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### Coder metadata items
|
||||
|
||||
In cases where multiple item properties exist, the order is inherited from the
|
||||
file, facilitating seamless integration between a Coder template and UI
|
||||
presentation.
|
||||
|
||||
```tf
|
||||
resource "coder_metadata" "attached_volumes" {
|
||||
resource_id = docker_image.main.id
|
||||
|
||||
// Items will be presented in the UI in the following order.
|
||||
item {
|
||||
key = "disk-a"
|
||||
value = "60 GiB"
|
||||
}
|
||||
item {
|
||||
key = "disk-b"
|
||||
value = "128 GiB"
|
||||
}
|
||||
}
|
||||
```
|
@ -0,0 +1,93 @@
|
||||
# Resource persistence
|
||||
|
||||
By default, all Coder resources are persistent, but production templates
|
||||
**must** use the practices laid out in this document to prevent accidental
|
||||
deletion.
|
||||
|
||||
Coder templates have full control over workspace ephemerality. In a completely
|
||||
ephemeral workspace, there are zero resources in the Off state. In a completely
|
||||
persistent workspace, there is no difference between the Off and On states.
|
||||
|
||||
The needs of most workspaces fall somewhere in the middle, persisting user data
|
||||
like filesystem volumes, but deleting expensive, reproducible resources such as
|
||||
compute instances.
|
||||
|
||||
## Disabling persistence
|
||||
|
||||
The Terraform
|
||||
[`coder_workspace` data source](https://registry.terraform.io/providers/coder/coder/latest/docs/data-sources/workspace)
|
||||
exposes the `start_count = [0 | 1]` attribute. To make a resource ephemeral, you
|
||||
can assign the `start_count` attribute to resource's
|
||||
[`count`](https://developer.hashicorp.com/terraform/language/meta-arguments/count)
|
||||
meta-argument.
|
||||
|
||||
In this example, Coder will provision or tear down the `docker_container`
|
||||
resource:
|
||||
|
||||
```tf
|
||||
data "coder_workspace" "me" {
|
||||
}
|
||||
|
||||
resource "docker_container" "workspace" {
|
||||
# When `start_count` is 0, `count` is 0, so no `docker_container` is created.
|
||||
count = data.coder_workspace.me.start_count # 0 (stopped), 1 (started)
|
||||
# ... other config
|
||||
}
|
||||
```
|
||||
|
||||
## ⚠️ Persistence pitfalls
|
||||
|
||||
Take this example resource:
|
||||
|
||||
```tf
|
||||
data "coder_workspace" "me" {
|
||||
}
|
||||
|
||||
resource "docker_volume" "home_volume" {
|
||||
name = "coder-${data.coder_workspace.me.owner}-home"
|
||||
}
|
||||
```
|
||||
|
||||
Because we depend on `coder_workspace.me.owner`, if the owner changes their
|
||||
username, Terraform will recreate the volume (wiping its data!) the next time
|
||||
that Coder starts the workspace.
|
||||
|
||||
To prevent this, use immutable IDs:
|
||||
|
||||
- `coder_workspace.me.owner_id`
|
||||
- `coder_workspace.me.id`
|
||||
|
||||
```tf
|
||||
data "coder_workspace" "me" {
|
||||
}
|
||||
|
||||
resource "docker_volume" "home_volume" {
|
||||
# This volume will survive until the Workspace is deleted or the template
|
||||
# admin changes this resource block.
|
||||
name = "coder-${data.coder_workspace.id}-home"
|
||||
}
|
||||
```
|
||||
|
||||
## 🛡 Bulletproofing
|
||||
|
||||
Even if your persistent resource depends exclusively on immutable IDs, a change
|
||||
to the `name` format or other attributes would cause Terraform to rebuild the
|
||||
resource.
|
||||
|
||||
You can prevent Terraform from recreating a resource under any circumstance by
|
||||
setting the
|
||||
[`ignore_changes = all` directive in the `lifecycle` block](https://developer.hashicorp.com/terraform/language/meta-arguments/lifecycle#ignore_changes).
|
||||
|
||||
```tf
|
||||
data "coder_workspace" "me" {
|
||||
}
|
||||
|
||||
resource "docker_volume" "home_volume" {
|
||||
# This resource will survive until either the entire block is deleted
|
||||
# or the workspace is.
|
||||
name = "coder-${data.coder_workspace.me.id}-home"
|
||||
lifecycle {
|
||||
ignore_changes = all
|
||||
}
|
||||
}
|
||||
```
|
126
docs/admin/templates/extending-templates/variables.md
Normal file
126
docs/admin/templates/extending-templates/variables.md
Normal file
@ -0,0 +1,126 @@
|
||||
# Terraform template-wide variables
|
||||
|
||||
In Coder, Terraform templates offer extensive flexibility through template-wide
|
||||
variables. These variables, managed by template authors, facilitate the
|
||||
construction of customizable templates. Unlike parameters, which are primarily
|
||||
for workspace customization, template variables remain under the control of the
|
||||
template author, ensuring workspace users cannot modify them.
|
||||
|
||||
```tf
|
||||
variable "CLOUD_API_KEY" {
|
||||
type = string
|
||||
description = "API key for the service"
|
||||
default = "1234567890"
|
||||
sensitive = true
|
||||
}
|
||||
```
|
||||
|
||||
Given that variables are a
|
||||
[fundamental concept in Terraform](https://developer.hashicorp.com/terraform/language/values/variables),
|
||||
Coder endeavors to fully support them. Native support includes `string`,
|
||||
`number`, and `bool` formats. However, other types such as `list(string)` or
|
||||
`map(any)` will default to being treated as strings.
|
||||
|
||||
## Default value
|
||||
|
||||
Upon adding a template variable, it's mandatory to provide a value during the
|
||||
first push. At this stage, the template administrator faces two choices:
|
||||
|
||||
1. _No `default` property_: opt not to define a default property. Instead,
|
||||
utilize the `--var name=value` command-line argument during the push to
|
||||
supply the variable's value.
|
||||
2. _Define `default` property_: set a default property for the template
|
||||
variable. If the administrator doesn't input a value via CLI, Coder
|
||||
automatically uses this default during the push.
|
||||
|
||||
After the initial push, variables are stored in the database table, associated
|
||||
with the specific template version. They can be conveniently managed via
|
||||
_Template Settings_ without requiring an extra push.
|
||||
|
||||
### Resolved values vs. default values
|
||||
|
||||
It's crucial to note that Coder templates operate based on resolved values
|
||||
during a push, rather than default values. This ensures that default values do
|
||||
not inadvertently override the configured variable settings during the push
|
||||
process.
|
||||
|
||||
This approach caters to users who prefer to avoid accidental overrides of their
|
||||
variable settings with default values during pushes, thereby enhancing control
|
||||
and predictability.
|
||||
|
||||
If you encounter a situation where you need to override template settings for
|
||||
variables, you can employ a straightforward solution:
|
||||
|
||||
1. Create a `terraform.tfvars` file in in the template directory:
|
||||
|
||||
```tf
|
||||
coder_image = newimage:tag
|
||||
```
|
||||
|
||||
2. Push the new template revision using Coder CLI:
|
||||
|
||||
```
|
||||
coder templates push my-template -y # no need to use --var
|
||||
```
|
||||
|
||||
This file serves as a mechanism to override the template settings for variables.
|
||||
It can be stored in the repository for easy access and reference. Coder CLI
|
||||
automatically detects it and loads variable values.
|
||||
|
||||
## Input options
|
||||
|
||||
When working with Terraform configurations in Coder, you have several options
|
||||
for providing values to variables using the Coder CLI:
|
||||
|
||||
1. _Manual input in CLI_: You can manually input values for Terraform variables
|
||||
directly in the CLI during the deployment process.
|
||||
2. _Command-line argument_: Utilize the `--var name=value` command-line argument
|
||||
to specify variable values inline as key-value pairs.
|
||||
3. _Variables file selection_: Alternatively, you can use a variables file
|
||||
selected via the `--variables-file values.yml` command-line argument. This
|
||||
approach is particularly useful when dealing with multiple variables or to
|
||||
avoid manual input of numerous values. Variables files can be versioned for
|
||||
better traceability and management, and it enhances reproducibility.
|
||||
|
||||
Here's an example of a YAML-formatted variables file, `values.yml`:
|
||||
|
||||
```yaml
|
||||
region: us-east-1
|
||||
bucket_name: magic
|
||||
zone_types: '{"us-east-1":"US East", "eu-west-1": "EU West"}'
|
||||
cpu: 1
|
||||
```
|
||||
|
||||
In this sample file:
|
||||
|
||||
- `region`, `bucket_name`, `zone_types`, and `cpu` are Terraform variable names.
|
||||
- Corresponding values are provided for each variable.
|
||||
- The `zone_types` variable demonstrates how to provide a JSON-formatted string
|
||||
as a value in YAML.
|
||||
|
||||
## Terraform .tfvars files
|
||||
|
||||
In Terraform, `.tfvars` files provide a convenient means to define variable
|
||||
values for a project in a reusable manner. These files, ending with either
|
||||
`.tfvars` or `.tfvars.json`, streamline the process of setting numerous
|
||||
variables.
|
||||
|
||||
By utilizing `.tfvars` files, you can efficiently manage and organize variable
|
||||
values for your Terraform projects. This approach offers several advantages:
|
||||
|
||||
- Clarity and consistency: Centralize variable definitions in dedicated files,
|
||||
enhancing clarity, instead of input values on template push.
|
||||
- Ease of maintenance: Modify variable values in a single location under version
|
||||
control, simplifying maintenance and updates.
|
||||
|
||||
Coder automatically loads variable definition files following a specific order,
|
||||
providing flexibility and control over variable configuration. The loading
|
||||
sequence is as follows:
|
||||
|
||||
1. `terraform.tfvars`: This file contains variable values and is loaded first.
|
||||
2. `terraform.tfvars.json`: If present, this JSON-formatted file is loaded after
|
||||
`terraform.tfvars`.
|
||||
3. `\*.auto.tfvars`: Files matching this pattern are loaded next, ordered
|
||||
alphabetically.
|
||||
4. `\*.auto.tfvars.json`: JSON-formatted files matching this pattern are loaded
|
||||
last.
|
376
docs/admin/templates/extending-templates/web-ides.md
Normal file
376
docs/admin/templates/extending-templates/web-ides.md
Normal file
@ -0,0 +1,376 @@
|
||||
# Web IDEs
|
||||
|
||||
In Coder, web IDEs are defined as
|
||||
[coder_app](https://registry.terraform.io/providers/coder/coder/latest/docs/resources/app)
|
||||
resources in the template. With our generic model, any web application can be
|
||||
used as a Coder application. For example:
|
||||
|
||||
```tf
|
||||
# Add button to open Portainer in the workspace dashboard
|
||||
# Note: Portainer must be already running in the workspace
|
||||
resource "coder_app" "portainer" {
|
||||
agent_id = coder_agent.main.id
|
||||
slug = "portainer"
|
||||
display_name = "Portainer"
|
||||
icon = "https://simpleicons.org/icons/portainer.svg"
|
||||
url = "https://localhost:9443/api/status"
|
||||
|
||||
healthcheck {
|
||||
url = "https://localhost:9443/api/status"
|
||||
interval = 6
|
||||
threshold = 10
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## code-server
|
||||
|
||||
[code-server](https://github.com/coder/coder) is our supported method of running
|
||||
VS Code in the web browser. A simple way to install code-server in Linux/macOS
|
||||
workspaces is via the Coder agent in your template:
|
||||
|
||||
```console
|
||||
# edit your template
|
||||
cd your-template/
|
||||
vim main.tf
|
||||
```
|
||||
|
||||
```tf
|
||||
resource "coder_agent" "main" {
|
||||
arch = "amd64"
|
||||
os = "linux"
|
||||
startup_script = <<EOF
|
||||
#!/bin/sh
|
||||
# install code-server
|
||||
# add '-s -- --version x.x.x' to install a specific code-server version
|
||||
curl -fsSL https://code-server.dev/install.sh | sh -s -- --method=standalone --prefix=/tmp/code-server
|
||||
|
||||
# start code-server on a specific port
|
||||
# authn is off since the user already authn-ed into the coder deployment
|
||||
# & is used to run the process in the background
|
||||
/tmp/code-server/bin/code-server --auth none --port 13337 &
|
||||
EOF
|
||||
}
|
||||
```
|
||||
|
||||
For advanced use, we recommend installing code-server in your VM snapshot or
|
||||
container image. Here's a Dockerfile which leverages some special
|
||||
[code-server features](https://coder.com/docs/code-server/):
|
||||
|
||||
```Dockerfile
|
||||
FROM codercom/enterprise-base:ubuntu
|
||||
|
||||
# install the latest version
|
||||
USER root
|
||||
RUN curl -fsSL https://code-server.dev/install.sh | sh
|
||||
USER coder
|
||||
|
||||
# pre-install VS Code extensions
|
||||
RUN code-server --install-extension eamodio.gitlens
|
||||
|
||||
# directly start code-server with the agent's startup_script (see above),
|
||||
# or use a process manager like supervisord
|
||||
```
|
||||
|
||||
You'll also need to specify a `coder_app` resource related to the agent. This is
|
||||
how code-server is displayed on the workspace page.
|
||||
|
||||
```tf
|
||||
resource "coder_app" "code-server" {
|
||||
agent_id = coder_agent.main.id
|
||||
slug = "code-server"
|
||||
display_name = "code-server"
|
||||
url = "http://localhost:13337/?folder=/home/coder"
|
||||
icon = "/icon/code.svg"
|
||||
subdomain = false
|
||||
|
||||
healthcheck {
|
||||
url = "http://localhost:13337/healthz"
|
||||
interval = 2
|
||||
threshold = 10
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
## VS Code Web
|
||||
|
||||
VS Code supports launching a local web client using the `code serve-web`
|
||||
command. To add VS Code web as a web IDE, you have two options.
|
||||
|
||||
1. Install using the
|
||||
[vscode-web module](https://registry.coder.com/modules/vscode-web) from the
|
||||
coder registry.
|
||||
|
||||
```tf
|
||||
module "vscode-web" {
|
||||
source = "registry.coder.com/modules/vscode-web/coder"
|
||||
version = "1.0.14"
|
||||
agent_id = coder_agent.main.id
|
||||
accept_license = true
|
||||
}
|
||||
```
|
||||
|
||||
2. Install and start in your `startup_script` and create a corresponding
|
||||
`coder_app`
|
||||
|
||||
```tf
|
||||
resource "coder_agent" "main" {
|
||||
arch = "amd64"
|
||||
os = "linux"
|
||||
startup_script = <<EOF
|
||||
#!/bin/sh
|
||||
# install VS Code
|
||||
curl -Lk 'https://code.visualstudio.com/sha/download?build=stable&os=cli-alpine-x64' --output vscode_cli.tar.gz
|
||||
mkdir -p /tmp/vscode-cli
|
||||
tar -xf vscode_cli.tar.gz -C /tmp/vscode-cli
|
||||
rm vscode_cli.tar.gz
|
||||
# start the web server on a specific port
|
||||
/tmp/vscode-cli/code serve-web --port 13338 --without-connection-token --accept-server-license-terms >/tmp/vscode-web.log 2>&1 &
|
||||
EOF
|
||||
}
|
||||
```
|
||||
|
||||
> `code serve-web` was introduced in version 1.82.0 (August 2023).
|
||||
|
||||
You also need to add a `coder_app` resource for this.
|
||||
|
||||
```tf
|
||||
# VS Code Web
|
||||
resource "coder_app" "vscode-web" {
|
||||
agent_id = coder_agent.coder.id
|
||||
slug = "vscode-web"
|
||||
display_name = "VS Code Web"
|
||||
icon = "/icon/code.svg"
|
||||
url = "http://localhost:13338?folder=/home/coder"
|
||||
subdomain = true # VS Code Web does currently does not work with a subpath https://github.com/microsoft/vscode/issues/192947
|
||||
share = "owner"
|
||||
}
|
||||
```
|
||||
|
||||
## Jupyter Notebook
|
||||
|
||||
To use Jupyter Notebook in your workspace, you can install it by using the
|
||||
[Jupyter Notebook module](https://registry.coder.com/modules/jupyter-notebook)
|
||||
from the Coder registry:
|
||||
|
||||
```tf
|
||||
module "jupyter-notebook" {
|
||||
source = "registry.coder.com/modules/jupyter-notebook/coder"
|
||||
version = "1.0.19"
|
||||
agent_id = coder_agent.example.id
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
## JupyterLab
|
||||
|
||||
Configure your agent and `coder_app` like so to use Jupyter. Notice the
|
||||
`subdomain=true` configuration:
|
||||
|
||||
```tf
|
||||
data "coder_workspace" "me" {}
|
||||
|
||||
resource "coder_agent" "coder" {
|
||||
os = "linux"
|
||||
arch = "amd64"
|
||||
dir = "/home/coder"
|
||||
startup_script = <<-EOF
|
||||
pip3 install jupyterlab
|
||||
$HOME/.local/bin/jupyter lab --ServerApp.token='' --ip='*'
|
||||
EOF
|
||||
}
|
||||
|
||||
resource "coder_app" "jupyter" {
|
||||
agent_id = coder_agent.coder.id
|
||||
slug = "jupyter"
|
||||
display_name = "JupyterLab"
|
||||
url = "http://localhost:8888"
|
||||
icon = "/icon/jupyter.svg"
|
||||
share = "owner"
|
||||
subdomain = true
|
||||
|
||||
healthcheck {
|
||||
url = "http://localhost:8888/healthz"
|
||||
interval = 5
|
||||
threshold = 10
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Or Alternatively, you can use the JupyterLab module from the Coder registry:
|
||||
|
||||
```tf
|
||||
module "jupyter" {
|
||||
source = "registry.coder.com/modules/jupyter-lab/coder"
|
||||
version = "1.0.0"
|
||||
agent_id = coder_agent.main.id
|
||||
}
|
||||
```
|
||||
|
||||
If you cannot enable a
|
||||
[wildcard subdomain](../../../admin/setup/index.md#wildcard-access-url), you can
|
||||
configure the template to run Jupyter on a path. There is however
|
||||
[security risk](../../../reference/cli/server.md#--dangerous-allow-path-app-sharing)
|
||||
running an app on a path and the template code is more complicated with coder
|
||||
value substitution to recreate the path structure.
|
||||
|
||||

|
||||
|
||||
## RStudio
|
||||
|
||||
Configure your agent and `coder_app` like so to use RStudio. Notice the
|
||||
`subdomain=true` configuration:
|
||||
|
||||
```tf
|
||||
resource "coder_agent" "coder" {
|
||||
os = "linux"
|
||||
arch = "amd64"
|
||||
dir = "/home/coder"
|
||||
startup_script = <<EOT
|
||||
#!/bin/bash
|
||||
# start rstudio
|
||||
/usr/lib/rstudio-server/bin/rserver --server-daemonize=1 --auth-none=1 &
|
||||
EOT
|
||||
}
|
||||
|
||||
resource "coder_app" "rstudio" {
|
||||
agent_id = coder_agent.coder.id
|
||||
slug = "rstudio"
|
||||
display_name = "R Studio"
|
||||
icon = "https://upload.wikimedia.org/wikipedia/commons/d/d0/RStudio_logo_flat.svg"
|
||||
url = "http://localhost:8787"
|
||||
subdomain = true
|
||||
share = "owner"
|
||||
|
||||
healthcheck {
|
||||
url = "http://localhost:8787/healthz"
|
||||
interval = 3
|
||||
threshold = 10
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you cannot enable a
|
||||
[wildcard subdomain](https://coder.com/docs/admin/configure#wildcard-access-url),
|
||||
you can configure the template to run RStudio on a path using an NGINX reverse
|
||||
proxy in the template. There is however
|
||||
[security risk](https://coder.com/docs/reference/cli/server#--dangerous-allow-path-app-sharing)
|
||||
running an app on a path and the template code is more complicated with coder
|
||||
value substitution to recreate the path structure.
|
||||
|
||||
[This](https://github.com/sempie/coder-templates/tree/main/rstudio) is a
|
||||
community template example.
|
||||
|
||||

|
||||
|
||||
## Airflow
|
||||
|
||||
Configure your agent and `coder_app` like so to use Airflow. Notice the
|
||||
`subdomain=true` configuration:
|
||||
|
||||
```tf
|
||||
resource "coder_agent" "coder" {
|
||||
os = "linux"
|
||||
arch = "amd64"
|
||||
dir = "/home/coder"
|
||||
startup_script = <<EOT
|
||||
#!/bin/bash
|
||||
# install and start airflow
|
||||
pip3 install apache-airflow
|
||||
/home/coder/.local/bin/airflow standalone &
|
||||
EOT
|
||||
}
|
||||
|
||||
resource "coder_app" "airflow" {
|
||||
agent_id = coder_agent.coder.id
|
||||
slug = "airflow"
|
||||
display_name = "Airflow"
|
||||
icon = "/icon/airflow.svg"
|
||||
url = "http://localhost:8080"
|
||||
subdomain = true
|
||||
share = "owner"
|
||||
|
||||
healthcheck {
|
||||
url = "http://localhost:8080/healthz"
|
||||
interval = 10
|
||||
threshold = 60
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
or use the [Airflow module](https://registry.coder.com/modules/apache-airflow)
|
||||
from the Coder registry:
|
||||
|
||||
```tf
|
||||
module "airflow" {
|
||||
source = "registry.coder.com/modules/airflow/coder"
|
||||
version = "1.0.13"
|
||||
agent_id = coder_agent.main.id
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
## File Browser
|
||||
|
||||
To access the contents of a workspace directory in a browser, you can use File
|
||||
Browser. File Browser is a lightweight file manager that allows you to view and
|
||||
manipulate files in a web browser.
|
||||
|
||||
Show and manipulate the contents of the `/home/coder` directory in a browser.
|
||||
|
||||
```tf
|
||||
resource "coder_agent" "coder" {
|
||||
os = "linux"
|
||||
arch = "amd64"
|
||||
dir = "/home/coder"
|
||||
startup_script = <<EOT
|
||||
#!/bin/bash
|
||||
|
||||
curl -fsSL https://raw.githubusercontent.com/filebrowser/get/master/get.sh | bash
|
||||
filebrowser --noauth --root /home/coder --port 13339 >/tmp/filebrowser.log 2>&1 &
|
||||
|
||||
EOT
|
||||
}
|
||||
|
||||
resource "coder_app" "filebrowser" {
|
||||
agent_id = coder_agent.coder.id
|
||||
display_name = "file browser"
|
||||
slug = "filebrowser"
|
||||
url = "http://localhost:13339"
|
||||
icon = "https://raw.githubusercontent.com/matifali/logos/main/database.svg"
|
||||
subdomain = true
|
||||
share = "owner"
|
||||
|
||||
healthcheck {
|
||||
url = "http://localhost:13339/healthz"
|
||||
interval = 3
|
||||
threshold = 10
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Or alternatively, you can use the
|
||||
[`filebrowser`](https://registry.coder.com/modules/filebrowser) module from the
|
||||
Coder registry:
|
||||
|
||||
```tf
|
||||
module "filebrowser" {
|
||||
source = "registry.coder.com/modules/filebrowser/coder"
|
||||
version = "1.0.8"
|
||||
agent_id = coder_agent.main.id
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
## SSH Fallback
|
||||
|
||||
If you prefer to run web IDEs in localhost, you can port forward using
|
||||
[SSH](../../../user-guides/workspace-access/index.md#ssh) or the Coder CLI
|
||||
`port-forward` sub-command. Some web IDEs may not support URL base path
|
||||
adjustment so port forwarding is the only approach.
|
87
docs/admin/templates/extending-templates/workspace-tags.md
Normal file
87
docs/admin/templates/extending-templates/workspace-tags.md
Normal file
@ -0,0 +1,87 @@
|
||||
# Workspace Tags
|
||||
|
||||
Template administrators can leverage static template tags to limit workspace
|
||||
provisioning to designated provisioner groups that have locally deployed
|
||||
credentials for creating workspace resources. While this method ensures
|
||||
controlled access, it offers limited flexibility and does not permit users to
|
||||
select the nodes for their workspace creation.
|
||||
|
||||
By using `coder_workspace_tags` and `coder_parameter`s, template administrators
|
||||
can enable dynamic tag selection and modify static template tags.
|
||||
|
||||
## Dynamic tag selection
|
||||
|
||||
Here is a sample `coder_workspace_tags` data resource with a few workspace tags
|
||||
specified:
|
||||
|
||||
```tf
|
||||
data "coder_workspace_tags" "custom_workspace_tags" {
|
||||
tags = {
|
||||
"zone" = "developers"
|
||||
"runtime" = data.coder_parameter.runtime_selector.value
|
||||
"project_id" = "PROJECT_${data.coder_parameter.project_name.value}"
|
||||
"cache" = data.coder_parameter.feature_cache_enabled.value == "true" ? "with-cache" : "no-cache"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Legend**
|
||||
|
||||
- `zone` - static tag value set to `developers`
|
||||
- `runtime` - supported by the string-type `coder_parameter` to select
|
||||
provisioner runtime, `runtime_selector`
|
||||
- `project_id` - a formatted string supported by the string-type
|
||||
`coder_parameter`, `project_name`
|
||||
- `cache` - an HCL condition involving boolean-type `coder_parameter`,
|
||||
`feature_cache_enabled`
|
||||
|
||||
Review the
|
||||
[full template example](https://github.com/coder/coder/tree/main/examples/workspace-tags)
|
||||
using `coder_workspace_tags` and `coder_parameter`s.
|
||||
|
||||
## Constraints
|
||||
|
||||
### Tagged provisioners
|
||||
|
||||
It is possible to choose tag combinations that no provisioner can handle. This
|
||||
will cause the provisioner job to get stuck in the queue until a provisioner is
|
||||
added that can handle its combination of tags.
|
||||
|
||||
Before releasing the template version with configurable workspace tags, ensure
|
||||
that every tag set is associated with at least one healthy provisioner.
|
||||
|
||||
### Parameters types
|
||||
|
||||
Provisioners require job tags to be defined in plain string format. When a
|
||||
workspace tag refers to a `coder_parameter` without involving the string
|
||||
formatter, for example,
|
||||
(`"runtime" = data.coder_parameter.runtime_selector.value`), the Coder
|
||||
provisioner server can transform only the following parameter types to strings:
|
||||
_string_, _number_, and _bool_.
|
||||
|
||||
### Mutability
|
||||
|
||||
A mutable `coder_parameter` can be dangerous for a workspace tag as it allows
|
||||
the workspace owner to change a provisioner group (due to different tags). In
|
||||
most cases, `coder_parameter`s backing `coder_workspace_tags` should be marked
|
||||
as immutable and set only once, during workspace creation.
|
||||
|
||||
### HCL syntax
|
||||
|
||||
When importing the template version with `coder_workspace_tags`, the Coder
|
||||
provisioner server extracts raw partial queries for each workspace tag and
|
||||
stores them in the database. During workspace build time, the Coder server uses
|
||||
the [Hashicorp HCL library](https://github.com/hashicorp/hcl) to evaluate these
|
||||
raw queries on-the-fly without processing the entire Terraform template. This
|
||||
evaluation is simpler but also limited in terms of available functions,
|
||||
variables, and references to other resources.
|
||||
|
||||
**Supported syntax**
|
||||
|
||||
- Static string: `foobar_tag = "foobaz"`
|
||||
- Formatted string: `foobar_tag = "foobaz ${data.coder_parameter.foobaz.value}"`
|
||||
- Reference to `coder_parameter`:
|
||||
`foobar_tag = data.coder_parameter.foobar.value`
|
||||
- Boolean logic: `production_tag = !data.coder_parameter.staging_env.value`
|
||||
- Condition:
|
||||
`cache = data.coder_parameter.feature_cache_enabled.value == "true" ? "with-cache" : "no-cache"`
|
Reference in New Issue
Block a user