mirror of
https://github.com/coder/registry.git
synced 2026-06-03 04:58:15 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 258591833f | |||
| 3efc22c589 | |||
| 8ba4c323c2 | |||
| 3afa72095b | |||
| cf66809349 | |||
| 020a2cba79 | |||
| 3fd7b47097 | |||
| e1f077dac3 | |||
| 29c52b7072 | |||
| 312cb71bf0 |
@@ -1,6 +1,9 @@
|
||||
name: deploy-registry
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Runs at 02:30 UTC Monday through Friday
|
||||
- cron: "30 2 * * 1-5"
|
||||
push:
|
||||
tags:
|
||||
# Matches release/<namespace>/<resource_name>/<semantic_version>
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
---
|
||||
display_name: AgentAPI
|
||||
description: Building block for modules that need to run an agentapi server
|
||||
description: Building block for modules that need to run an AgentAPI server
|
||||
icon: ../../../../.icons/coder.svg
|
||||
verified: true
|
||||
tags: [internal]
|
||||
tags: [internal, library]
|
||||
---
|
||||
|
||||
# AgentAPI
|
||||
|
||||
The AgentAPI module is a building block for modules that need to run an agentapi server. It is intended primarily for internal use by Coder to create modules compatible with Tasks.
|
||||
> [!CAUTION]
|
||||
> We do not recommend using this module directly. Instead, please consider using one of our [Tasks-compatible AI agent modules](https://registry.coder.com/modules?search=tag%3Atasks).
|
||||
|
||||
We do not recommend using this module directly. Instead, please consider using one of our [Tasks-compatible AI agent modules](https://registry.coder.com/modules?search=tag%3Atasks).
|
||||
The AgentAPI module is a building block for modules that need to run an AgentAPI server. It is intended primarily for internal use by Coder to create modules compatible with Tasks.
|
||||
|
||||
```tf
|
||||
module "agentapi" {
|
||||
source = "registry.coder.com/coder/agentapi/coder"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
|
||||
agent_id = var.agent_id
|
||||
web_app_slug = local.app_slug
|
||||
@@ -50,4 +51,4 @@ module "agentapi" {
|
||||
|
||||
## For module developers
|
||||
|
||||
For a complete example of how to use this module, see the [goose module](https://github.com/coder/registry/blob/main/registry/coder/modules/goose/main.tf).
|
||||
For a complete example of how to use this module, see the [Goose module](https://github.com/coder/registry/blob/main/registry/coder/modules/goose/main.tf).
|
||||
|
||||
@@ -13,7 +13,7 @@ Run [Aider](https://aider.chat) AI pair programming in your workspace. This modu
|
||||
```tf
|
||||
module "aider" {
|
||||
source = "registry.coder.com/coder/aider/coder"
|
||||
version = "1.1.1"
|
||||
version = "1.1.2"
|
||||
agent_id = coder_agent.example.id
|
||||
}
|
||||
```
|
||||
@@ -30,29 +30,8 @@ module "aider" {
|
||||
|
||||
## Module Parameters
|
||||
|
||||
| Parameter | Description | Type | Default |
|
||||
| ---------------------------------- | -------------------------------------------------------------------------- | -------- | ------------------- |
|
||||
| `agent_id` | The ID of a Coder agent (required) | `string` | - |
|
||||
| `folder` | The folder to run Aider in | `string` | `/home/coder` |
|
||||
| `install_aider` | Whether to install Aider | `bool` | `true` |
|
||||
| `aider_version` | The version of Aider to install | `string` | `"latest"` |
|
||||
| `use_screen` | Whether to use screen for running Aider in the background | `bool` | `true` |
|
||||
| `use_tmux` | Whether to use tmux instead of screen for running Aider in the background | `bool` | `false` |
|
||||
| `session_name` | Name for the persistent session (screen or tmux) | `string` | `"aider"` |
|
||||
| `order` | Position of the app in the UI presentation | `number` | `null` |
|
||||
| `icon` | The icon to use for the app | `string` | `"/icon/aider.svg"` |
|
||||
| `experiment_report_tasks` | Whether to enable task reporting | `bool` | `true` |
|
||||
| `system_prompt` | System prompt for instructing Aider on task reporting and behavior | `string` | See default in code |
|
||||
| `task_prompt` | Task prompt to use with Aider | `string` | `""` |
|
||||
| `ai_provider` | AI provider to use with Aider (openai, anthropic, azure, etc.) | `string` | `"anthropic"` |
|
||||
| `ai_model` | AI model to use (can use Aider's built-in aliases like "sonnet", "4o") | `string` | `"sonnet"` |
|
||||
| `ai_api_key` | API key for the selected AI provider | `string` | `""` |
|
||||
| `custom_env_var_name` | Custom environment variable name when using custom provider | `string` | `""` |
|
||||
| `experiment_pre_install_script` | Custom script to run before installing Aider | `string` | `null` |
|
||||
| `experiment_post_install_script` | Custom script to run after installing Aider | `string` | `null` |
|
||||
| `experiment_additional_extensions` | Additional extensions configuration in YAML format to append to the config | `string` | `null` |
|
||||
|
||||
> **Note**: `use_screen` and `use_tmux` cannot both be enabled at the same time. By default, `use_screen` is set to `true` and `use_tmux` is set to `false`.
|
||||
> [!NOTE]
|
||||
> The `use_screen` and `use_tmux` parameters cannot both be enabled at the same time. By default, `use_screen` is set to `true` and `use_tmux` is set to `false`.
|
||||
|
||||
## Usage Examples
|
||||
|
||||
@@ -68,7 +47,7 @@ variable "anthropic_api_key" {
|
||||
module "aider" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/aider/coder"
|
||||
version = "1.1.1"
|
||||
version = "1.1.2"
|
||||
agent_id = coder_agent.example.id
|
||||
ai_api_key = var.anthropic_api_key
|
||||
}
|
||||
@@ -93,7 +72,7 @@ variable "openai_api_key" {
|
||||
module "aider" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/aider/coder"
|
||||
version = "1.1.1"
|
||||
version = "1.1.2"
|
||||
agent_id = coder_agent.example.id
|
||||
use_tmux = true
|
||||
ai_provider = "openai"
|
||||
@@ -114,7 +93,7 @@ variable "custom_api_key" {
|
||||
module "aider" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/aider/coder"
|
||||
version = "1.1.1"
|
||||
version = "1.1.2"
|
||||
agent_id = coder_agent.example.id
|
||||
ai_provider = "custom"
|
||||
custom_env_var_name = "MY_CUSTOM_API_KEY"
|
||||
@@ -131,7 +110,7 @@ You can extend Aider's capabilities by adding custom extensions:
|
||||
module "aider" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/aider/coder"
|
||||
version = "1.1.1"
|
||||
version = "1.1.2"
|
||||
agent_id = coder_agent.example.id
|
||||
ai_api_key = var.anthropic_api_key
|
||||
|
||||
@@ -210,7 +189,7 @@ data "coder_parameter" "ai_prompt" {
|
||||
module "aider" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/aider/coder"
|
||||
version = "1.1.1"
|
||||
version = "1.1.2"
|
||||
agent_id = coder_agent.example.id
|
||||
ai_api_key = var.anthropic_api_key
|
||||
task_prompt = data.coder_parameter.ai_prompt.value
|
||||
@@ -308,7 +287,3 @@ If you encounter issues:
|
||||
3. **Browser mode issues**: If the browser interface doesn't open, check that you're accessing it from a machine that can reach your Coder workspace
|
||||
|
||||
For more information on using Aider, see the [Aider documentation](https://aider.chat/docs/).
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
@@ -13,8 +13,9 @@ Run [Amazon Q](https://aws.amazon.com/q/) in your workspace to access Amazon's A
|
||||
```tf
|
||||
module "amazon-q" {
|
||||
source = "registry.coder.com/coder/amazon-q/coder"
|
||||
version = "1.1.1"
|
||||
version = "1.1.2"
|
||||
agent_id = coder_agent.example.id
|
||||
|
||||
# Required: see below for how to generate
|
||||
experiment_auth_tarball = var.amazon_q_auth_tarball
|
||||
}
|
||||
@@ -81,7 +82,7 @@ module "amazon-q" {
|
||||
```tf
|
||||
module "amazon-q" {
|
||||
source = "registry.coder.com/coder/amazon-q/coder"
|
||||
version = "1.1.1"
|
||||
version = "1.1.2"
|
||||
agent_id = coder_agent.example.id
|
||||
experiment_auth_tarball = var.amazon_q_auth_tarball
|
||||
experiment_use_tmux = true
|
||||
@@ -93,7 +94,7 @@ module "amazon-q" {
|
||||
```tf
|
||||
module "amazon-q" {
|
||||
source = "registry.coder.com/coder/amazon-q/coder"
|
||||
version = "1.1.1"
|
||||
version = "1.1.2"
|
||||
agent_id = coder_agent.example.id
|
||||
experiment_auth_tarball = var.amazon_q_auth_tarball
|
||||
experiment_report_tasks = true
|
||||
@@ -105,7 +106,7 @@ module "amazon-q" {
|
||||
```tf
|
||||
module "amazon-q" {
|
||||
source = "registry.coder.com/coder/amazon-q/coder"
|
||||
version = "1.1.1"
|
||||
version = "1.1.2"
|
||||
agent_id = coder_agent.example.id
|
||||
experiment_auth_tarball = var.amazon_q_auth_tarball
|
||||
experiment_pre_install_script = "echo Pre-install!"
|
||||
|
||||
@@ -125,24 +125,7 @@ variable "ai_prompt" {
|
||||
locals {
|
||||
encoded_pre_install_script = var.experiment_pre_install_script != null ? base64encode(var.experiment_pre_install_script) : ""
|
||||
encoded_post_install_script = var.experiment_post_install_script != null ? base64encode(var.experiment_post_install_script) : ""
|
||||
# We need to use allowed tools to limit the context Amazon Q receives.
|
||||
# Amazon Q can't handle big contexts, and the `create_template_version` tool
|
||||
# has a description that's too long.
|
||||
mcp_json = <<EOT
|
||||
{
|
||||
"mcpServers": {
|
||||
"coder": {
|
||||
"command": "coder",
|
||||
"args": ["exp", "mcp", "server", "--allowed-tools", "coder_report_task"],
|
||||
"env": {
|
||||
"CODER_MCP_APP_STATUS_SLUG": "amazon-q"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EOT
|
||||
encoded_mcp_json = base64encode(local.mcp_json)
|
||||
full_prompt = <<-EOT
|
||||
full_prompt = <<-EOT
|
||||
${var.system_prompt}
|
||||
|
||||
Your first task is:
|
||||
@@ -211,6 +194,12 @@ resource "coder_script" "amazon_q" {
|
||||
cd "$PREV_DIR"
|
||||
echo "Extracted auth tarball"
|
||||
|
||||
if [ "${var.experiment_report_tasks}" = "true" ]; then
|
||||
echo "Configuring Amazon Q to report tasks via Coder MCP..."
|
||||
q mcp add --name coder --command "coder" --args "exp,mcp,server,--allowed-tools,coder_report_task" --env "CODER_MCP_APP_STATUS_SLUG=amazon-q" --scope global --force
|
||||
echo "Added Coder MCP server to Amazon Q configuration"
|
||||
fi
|
||||
|
||||
if [ -n "${local.encoded_post_install_script}" ]; then
|
||||
echo "Running post-install script..."
|
||||
echo "${local.encoded_post_install_script}" | base64 -d > /tmp/post_install.sh
|
||||
@@ -218,13 +207,6 @@ resource "coder_script" "amazon_q" {
|
||||
/tmp/post_install.sh
|
||||
fi
|
||||
|
||||
if [ "${var.experiment_report_tasks}" = "true" ]; then
|
||||
echo "Configuring Amazon Q to report tasks via Coder MCP..."
|
||||
mkdir -p ~/.aws/amazonq
|
||||
echo "${local.encoded_mcp_json}" | base64 -d > ~/.aws/amazonq/mcp.json
|
||||
echo "Created the ~/.aws/amazonq/mcp.json configuration file"
|
||||
fi
|
||||
|
||||
if [ "${var.experiment_use_tmux}" = "true" ] && [ "${var.experiment_use_screen}" = "true" ]; then
|
||||
echo "Error: Both experiment_use_tmux and experiment_use_screen cannot be true simultaneously."
|
||||
echo "Please set only one of them to true."
|
||||
|
||||
@@ -13,7 +13,7 @@ Run the [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude
|
||||
```tf
|
||||
module "claude-code" {
|
||||
source = "registry.coder.com/coder/claude-code/coder"
|
||||
version = "2.0.3"
|
||||
version = "2.0.5"
|
||||
agent_id = coder_agent.example.id
|
||||
folder = "/home/coder"
|
||||
install_claude_code = true
|
||||
@@ -84,7 +84,7 @@ resource "coder_agent" "main" {
|
||||
module "claude-code" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/claude-code/coder"
|
||||
version = "2.0.3"
|
||||
version = "2.0.5"
|
||||
agent_id = coder_agent.example.id
|
||||
folder = "/home/coder"
|
||||
install_claude_code = true
|
||||
@@ -102,7 +102,7 @@ Run Claude Code as a standalone app in your workspace. This will install Claude
|
||||
```tf
|
||||
module "claude-code" {
|
||||
source = "registry.coder.com/coder/claude-code/coder"
|
||||
version = "2.0.3"
|
||||
version = "2.0.5"
|
||||
agent_id = coder_agent.example.id
|
||||
folder = "/home/coder"
|
||||
install_claude_code = true
|
||||
|
||||
@@ -100,7 +100,7 @@ variable "install_agentapi" {
|
||||
variable "agentapi_version" {
|
||||
type = string
|
||||
description = "The version of AgentAPI to install."
|
||||
default = "v0.2.3"
|
||||
default = "v0.3.0"
|
||||
}
|
||||
|
||||
locals {
|
||||
@@ -289,3 +289,11 @@ resource "coder_ai_task" "claude_code" {
|
||||
id = coder_app.claude_code_web.id
|
||||
}
|
||||
}
|
||||
|
||||
# As of https://github.com/coder/coder/commit/6ba4b5bbc95e2e528d7f5b1e31fffa200ae1a6db,
|
||||
# there's a bug in Coder's Terraform statefile parsing which prevents it from seeing coder_apps
|
||||
# in certain scenarios. This is a workaround to bypass this bug until we have a proper fix.
|
||||
# For more details see https://github.com/coder/coder/issues/18776
|
||||
resource "terraform_data" "claude_code_app_id" {
|
||||
input = coder_app.claude_code_web.id
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ The devcontainers-cli module provides an easy way to install [`@devcontainers/cl
|
||||
```tf
|
||||
module "devcontainers-cli" {
|
||||
source = "registry.coder.com/coder/devcontainers-cli/coder"
|
||||
version = "1.0.31"
|
||||
version = "1.0.32"
|
||||
agent_id = coder_agent.example.id
|
||||
}
|
||||
```
|
||||
|
||||
@@ -45,6 +45,8 @@ const executeScriptInContainerWithPackageManager = async (
|
||||
|
||||
console.log(path);
|
||||
|
||||
await execContainer(id, [shell, "-c", "mkdir -p /tmp/coder-script-data"]);
|
||||
|
||||
const resp = await execContainer(
|
||||
id,
|
||||
[shell, "-c", instance.script],
|
||||
@@ -52,6 +54,8 @@ const executeScriptInContainerWithPackageManager = async (
|
||||
"--env",
|
||||
"CODER_SCRIPT_BIN_DIR=/tmp/coder-script-data/bin",
|
||||
"--env",
|
||||
"CODER_SCRIPT_DATA_DIR=/tmp/coder-script-data",
|
||||
"--env",
|
||||
`PATH=${path}:/tmp/coder-script-data/bin`,
|
||||
],
|
||||
);
|
||||
|
||||
Regular → Executable
+7
-1
@@ -1,5 +1,11 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
# We want to cd into `$CODER_SCRIPT_DATA_DIR` as the current directory
|
||||
# might contain a `package.json` with `packageManager` set to something
|
||||
# other than the detected package manager. When this happens, it can
|
||||
# cause the installation to fail.
|
||||
cd "$CODER_SCRIPT_DATA_DIR"
|
||||
|
||||
# If @devcontainers/cli is already installed, we can skip
|
||||
if command -v devcontainer >/dev/null 2>&1; then
|
||||
echo "🥳 @devcontainers/cli is already installed into $(which devcontainer)!"
|
||||
@@ -34,7 +40,7 @@ install() {
|
||||
# so that the devcontainer command is available
|
||||
if [ -z "$PNPM_HOME" ]; then
|
||||
PNPM_HOME="$CODER_SCRIPT_BIN_DIR"
|
||||
export M_HOME
|
||||
export PNPM_HOME
|
||||
fi
|
||||
pnpm add -g @devcontainers/cli
|
||||
elif [ "$PACKAGE_MANAGER" = "yarn" ]; then
|
||||
|
||||
@@ -15,9 +15,9 @@ tags: [integration, vault, hashicorp, hvs]
|
||||
>
|
||||
> **Use these Coder registry modules instead:**
|
||||
>
|
||||
> - **[vault-token](https://registry.coder.com/modules/vault-token)** - Connect to Vault using access tokens
|
||||
> - **[vault-jwt](https://registry.coder.com/modules/vault-jwt)** - Connect to Vault using JWT/OIDC authentication
|
||||
> - **[vault-github](https://registry.coder.com/modules/vault-github)** - Connect to Vault using GitHub authentication
|
||||
> - **[vault-token](https://registry.coder.com/modules/coder/vault-token)** - Connect to Vault using access tokens
|
||||
> - **[vault-jwt](https://registry.coder.com/modules/coder/vault-jwt)** - Connect to Vault using JWT/OIDC authentication
|
||||
> - **[vault-github](https://registry.coder.com/modules/coder/vault-github)** - Connect to Vault using GitHub authentication
|
||||
>
|
||||
> These modules work with both self-hosted Vault and HCP Vault Dedicated. For migration help, see the [official HashiCorp announcement](https://developer.hashicorp.com/hcp/docs/vault-secrets/end-of-sale-announcement).
|
||||
|
||||
@@ -26,7 +26,7 @@ This module lets you fetch all or selective secrets from a [HCP Vault Secrets](h
|
||||
```tf
|
||||
module "vault" {
|
||||
source = "registry.coder.com/coder/hcp-vault-secrets/coder"
|
||||
version = "1.0.33"
|
||||
version = "1.0.34"
|
||||
agent_id = coder_agent.example.id
|
||||
app_name = "demo-app"
|
||||
project_id = "aaa-bbb-ccc"
|
||||
@@ -52,7 +52,7 @@ To fetch all secrets from the HCP Vault Secrets app, skip the `secrets` input.
|
||||
```tf
|
||||
module "vault" {
|
||||
source = "registry.coder.com/coder/hcp-vault-secrets/coder"
|
||||
version = "1.0.33"
|
||||
version = "1.0.34"
|
||||
agent_id = coder_agent.example.id
|
||||
app_name = "demo-app"
|
||||
project_id = "aaa-bbb-ccc"
|
||||
@@ -66,7 +66,7 @@ To fetch selective secrets from the HCP Vault Secrets app, set the `secrets` inp
|
||||
```tf
|
||||
module "vault" {
|
||||
source = "registry.coder.com/coder/hcp-vault-secrets/coder"
|
||||
version = "1.0.33"
|
||||
version = "1.0.34"
|
||||
agent_id = coder_agent.example.id
|
||||
app_name = "demo-app"
|
||||
project_id = "aaa-bbb-ccc"
|
||||
@@ -81,7 +81,7 @@ Set `client_id` and `client_secret` as module inputs.
|
||||
```tf
|
||||
module "vault" {
|
||||
source = "registry.coder.com/coder/hcp-vault-secrets/coder"
|
||||
version = "1.0.33"
|
||||
version = "1.0.34"
|
||||
agent_id = coder_agent.example.id
|
||||
app_name = "demo-app"
|
||||
project_id = "aaa-bbb-ccc"
|
||||
|
||||
@@ -16,7 +16,7 @@ A module that adds Jupyter Notebook in your Coder template.
|
||||
module "jupyter-notebook" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/jupyter-notebook/coder"
|
||||
version = "1.1.1"
|
||||
version = "1.2.0"
|
||||
agent_id = coder_agent.example.id
|
||||
}
|
||||
```
|
||||
|
||||
@@ -48,13 +48,27 @@ variable "group" {
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "requirements_path" {
|
||||
type = string
|
||||
description = "The path to requirements.txt with packages to preinstall"
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "pip_install_extra_packages" {
|
||||
type = string
|
||||
description = "List of extra packages to preinstall (example: numpy==1.26.4 pandas matplotlib<4 scikit-learn)"
|
||||
default = ""
|
||||
}
|
||||
|
||||
resource "coder_script" "jupyter-notebook" {
|
||||
agent_id = var.agent_id
|
||||
display_name = "jupyter-notebook"
|
||||
icon = "/icon/jupyter.svg"
|
||||
script = templatefile("${path.module}/run.sh", {
|
||||
LOG_PATH : var.log_path,
|
||||
PORT : var.port
|
||||
PORT : var.port,
|
||||
REQUIREMENTS_PATH : var.requirements_path,
|
||||
PIP_INSTALL_EXTRA_PACKAGES : var.pip_install_extra_packages
|
||||
})
|
||||
run_on_start = true
|
||||
}
|
||||
|
||||
@@ -20,6 +20,24 @@ else
|
||||
echo "🥳 jupyter-notebook is already installed\n\n"
|
||||
fi
|
||||
|
||||
# Install packages selected with REQUIREMENTS_PATH
|
||||
if [ -n "${REQUIREMENTS_PATH}" ]; then
|
||||
if [ -f "${REQUIREMENTS_PATH}" ]; then
|
||||
echo "📄 Installing packages from ${REQUIREMENTS_PATH}..."
|
||||
pipx -q runpip notebook install -r "${REQUIREMENTS_PATH}"
|
||||
echo "🥳 Packages from ${REQUIREMENTS_PATH} have been installed\n\n"
|
||||
else
|
||||
echo "⚠️ REQUIREMENTS_PATH is set to '${REQUIREMENTS_PATH}' but the file does not exist!\n\n"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Install packages selected with PIP_INSTALL_EXTRA_PACKAGES
|
||||
if [ -n "${PIP_INSTALL_EXTRA_PACKAGES}" ]; then
|
||||
echo "📦 Installing additional packages: ${PIP_INSTALL_EXTRA_PACKAGES}"
|
||||
pipx -q runpip notebook install ${PIP_INSTALL_EXTRA_PACKAGES}
|
||||
echo "🥳 Additional packages have been installed\n\n"
|
||||
fi
|
||||
|
||||
echo "👷 Starting jupyter-notebook in background..."
|
||||
echo "check logs at ${LOG_PATH}"
|
||||
$HOME/.local/bin/jupyter-notebook --NotebookApp.ip='0.0.0.0' --ServerApp.port=${PORT} --no-browser --ServerApp.token='' --ServerApp.password='' > ${LOG_PATH} 2>&1 &
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
@@ -0,0 +1,16 @@
|
||||
---
|
||||
display_name: "Eric Paulsen"
|
||||
bio: "Field CTO, EMEA @ Coder"
|
||||
avatar_url: "./.images/avatar.png"
|
||||
github: "ericpaulsen"
|
||||
linkedin: "https://www.linkedin.com/in/ericpaulsen17" # Optional
|
||||
website: "https://ericpaulsen.io" # Optional
|
||||
support_email: "ericpaulsen@hey.com" # Optional
|
||||
status: "community"
|
||||
---
|
||||
|
||||
# Eric Paulsen
|
||||
|
||||
I'm Eric Paulsen, Coder's EMEA Field CTO based in London, originating from Miami.
|
||||
Outside of working with our customers, I enjoy teaching myself things,
|
||||
playing volleyball, and dabbling in a bit of DJing & photography.
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
display_name: Kubernetes (Deployment) with Dynamic Username
|
||||
description: Provision Kubernetes Deployments as Coder workspaces with your Username
|
||||
icon: ../../../../.icons/kubernetes.svg
|
||||
verified: true
|
||||
tags: [kubernetes, container, username]
|
||||
---
|
||||
|
||||
# Remote development on Kubernetes with dynamic usernames
|
||||
|
||||
Provision Kubernetes Pods as [Coder workspaces](https://coder.com/docs/workspaces) with this example template. This template
|
||||
will run the workspace container as a non-root UID using your Coder username.
|
||||
|
||||
Here is the entrypoint logic in the template that enables Coder to source your username and write it to the Ubuntu operating system at start-up.
|
||||
|
||||
> These commands may differ if you run your workspace image with a distro other than Ubuntu.
|
||||
|
||||
```terraform
|
||||
command = ["sh", "-c", <<EOF
|
||||
# Create user and setup home directory
|
||||
sudo useradd ${data.coder_workspace_owner.me.name} --home=/home/${data.coder_workspace_owner.me.name} --shell=/bin/bash --uid=1001 --user-group
|
||||
sudo chown -R ${data.coder_workspace_owner.me.name}:${data.coder_workspace_owner.me.name} /home/${data.coder_workspace_owner.me.name}
|
||||
|
||||
# Switch to user and run agent
|
||||
exec sudo --preserve-env=CODER_AGENT_TOKEN -u ${data.coder_workspace_owner.me.name} sh -c '${coder_agent.main.init_script}'
|
||||
EOF
|
||||
]
|
||||
```
|
||||
|
||||
<!-- TODO: Add screenshot -->
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Infrastructure
|
||||
|
||||
**Cluster**: This template requires an existing Kubernetes cluster
|
||||
|
||||
**Container Image**: This template uses the [codercom/enterprise-base:ubuntu image](https://github.com/coder/enterprise-images/tree/main/images/base) with some dev tools preinstalled. To add additional tools, extend this image or build it yourself.
|
||||
|
||||
### Authentication
|
||||
|
||||
This template authenticates using a `~/.kube/config`, if present on the server, or via built-in authentication if the Coder provisioner is running on Kubernetes with an authorized ServiceAccount. To use another [authentication method](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs#authentication), edit the template.
|
||||
|
||||
## Architecture
|
||||
|
||||
This template provisions the following resources:
|
||||
|
||||
- Kubernetes Deployment (ephemeral)
|
||||
- Kubernetes persistent volume claim (persistent on `/home/${username}`, where `${username}` is your Coder username)
|
||||
|
||||
This means, when the workspace restarts, any tools or files outside of the home directory are not persisted. To pre-bake tools into the workspace (e.g. `python3`), modify the container image. Alternatively, individual developers can [personalize](https://coder.com/docs/dotfiles) their workspaces with dotfiles.
|
||||
@@ -0,0 +1,327 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
coder = {
|
||||
source = "coder/coder"
|
||||
}
|
||||
kubernetes = {
|
||||
source = "hashicorp/kubernetes"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "coder" {
|
||||
}
|
||||
|
||||
variable "use_kubeconfig" {
|
||||
type = bool
|
||||
description = <<-EOF
|
||||
Use host kubeconfig? (true/false)
|
||||
|
||||
Set this to false if the Coder host is itself running as a Pod on the same
|
||||
Kubernetes cluster as you are deploying workspaces to.
|
||||
|
||||
Set this to true if the Coder host is running outside the Kubernetes cluster
|
||||
for workspaces. A valid "~/.kube/config" must be present on the Coder host.
|
||||
EOF
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "namespace" {
|
||||
type = string
|
||||
description = "The Kubernetes namespace to create workspaces in (must exist prior to creating workspaces). If the Coder host is itself running as a Pod on the same Kubernetes cluster as you are deploying workspaces to, set this to the same namespace."
|
||||
}
|
||||
|
||||
data "coder_parameter" "cpu" {
|
||||
name = "cpu"
|
||||
display_name = "CPU"
|
||||
description = "The number of CPU cores"
|
||||
default = "2"
|
||||
icon = "/icon/memory.svg"
|
||||
mutable = true
|
||||
option {
|
||||
name = "2 Cores"
|
||||
value = "2"
|
||||
}
|
||||
option {
|
||||
name = "4 Cores"
|
||||
value = "4"
|
||||
}
|
||||
option {
|
||||
name = "6 Cores"
|
||||
value = "6"
|
||||
}
|
||||
option {
|
||||
name = "8 Cores"
|
||||
value = "8"
|
||||
}
|
||||
}
|
||||
|
||||
data "coder_parameter" "memory" {
|
||||
name = "memory"
|
||||
display_name = "Memory"
|
||||
description = "The amount of memory in GB"
|
||||
default = "2"
|
||||
icon = "/icon/memory.svg"
|
||||
mutable = true
|
||||
option {
|
||||
name = "2 GB"
|
||||
value = "2"
|
||||
}
|
||||
option {
|
||||
name = "4 GB"
|
||||
value = "4"
|
||||
}
|
||||
option {
|
||||
name = "6 GB"
|
||||
value = "6"
|
||||
}
|
||||
option {
|
||||
name = "8 GB"
|
||||
value = "8"
|
||||
}
|
||||
}
|
||||
|
||||
data "coder_parameter" "home_disk_size" {
|
||||
name = "home_disk_size"
|
||||
display_name = "Home disk size"
|
||||
description = "The size of the home disk in GB"
|
||||
default = "10"
|
||||
type = "number"
|
||||
icon = "/emojis/1f4be.png"
|
||||
mutable = false
|
||||
validation {
|
||||
min = 1
|
||||
max = 99999
|
||||
}
|
||||
}
|
||||
|
||||
provider "kubernetes" {
|
||||
# Authenticate via ~/.kube/config or a Coder-specific ServiceAccount, depending on admin preferences
|
||||
config_path = var.use_kubeconfig == true ? "~/.kube/config" : null
|
||||
}
|
||||
|
||||
data "coder_workspace" "me" {}
|
||||
data "coder_workspace_owner" "me" {}
|
||||
|
||||
module "vscode-web" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/vscode-web/coder"
|
||||
version = "1.3.1"
|
||||
agent_id = coder_agent.main.id
|
||||
accept_license = true
|
||||
}
|
||||
|
||||
resource "coder_agent" "main" {
|
||||
os = "linux"
|
||||
arch = "amd64"
|
||||
|
||||
# The following metadata blocks are optional. They are used to display
|
||||
# information about your workspace in the dashboard. You can remove them
|
||||
# if you don't want to display any information.
|
||||
# For basic resources, you can use the `coder stat` command.
|
||||
# If you need more control, you can write your own script.
|
||||
metadata {
|
||||
display_name = "CPU Usage"
|
||||
key = "0_cpu_usage"
|
||||
script = "coder stat cpu"
|
||||
interval = 10
|
||||
timeout = 1
|
||||
}
|
||||
|
||||
metadata {
|
||||
display_name = "RAM Usage"
|
||||
key = "1_ram_usage"
|
||||
script = "coder stat mem"
|
||||
interval = 10
|
||||
timeout = 1
|
||||
}
|
||||
|
||||
metadata {
|
||||
display_name = "Home Disk"
|
||||
key = "3_home_disk"
|
||||
script = "coder stat disk --path $${HOME}"
|
||||
interval = 60
|
||||
timeout = 1
|
||||
}
|
||||
|
||||
metadata {
|
||||
display_name = "CPU Usage (Host)"
|
||||
key = "4_cpu_usage_host"
|
||||
script = "coder stat cpu --host"
|
||||
interval = 10
|
||||
timeout = 1
|
||||
}
|
||||
|
||||
metadata {
|
||||
display_name = "Memory Usage (Host)"
|
||||
key = "5_mem_usage_host"
|
||||
script = "coder stat mem --host"
|
||||
interval = 10
|
||||
timeout = 1
|
||||
}
|
||||
|
||||
metadata {
|
||||
display_name = "Load Average (Host)"
|
||||
key = "6_load_host"
|
||||
# get load avg scaled by number of cores
|
||||
script = <<EOT
|
||||
echo "`cat /proc/loadavg | awk '{ print $1 }'` `nproc`" | awk '{ printf "%0.2f", $1/$2 }'
|
||||
EOT
|
||||
interval = 60
|
||||
timeout = 1
|
||||
}
|
||||
}
|
||||
|
||||
resource "kubernetes_persistent_volume_claim" "home" {
|
||||
metadata {
|
||||
name = "coder-${data.coder_workspace.me.id}-home"
|
||||
namespace = var.namespace
|
||||
labels = {
|
||||
"app.kubernetes.io/name" = "coder-pvc"
|
||||
"app.kubernetes.io/instance" = "coder-pvc-${data.coder_workspace.me.id}"
|
||||
"app.kubernetes.io/part-of" = "coder"
|
||||
//Coder-specific labels.
|
||||
"com.coder.resource" = "true"
|
||||
"com.coder.workspace.id" = data.coder_workspace.me.id
|
||||
"com.coder.workspace.name" = data.coder_workspace.me.name
|
||||
"com.coder.user.id" = data.coder_workspace_owner.me.id
|
||||
"com.coder.user.username" = data.coder_workspace_owner.me.name
|
||||
}
|
||||
annotations = {
|
||||
"com.coder.user.email" = data.coder_workspace_owner.me.email
|
||||
}
|
||||
}
|
||||
wait_until_bound = false
|
||||
spec {
|
||||
access_modes = ["ReadWriteOnce"]
|
||||
resources {
|
||||
requests = {
|
||||
storage = "${data.coder_parameter.home_disk_size.value}Gi"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "kubernetes_deployment" "main" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
depends_on = [
|
||||
kubernetes_persistent_volume_claim.home
|
||||
]
|
||||
wait_for_rollout = false
|
||||
metadata {
|
||||
name = "coder-${data.coder_workspace.me.id}"
|
||||
namespace = var.namespace
|
||||
labels = {
|
||||
"app.kubernetes.io/name" = "coder-workspace"
|
||||
"app.kubernetes.io/instance" = "coder-workspace-${data.coder_workspace.me.id}"
|
||||
"app.kubernetes.io/part-of" = "coder"
|
||||
"com.coder.resource" = "true"
|
||||
"com.coder.workspace.id" = data.coder_workspace.me.id
|
||||
"com.coder.workspace.name" = data.coder_workspace.me.name
|
||||
"com.coder.user.id" = data.coder_workspace_owner.me.id
|
||||
"com.coder.user.username" = data.coder_workspace_owner.me.name
|
||||
}
|
||||
annotations = {
|
||||
"com.coder.user.email" = data.coder_workspace_owner.me.email
|
||||
}
|
||||
}
|
||||
|
||||
spec {
|
||||
replicas = 1
|
||||
selector {
|
||||
match_labels = {
|
||||
"app.kubernetes.io/name" = "coder-workspace"
|
||||
"app.kubernetes.io/instance" = "coder-workspace-${data.coder_workspace.me.id}"
|
||||
"app.kubernetes.io/part-of" = "coder"
|
||||
"com.coder.resource" = "true"
|
||||
"com.coder.workspace.id" = data.coder_workspace.me.id
|
||||
"com.coder.workspace.name" = data.coder_workspace.me.name
|
||||
"com.coder.user.id" = data.coder_workspace_owner.me.id
|
||||
"com.coder.user.username" = data.coder_workspace_owner.me.name
|
||||
}
|
||||
}
|
||||
strategy {
|
||||
type = "Recreate"
|
||||
}
|
||||
|
||||
template {
|
||||
metadata {
|
||||
labels = {
|
||||
"app.kubernetes.io/name" = "coder-workspace"
|
||||
"app.kubernetes.io/instance" = "coder-workspace-${data.coder_workspace.me.id}"
|
||||
"app.kubernetes.io/part-of" = "coder"
|
||||
"com.coder.resource" = "true"
|
||||
"com.coder.workspace.id" = data.coder_workspace.me.id
|
||||
"com.coder.workspace.name" = data.coder_workspace.me.name
|
||||
"com.coder.user.id" = data.coder_workspace_owner.me.id
|
||||
"com.coder.user.username" = data.coder_workspace_owner.me.name
|
||||
}
|
||||
}
|
||||
spec {
|
||||
|
||||
|
||||
container {
|
||||
name = "dev"
|
||||
image = "codercom/enterprise-base:ubuntu"
|
||||
image_pull_policy = "Always"
|
||||
command = ["sh", "-c", <<EOF
|
||||
# Create user and setup home directory
|
||||
sudo useradd ${lower(data.coder_workspace_owner.me.name)} --home=/home/${lower(data.coder_workspace_owner.me.name)} --shell=/bin/bash --uid=1001 --user-group
|
||||
sudo chown -R ${lower(data.coder_workspace_owner.me.name)}:${lower(data.coder_workspace_owner.me.name)} /home/${lower(data.coder_workspace_owner.me.name)}
|
||||
|
||||
# Switch to user and run agent
|
||||
exec sudo --preserve-env=CODER_AGENT_TOKEN -u ${lower(data.coder_workspace_owner.me.name)} sh -c '${coder_agent.main.init_script}'
|
||||
EOF
|
||||
]
|
||||
env {
|
||||
name = "CODER_AGENT_TOKEN"
|
||||
value = coder_agent.main.token
|
||||
}
|
||||
resources {
|
||||
requests = {
|
||||
"cpu" = "250m"
|
||||
"memory" = "512Mi"
|
||||
}
|
||||
limits = {
|
||||
"cpu" = "${data.coder_parameter.cpu.value}"
|
||||
"memory" = "${data.coder_parameter.memory.value}Gi"
|
||||
}
|
||||
}
|
||||
volume_mount {
|
||||
mount_path = "/home/${lower(data.coder_workspace_owner.me.name)}"
|
||||
name = "home"
|
||||
read_only = false
|
||||
}
|
||||
}
|
||||
|
||||
volume {
|
||||
name = "home"
|
||||
persistent_volume_claim {
|
||||
claim_name = kubernetes_persistent_volume_claim.home.metadata.0.name
|
||||
read_only = false
|
||||
}
|
||||
}
|
||||
|
||||
affinity {
|
||||
// This affinity attempts to spread out all workspace pods evenly across
|
||||
// nodes.
|
||||
pod_anti_affinity {
|
||||
preferred_during_scheduling_ignored_during_execution {
|
||||
weight = 1
|
||||
pod_affinity_term {
|
||||
topology_key = "kubernetes.io/hostname"
|
||||
label_selector {
|
||||
match_expressions {
|
||||
key = "app.kubernetes.io/name"
|
||||
operator = "In"
|
||||
values = ["coder-workspace"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user