mirror of
https://github.com/coder/registry.git
synced 2026-06-02 20:48:14 +00:00
feat(cmd/tests): update tests to fail on invalid and absolute urls (#639)
## Description Updated the tests: - We now don't accept absolute urls (http / https) - We only accept urls that start with `../../../../.icons/*` and exists <!-- Briefly describe what this PR does and why --> ## Type of Change - [ ] New module - [ ] New template - [x] Bug fix - [ ] Feature/enhancement - [ ] Documentation - [ ] Other ## Testing & Validation - [x] Tests pass (`bun test`) - [x] Code formatted (`bun fmt`) - [x] Changes tested locally --------- Co-authored-by: DevCats <christofer@coder.com>
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
<svg width="120" height="120" viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M118.049 51.0329C115.107 49.0467 107.377 48.1984 101.756 49.7167C101.453 44.1017 98.5669 39.37 93.2862 35.2415L91.3313 33.9253L90.0286 35.8988C87.4676 39.7981 86.3886 44.993 86.771 49.7151C87.0725 52.6245 88.0816 55.8951 90.0286 58.2681C82.7137 62.524 75.9715 61.5579 46.112 61.5579H0.0103499C-0.124524 68.3204 0.95923 81.3298 9.20719 91.92C10.118 93.0898 11.1176 94.2214 12.2014 95.3116C18.907 102.047 29.0385 106.986 44.1888 107C67.3015 107.021 87.1042 94.4904 99.1493 64.1919C103.113 64.2572 113.576 64.9049 118.697 54.98C118.822 54.8129 119.999 52.3475 119.999 52.3475L118.048 51.0313L118.049 51.0329ZM30.0968 44.8481H17.133V57.8511H30.0968V44.8481ZM46.845 44.8481H33.8812V57.8511H46.845V44.8481ZM63.5932 44.8481H50.6294V57.8511H63.5932V44.8481ZM80.3414 44.8481H67.3777V57.8511H80.3414V44.8481ZM13.3486 44.8481H0.384824V57.8511H13.3486V44.8481ZM30.0968 28.4249H17.133V41.4279H30.0968V28.4249ZM46.845 28.4249H33.8812V41.4279H46.845V28.4249ZM63.5932 28.4249H50.6294V41.4279H63.5932V28.4249ZM63.5932 12H50.6294V25.003H63.5932V12Z" fill="#1D63ED"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,4 @@
|
||||
<svg width="1648" height="1648" viewBox="0 0 1648 1648" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1248 0H400C179.086 0 0 179.086 0 400V1248C0 1468.91 179.086 1648 400 1648H1248C1468.91 1648 1648 1468.91 1648 1248V400C1648 179.086 1468.91 0 1248 0Z" fill="white"/>
|
||||
<path d="M1293.43 1351.06C1349.36 1338.92 1408.68 1305.87 1408.68 1305.87L1304.86 1220.35C1253.53 1178.1 1209.85 1127.35 1175.69 1070.32C1128.5 991.535 1063.29 925.049 985.455 876.335L947.439 854.198C934.413 845.144 925.332 831.032 924.039 815.111C923.211 804.845 925.671 795.668 931.41 787.592C951.204 759.692 1053.52 638.291 1072.27 622.778C1096.41 602.819 1123.32 586.217 1148.28 567.209C1151.83 564.503 1155.39 561.812 1158.9 559.067C1159.02 558.944 1159.2 558.848 1159.31 558.74C1167.32 552.416 1174.88 545.699 1180.89 537.734C1202.59 512.606 1207.86 490.391 1209.15 480.56C1206.22 471.098 1197.46 449.942 1173.05 425.537C1188.35 426.476 1206.87 438.575 1223.66 452.81C1234.93 434.795 1246.73 415.76 1258.52 396.686C1266.39 383.945 1254.71 374.414 1254.39 374.117L1254.32 374.102C1254.32 374.102 1254.32 374.048 1254.31 374.036C1254.01 373.709 1244.48 362.03 1231.76 369.902C1204.58 386.72 1177.42 403.553 1153.26 418.847C1153.26 418.847 1124.62 418.25 1090.72 447.536C1082.74 453.56 1076.02 461.117 1069.71 469.112C1069.59 469.235 1069.48 469.397 1069.38 469.52C1066.62 473.015 1063.93 476.576 1061.24 480.14C1042.22 505.115 1025.63 532.01 1005.67 556.157C990.171 574.919 868.758 677.216 840.858 697.013C832.782 702.752 823.617 705.224 813.339 704.381C797.433 703.103 783.306 694.007 774.249 680.984L752.115 642.968C703.401 565.103 636.915 499.922 558.126 452.729C501.102 418.577 450.36 374.876 408.105 323.564L322.557 219.743C322.557 219.743 289.491 279.05 277.362 334.985C294.234 355.502 338.247 406.421 389.478 445.307C334.398 419.405 293.679 399.377 261.564 382.628C256.614 419.255 258.546 474.647 263.643 517.544C298.41 532.757 357.606 556.196 417.867 568.652C369.666 579.923 316.791 581.975 275.961 581.174C283.155 607.727 293.16 634.811 306.621 662.057C312.345 674.66 318.612 686.966 325.383 699.011C346.989 704.954 431.817 717.311 476.955 707.168C432.048 723.2 356.655 750.134 356.655 750.134C414.561 822.179 478.56 880.793 478.56 880.793C575.907 828.449 598.098 821.297 671.109 773.546C552.876 869.753 522.189 909.032 489 949.4L465.873 981.854C453.855 998.714 443.427 1016.62 434.712 1035.4C405.549 1098.14 364.269 1231.85 364.269 1231.85C356.901 1255.15 373.977 1272.23 396.6 1264.18C396.6 1264.18 530.28 1222.9 593.052 1193.74C611.817 1185.01 629.751 1174.58 646.596 1162.57L679.05 1139.45C689.94 1130.49 700.764 1121.71 712.647 1111.35C712.647 1111.35 794.346 1208.14 878.328 1271.81C878.328 1271.81 905.265 1196.42 921.294 1151.51C911.136 1196.66 923.496 1281.49 929.451 1303.08C941.469 1309.85 953.802 1316.12 966.405 1321.84C993.666 1335.31 1020.73 1345.31 1047.29 1352.5C1046.5 1311.66 1048.54 1258.8 1059.81 1210.6C1072.27 1270.86 1095.69 1330.07 1110.92 1364.82C1153.81 1369.92 1209.21 1371.85 1245.84 1366.9C1229.08 1334.79 1209.06 1294.04 1183.16 1238.99C1222.04 1290.22 1272.96 1334.23 1293.48 1351.1L1293.43 1351.06Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
@@ -1,22 +1,117 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
//go:embed testSamples/sampleReadmeBody.md
|
||||
var testBody string
|
||||
type readmeTestCase struct {
|
||||
filePath string
|
||||
shouldPass bool
|
||||
}
|
||||
|
||||
func TestValidateCoderResourceReadmeBody(t *testing.T) {
|
||||
func loadTestCases(t *testing.T, dir string, shouldPass bool) []readmeTestCase {
|
||||
t.Helper()
|
||||
files, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read directory %s: %v", dir, err)
|
||||
}
|
||||
|
||||
var testCases []readmeTestCase
|
||||
for _, file := range files {
|
||||
testCases = append(testCases, readmeTestCase{
|
||||
filePath: filepath.Join(dir, file.Name()),
|
||||
shouldPass: shouldPass,
|
||||
})
|
||||
}
|
||||
return testCases
|
||||
}
|
||||
|
||||
func TestValidateModuleReadmes(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("Parses a valid README body with zero issues", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
testCases := append(
|
||||
loadTestCases(t, "testSamples/modules/pass", true),
|
||||
loadTestCases(t, "testSamples/modules/fail", false)...,
|
||||
)
|
||||
|
||||
errs := validateCoderModuleReadmeBody(testBody)
|
||||
for _, e := range errs {
|
||||
t.Error(e)
|
||||
}
|
||||
})
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.filePath, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
content, err := os.ReadFile(tc.filePath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read file: %v", err)
|
||||
}
|
||||
|
||||
rm := readme{
|
||||
filePath: tc.filePath,
|
||||
rawText: string(content),
|
||||
}
|
||||
|
||||
resource, errs := parseCoderResourceReadme("modules", rm)
|
||||
if len(errs) != 0 {
|
||||
if tc.shouldPass {
|
||||
for _, e := range errs {
|
||||
t.Errorf("Unexpected parsing error: %v", e)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
validationErrs := validateCoderModuleReadme(resource)
|
||||
if tc.shouldPass && len(validationErrs) != 0 {
|
||||
for _, e := range validationErrs {
|
||||
t.Errorf("Unexpected validation error: %v", e)
|
||||
}
|
||||
} else if !tc.shouldPass && len(validationErrs) == 0 {
|
||||
t.Error("Expected validation errors but got none")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateTemplateReadmes(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := append(
|
||||
loadTestCases(t, "testSamples/templates/pass", true),
|
||||
loadTestCases(t, "testSamples/templates/fail", false)...,
|
||||
)
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.filePath, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
content, err := os.ReadFile(tc.filePath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read file: %v", err)
|
||||
}
|
||||
|
||||
rm := readme{
|
||||
filePath: tc.filePath,
|
||||
rawText: string(content),
|
||||
}
|
||||
|
||||
resource, errs := parseCoderResourceReadme("templates", rm)
|
||||
if len(errs) != 0 {
|
||||
if tc.shouldPass {
|
||||
for _, e := range errs {
|
||||
t.Errorf("Unexpected parsing error: %v", e)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
validationErrs := validateCoderModuleReadme(resource)
|
||||
if tc.shouldPass && len(validationErrs) != 0 {
|
||||
for _, e := range validationErrs {
|
||||
t.Errorf("Unexpected validation error: %v", e)
|
||||
}
|
||||
} else if !tc.shouldPass && len(validationErrs) == 0 {
|
||||
t.Error("Expected validation errors but got none")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,33 +82,43 @@ func validateCoderResourceDescription(description string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func isPermittedRelativeURL(checkURL string) bool {
|
||||
// Would normally be skittish about having relative paths like this, but it should be safe because we have
|
||||
// guarantees about the structure of the repo, and where this logic will run.
|
||||
return strings.HasPrefix(checkURL, "./") || strings.HasPrefix(checkURL, "/") || strings.HasPrefix(checkURL, "../../../../.icons")
|
||||
func isPermittedRelativeURL(checkURL string, readmeFilePath string) error {
|
||||
// Icon URLs must reference the top-level .icons directory
|
||||
expectedPrefix := "../../../../.icons/"
|
||||
if !strings.HasPrefix(checkURL, expectedPrefix) {
|
||||
return xerrors.Errorf("icon URL %q must reference the top-level .icons directory using %q", checkURL, expectedPrefix)
|
||||
}
|
||||
|
||||
// Resolve the path relative to the README file and check if it exists
|
||||
readmeDir := path.Dir(readmeFilePath)
|
||||
resolvedPath := path.Join(readmeDir, checkURL)
|
||||
|
||||
if _, err := os.Stat(resolvedPath); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return xerrors.Errorf("icon file does not exist at resolved path %q (referenced as %q)", resolvedPath, checkURL)
|
||||
}
|
||||
return xerrors.Errorf("error checking icon file at %q: %v", resolvedPath, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateCoderResourceIconURL(iconURL string) []error {
|
||||
func validateCoderResourceIconURL(iconURL string, filePath string) []error {
|
||||
if iconURL == "" {
|
||||
return []error{xerrors.New("icon URL cannot be empty")}
|
||||
}
|
||||
|
||||
var errs []error
|
||||
|
||||
// If the URL does not have a relative path.
|
||||
if !strings.HasPrefix(iconURL, ".") && !strings.HasPrefix(iconURL, "/") {
|
||||
if _, err := url.ParseRequestURI(iconURL); err != nil {
|
||||
errs = append(errs, xerrors.New("absolute icon URL is not correctly formatted"))
|
||||
}
|
||||
if strings.Contains(iconURL, "?") {
|
||||
errs = append(errs, xerrors.New("icon URLs cannot contain query parameters"))
|
||||
}
|
||||
// Reject absolute HTTP/HTTPS URLs - all icons must be local to the repository
|
||||
if strings.HasPrefix(iconURL, "http://") || strings.HasPrefix(iconURL, "https://") {
|
||||
errs = append(errs, xerrors.Errorf("icon URL must reference the top-level .icons directory, not an absolute URL %q", iconURL))
|
||||
return errs
|
||||
}
|
||||
|
||||
// If the URL has a relative path.
|
||||
if !isPermittedRelativeURL(iconURL) {
|
||||
errs = append(errs, xerrors.Errorf("relative icon URL %q must either be scoped to that module's directory, or the top-level /.icons directory (this can usually be done by starting the path with \"../../../.icons\")", iconURL))
|
||||
// Validate that the icon references ../../../../.icons/ and exists
|
||||
if err := isPermittedRelativeURL(iconURL, filePath); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
return errs
|
||||
@@ -153,7 +163,7 @@ func validateCoderResourceFrontmatter(resourceType string, filePath string, fm c
|
||||
errs = append(errs, addFilePathToError(filePath, err))
|
||||
}
|
||||
|
||||
for _, err := range validateCoderResourceIconURL(fm.IconURL) {
|
||||
for _, err := range validateCoderResourceIconURL(fm.IconURL, filePath) {
|
||||
errs = append(errs, addFilePathToError(filePath, err))
|
||||
}
|
||||
for _, err := range validateSupportedOperatingSystems(fm.OperatingSystems) {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
display_name: "Goose"
|
||||
description: "Run the Goose agent in your workspace to generate code and perform tasks"
|
||||
icon: "https://github.com/coder/registry/pull/599.svg"
|
||||
verified: false
|
||||
tags: ["ai", "agent"]
|
||||
---
|
||||
|
||||
# Goose
|
||||
|
||||
Run the [Goose](https://block.github.io/goose/) agent in your workspace to generate code and perform tasks.
|
||||
|
||||
```tf
|
||||
module "goose" {
|
||||
source = "registry.coder.com/coder/goose/coder"
|
||||
version = "1.0.31"
|
||||
agent_id = coder_agent.main.id
|
||||
folder = "/home/coder"
|
||||
install_goose = true
|
||||
goose_version = "v1.0.16"
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
display_name: "Wrong Path"
|
||||
description: "Test module with wrong icon path format"
|
||||
icon: "../../../../.icons/invalid.svg"
|
||||
verified: false
|
||||
tags: ["test"]
|
||||
---
|
||||
|
||||
# Wrong Path
|
||||
|
||||
This should fail validation.
|
||||
|
||||
```tf
|
||||
module "test" {
|
||||
source = "registry.coder.com/coder/test/coder"
|
||||
version = "1.0.0"
|
||||
agent_id = coder_agent.main.id
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,56 @@
|
||||
---
|
||||
display_name: "Docker Container"
|
||||
description: "Develop in a container on a Docker host"
|
||||
icon: "../../../../.icons/docker.svg"
|
||||
verified: true
|
||||
tags: ["docker", "container"]
|
||||
supported_os: ["linux", "macos"]
|
||||
---
|
||||
|
||||
# Docker Container
|
||||
|
||||
Develop in a Docker container on a remote Docker host.
|
||||
|
||||
```tf
|
||||
terraform {
|
||||
required_providers {
|
||||
coder = {
|
||||
source = "coder/coder"
|
||||
version = "~> 1.0"
|
||||
}
|
||||
docker = {
|
||||
source = "kreuzwerker/docker"
|
||||
version = "~> 3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "docker" {}
|
||||
|
||||
provider "coder" {}
|
||||
|
||||
data "coder_workspace" "me" {}
|
||||
|
||||
resource "coder_agent" "main" {
|
||||
os = "linux"
|
||||
arch = "amd64"
|
||||
}
|
||||
|
||||
resource "docker_container" "workspace" {
|
||||
image = "codercom/enterprise-base:ubuntu"
|
||||
name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}"
|
||||
|
||||
env = ["CODER_AGENT_TOKEN=${coder_agent.main.token}"]
|
||||
}
|
||||
```
|
||||
|
||||
## Getting Started
|
||||
|
||||
This template creates a Docker container on your Docker host. You'll need:
|
||||
|
||||
- A Docker host accessible from your Coder deployment
|
||||
- The Docker provider configured with appropriate credentials
|
||||
|
||||
## Customization
|
||||
|
||||
You can customize the container image, resources, and configuration to match your needs.
|
||||
@@ -1,121 +0,0 @@
|
||||
# Goose
|
||||
|
||||
Run the [Goose](https://block.github.io/goose/) agent in your workspace to generate code and perform tasks.
|
||||
|
||||
```tf
|
||||
module "goose" {
|
||||
source = "registry.coder.com/coder/goose/coder"
|
||||
version = "1.0.31"
|
||||
agent_id = coder_agent.main.id
|
||||
folder = "/home/coder"
|
||||
install_goose = true
|
||||
goose_version = "v1.0.16"
|
||||
}
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- `screen` must be installed in your workspace to run Goose in the background
|
||||
- You must add the [Coder Login](https://registry.coder.com/modules/coder-login) module to your template
|
||||
|
||||
The `codercom/oss-dogfood:latest` container image can be used for testing on container-based workspaces.
|
||||
|
||||
## Examples
|
||||
|
||||
Your workspace must have `screen` installed to use this.
|
||||
|
||||
### Run in the background and report tasks (Experimental)
|
||||
|
||||
> This functionality is in early access as of Coder v2.21 and is still evolving.
|
||||
> For now, we recommend testing it in a demo or staging environment,
|
||||
> rather than deploying to production
|
||||
>
|
||||
> Learn more in [the Coder documentation](https://coder.com/docs/tutorials/ai-agents)
|
||||
>
|
||||
> Join our [Discord channel](https://discord.gg/coder) or
|
||||
> [contact us](https://coder.com/contact) to get help or share feedback.
|
||||
|
||||
```tf
|
||||
module "coder-login" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/coder-login/coder"
|
||||
version = "1.0.15"
|
||||
agent_id = coder_agent.main.id
|
||||
}
|
||||
|
||||
variable "anthropic_api_key" {
|
||||
type = string
|
||||
description = "The Anthropic API key"
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
data "coder_parameter" "ai_prompt" {
|
||||
type = "string"
|
||||
name = "AI Prompt"
|
||||
default = ""
|
||||
description = "Write a prompt for Goose"
|
||||
mutable = true
|
||||
}
|
||||
|
||||
# Set the prompt and system prompt for Goose via environment variables
|
||||
resource "coder_agent" "main" {
|
||||
# ...
|
||||
env = {
|
||||
GOOSE_SYSTEM_PROMPT = <<-EOT
|
||||
You are a helpful assistant that can help write code.
|
||||
|
||||
Run all long running tasks (e.g. npm run dev) in the background and not in the foreground.
|
||||
|
||||
Periodically check in on background tasks.
|
||||
|
||||
Notify Coder of the status of the task before and after your steps.
|
||||
EOT
|
||||
GOOSE_TASK_PROMPT = data.coder_parameter.ai_prompt.value
|
||||
|
||||
# An API key is required for experiment_auto_configure
|
||||
# See https://block.github.io/goose/docs/getting-started/providers
|
||||
ANTHROPIC_API_KEY = var.anthropic_api_key # or use a coder_parameter
|
||||
}
|
||||
}
|
||||
|
||||
module "goose" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/goose/coder"
|
||||
version = "1.0.31"
|
||||
agent_id = coder_agent.main.id
|
||||
folder = "/home/coder"
|
||||
install_goose = true
|
||||
goose_version = "v1.0.16"
|
||||
|
||||
# Enable experimental features
|
||||
experiment_report_tasks = true
|
||||
|
||||
# Run Goose in the background
|
||||
experiment_use_screen = true
|
||||
|
||||
# Avoid configuring Goose manually
|
||||
experiment_auto_configure = true
|
||||
|
||||
# Required for experiment_auto_configure
|
||||
experiment_goose_provider = "anthropic"
|
||||
experiment_goose_model = "claude-3-5-sonnet-latest"
|
||||
}
|
||||
```
|
||||
|
||||
## Run standalone
|
||||
|
||||
Run Goose as a standalone app in your workspace. This will install Goose and run it directly without using screen or any task reporting to the Coder UI.
|
||||
|
||||
```tf
|
||||
module "goose" {
|
||||
source = "registry.coder.com/coder/goose/coder"
|
||||
version = "1.0.31"
|
||||
agent_id = coder_agent.main.id
|
||||
folder = "/home/coder"
|
||||
install_goose = true
|
||||
goose_version = "v1.0.16"
|
||||
|
||||
# Icon is not available in Coder v2.20 and below, so we'll use a custom icon URL
|
||||
icon = "https://raw.githubusercontent.com/block/goose/refs/heads/main/ui/desktop/src/images/icon.svg"
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
display_name: "Docker Container"
|
||||
description: "Develop in a container on a Docker host"
|
||||
icon: "https://github.com/coder/registry/pull/599.jpeg"
|
||||
verified: true
|
||||
tags: ["docker", "container"]
|
||||
supported_os: ["linux", "macos"]
|
||||
---
|
||||
|
||||
# Docker Container
|
||||
|
||||
Develop in a Docker container on a remote Docker host.
|
||||
|
||||
```tf
|
||||
terraform {
|
||||
required_providers {
|
||||
coder = {
|
||||
source = "coder/coder"
|
||||
version = "~> 1.0"
|
||||
}
|
||||
docker = {
|
||||
source = "kreuzwerker/docker"
|
||||
version = "~> 3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
display_name: "Docker Container"
|
||||
description: "Develop in a container on a Docker host"
|
||||
icon: "../../../../.icons/invalid.svg"
|
||||
verified: true
|
||||
tags: ["docker", "container"]
|
||||
supported_os: ["linux", "macos"]
|
||||
---
|
||||
|
||||
# Wrong Path
|
||||
|
||||
This should fail validation.
|
||||
|
||||
```tf
|
||||
module "test" {
|
||||
source = "registry.coder.com/coder/test/coder"
|
||||
version = "1.0.0"
|
||||
agent_id = coder_agent.main.id
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,56 @@
|
||||
---
|
||||
display_name: "Docker Container"
|
||||
description: "Develop in a container on a Docker host"
|
||||
icon: "../../../../.icons/docker.svg"
|
||||
verified: true
|
||||
tags: ["docker", "container"]
|
||||
supported_os: ["linux", "macos"]
|
||||
---
|
||||
|
||||
# Docker Container
|
||||
|
||||
Develop in a Docker container on a remote Docker host.
|
||||
|
||||
```tf
|
||||
terraform {
|
||||
required_providers {
|
||||
coder = {
|
||||
source = "coder/coder"
|
||||
version = "~> 1.0"
|
||||
}
|
||||
docker = {
|
||||
source = "kreuzwerker/docker"
|
||||
version = "~> 3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "docker" {}
|
||||
|
||||
provider "coder" {}
|
||||
|
||||
data "coder_workspace" "me" {}
|
||||
|
||||
resource "coder_agent" "main" {
|
||||
os = "linux"
|
||||
arch = "amd64"
|
||||
}
|
||||
|
||||
resource "docker_container" "workspace" {
|
||||
image = "codercom/enterprise-base:ubuntu"
|
||||
name = "coder-${data.coder_workspace.me.owner}-${data.coder_workspace.me.name}"
|
||||
|
||||
env = ["CODER_AGENT_TOKEN=${coder_agent.main.token}"]
|
||||
}
|
||||
```
|
||||
|
||||
## Getting Started
|
||||
|
||||
This template creates a Docker container on your Docker host. You'll need:
|
||||
|
||||
- A Docker host accessible from your Coder deployment
|
||||
- The Docker provider configured with appropriate credentials
|
||||
|
||||
## Customization
|
||||
|
||||
You can customize the container image, resources, and configuration to match your needs.
|
||||
Reference in New Issue
Block a user