Skip to content

Commit 9a74558

Browse files
authored
feat: add coder_devcontainer resource (#368)
* feat: add `coder_devcontainer` resource This change will allow autostarting Dev Containers. This is implemented as a resource instead of a `schema.TypeSet` on the agent resource to allow definition via Coder modules. Updates coder/coder#16423
1 parent f75fb0b commit 9a74558

File tree

4 files changed

+174
-0
lines changed

4 files changed

+174
-0
lines changed

docs/resources/devcontainer.md

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "coder_devcontainer Resource - terraform-provider-coder"
4+
subcategory: ""
5+
description: |-
6+
Define a Dev Container the agent should know of and attempt to autostart (minimum Coder version: v2.21).
7+
---
8+
9+
# coder_devcontainer (Resource)
10+
11+
Define a Dev Container the agent should know of and attempt to autostart (minimum Coder version: v2.21).
12+
13+
14+
15+
<!-- schema generated by tfplugindocs -->
16+
## Schema
17+
18+
### Required
19+
20+
- `agent_id` (String) The `id` property of a `coder_agent` resource to associate with.
21+
- `workspace_folder` (String) The workspace folder to for the Dev Container.
22+
23+
### Optional
24+
25+
- `config_path` (String) The path to the Dev Container configuration file (devcontainer.json).
26+
27+
### Read-Only
28+
29+
- `id` (String) The ID of this resource.

provider/devcontainer.go

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package provider
2+
3+
import (
4+
"context"
5+
6+
"github.com/google/uuid"
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
10+
)
11+
12+
func devcontainerResource() *schema.Resource {
13+
return &schema.Resource{
14+
SchemaVersion: 1,
15+
16+
Description: "Define a Dev Container the agent should know of and attempt to autostart (minimum Coder version: v2.21).",
17+
CreateContext: func(_ context.Context, rd *schema.ResourceData, _ interface{}) diag.Diagnostics {
18+
rd.SetId(uuid.NewString())
19+
20+
return nil
21+
},
22+
ReadContext: schema.NoopContext,
23+
DeleteContext: schema.NoopContext,
24+
Schema: map[string]*schema.Schema{
25+
"agent_id": {
26+
Type: schema.TypeString,
27+
Description: "The `id` property of a `coder_agent` resource to associate with.",
28+
ForceNew: true,
29+
Required: true,
30+
},
31+
"workspace_folder": {
32+
Type: schema.TypeString,
33+
Description: "The workspace folder to for the Dev Container.",
34+
ForceNew: true,
35+
Required: true,
36+
ValidateFunc: validation.StringIsNotEmpty,
37+
},
38+
"config_path": {
39+
Type: schema.TypeString,
40+
Description: "The path to the Dev Container configuration file (devcontainer.json).",
41+
ForceNew: true,
42+
Optional: true,
43+
},
44+
},
45+
}
46+
}

provider/devcontainer_test.go

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package provider_test
2+
3+
import (
4+
"regexp"
5+
"testing"
6+
7+
"github.com/stretchr/testify/require"
8+
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
11+
)
12+
13+
func TestDevcontainer(t *testing.T) {
14+
t.Parallel()
15+
16+
resource.Test(t, resource.TestCase{
17+
ProviderFactories: coderFactory(),
18+
IsUnitTest: true,
19+
Steps: []resource.TestStep{{
20+
Config: `
21+
provider "coder" {
22+
}
23+
resource "coder_devcontainer" "example" {
24+
agent_id = "king"
25+
workspace_folder = "/workspace"
26+
config_path = "/workspace/devcontainer.json"
27+
}
28+
`,
29+
Check: func(state *terraform.State) error {
30+
require.Len(t, state.Modules, 1)
31+
require.Len(t, state.Modules[0].Resources, 1)
32+
script := state.Modules[0].Resources["coder_devcontainer.example"]
33+
require.NotNil(t, script)
34+
t.Logf("script attributes: %#v", script.Primary.Attributes)
35+
for key, expected := range map[string]string{
36+
"agent_id": "king",
37+
"workspace_folder": "/workspace",
38+
"config_path": "/workspace/devcontainer.json",
39+
} {
40+
require.Equal(t, expected, script.Primary.Attributes[key])
41+
}
42+
return nil
43+
},
44+
}},
45+
})
46+
}
47+
48+
func TestDevcontainerNoConfigPath(t *testing.T) {
49+
t.Parallel()
50+
51+
resource.Test(t, resource.TestCase{
52+
ProviderFactories: coderFactory(),
53+
IsUnitTest: true,
54+
Steps: []resource.TestStep{{
55+
Config: `
56+
provider "coder" {
57+
}
58+
resource "coder_devcontainer" "example" {
59+
agent_id = "king"
60+
workspace_folder = "/workspace"
61+
}
62+
`,
63+
Check: func(state *terraform.State) error {
64+
require.Len(t, state.Modules, 1)
65+
require.Len(t, state.Modules[0].Resources, 1)
66+
script := state.Modules[0].Resources["coder_devcontainer.example"]
67+
require.NotNil(t, script)
68+
t.Logf("script attributes: %#v", script.Primary.Attributes)
69+
for key, expected := range map[string]string{
70+
"agent_id": "king",
71+
"workspace_folder": "/workspace",
72+
} {
73+
require.Equal(t, expected, script.Primary.Attributes[key])
74+
}
75+
return nil
76+
},
77+
}},
78+
})
79+
}
80+
81+
func TestDevcontainerNoWorkspaceFolder(t *testing.T) {
82+
t.Parallel()
83+
84+
resource.Test(t, resource.TestCase{
85+
ProviderFactories: coderFactory(),
86+
IsUnitTest: true,
87+
Steps: []resource.TestStep{{
88+
Config: `
89+
provider "coder" {
90+
}
91+
resource "coder_devcontainer" "example" {
92+
agent_id = ""
93+
}
94+
`,
95+
ExpectError: regexp.MustCompile(`The argument "workspace_folder" is required, but no definition was found.`),
96+
}},
97+
})
98+
}

provider/provider.go

+1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ func New() *schema.Provider {
7676
"coder_metadata": metadataResource(),
7777
"coder_script": scriptResource(),
7878
"coder_env": envResource(),
79+
"coder_devcontainer": devcontainerResource(),
7980
},
8081
}
8182
}

0 commit comments

Comments
 (0)