Skip to content

Commit 7c0e76b

Browse files
kylecarbsDanielleMaywood
authored andcommitted
feat: Add app support (#17)
This adds what are presently named "devurls" in v1. It seems this is a dated term, since this allows much more than accessing applications via URL. "coder open <name>" will launch any apps defined. If in the web, it'll open either a web terminal or port forward to the desired application. If in the terminal, it'll open the browser, or launch the command over SSH.
1 parent 041bdd0 commit 7c0e76b

File tree

4 files changed

+210
-0
lines changed

4 files changed

+210
-0
lines changed

docs/resources/app.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "coder_app Resource - terraform-provider-coder"
4+
subcategory: ""
5+
description: |-
6+
Use this resource to define shortcuts to access applications in a workspace.
7+
---
8+
9+
# coder_app (Resource)
10+
11+
Use this resource to define shortcuts to access applications in a workspace.
12+
13+
## Example Usage
14+
15+
```terraform
16+
data "coder_workspace" "me" {}
17+
18+
resource "coder_agent" "dev" {
19+
os = "linux"
20+
arch = "amd64"
21+
dir = "/workspace"
22+
startup_script = <<EOF
23+
curl -fsSL https://code-server.dev/install.sh | sh
24+
code-server --auth none --port 13337
25+
EOF
26+
}
27+
28+
resource "coder_app" "code-server" {
29+
agent_id = coder_agent.dev.id
30+
name = "VS Code"
31+
icon = data.coder_workspace.me.access_url + "/icons/vscode.svg"
32+
target = "http://localhost:13337"
33+
}
34+
35+
resource "coder_app" "vim" {
36+
agent_id = coder_agent.dev.id
37+
name = "Vim"
38+
icon = data.coder_workspace.me.access_url + "/icons/vim.svg"
39+
command = "vim"
40+
}
41+
42+
resource "coder_app" "intellij" {
43+
agent_id = coder_agent.dev.id
44+
icon = data.coder_workspace.me.access_url + "/icons/intellij.svg"
45+
name = "JetBrains IntelliJ"
46+
command = "projector run"
47+
}
48+
```
49+
50+
<!-- schema generated by tfplugindocs -->
51+
## Schema
52+
53+
### Required
54+
55+
- `agent_id` (String) The "id" property of a "coder_agent" resource to associate with.
56+
57+
### Optional
58+
59+
- `command` (String) A command to run in a terminal opening this app. In the web, this will open in a new tab. In the CLI, this will SSH and execute the command.
60+
- `icon` (String) A URL to an icon that will display in the dashboard. View built-in icons here: https://github.com/coder/coder/tree/main/site/static/icons. Use a built-in icon with `data.coder_workspace.me.access_url + "/icons/<path>"`.
61+
- `id` (String) The ID of this resource.
62+
- `name` (String) A display name to identify the app.
63+
- `target` (String) A URL to be proxied to from inside the workspace.
64+
65+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
data "coder_workspace" "me" {}
2+
3+
resource "coder_agent" "dev" {
4+
os = "linux"
5+
arch = "amd64"
6+
dir = "/workspace"
7+
startup_script = <<EOF
8+
curl -fsSL https://code-server.dev/install.sh | sh
9+
code-server --auth none --port 13337
10+
EOF
11+
}
12+
13+
resource "coder_app" "code-server" {
14+
agent_id = coder_agent.dev.id
15+
name = "VS Code"
16+
icon = data.coder_workspace.me.access_url + "/icons/vscode.svg"
17+
target = "http://localhost:13337"
18+
}
19+
20+
resource "coder_app" "vim" {
21+
agent_id = coder_agent.dev.id
22+
name = "Vim"
23+
icon = data.coder_workspace.me.access_url + "/icons/vim.svg"
24+
command = "vim"
25+
}
26+
27+
resource "coder_app" "intellij" {
28+
agent_id = coder_agent.dev.id
29+
icon = data.coder_workspace.me.access_url + "/icons/intellij.svg"
30+
name = "JetBrains IntelliJ"
31+
command = "projector run"
32+
}

internal/provider/provider.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,19 @@ func New() *schema.Provider {
8989
id = uuid.NewString()
9090
}
9191
rd.SetId(id)
92+
config, valid := i.(config)
93+
if !valid {
94+
return diag.Errorf("config was unexpected type %q", reflect.TypeOf(i).String())
95+
}
96+
rd.Set("access_url", config.URL.String())
9297
return nil
9398
},
9499
Schema: map[string]*schema.Schema{
100+
"access_url": {
101+
Type: schema.TypeString,
102+
Computed: true,
103+
Description: "The access URL of the Coder deployment provisioning this workspace.",
104+
},
95105
"start_count": {
96106
Type: schema.TypeInt,
97107
Computed: true,
@@ -227,6 +237,65 @@ func New() *schema.Provider {
227237
},
228238
},
229239
},
240+
"coder_app": {
241+
Description: "Use this resource to define shortcuts to access applications in a workspace.",
242+
CreateContext: func(c context.Context, resourceData *schema.ResourceData, i interface{}) diag.Diagnostics {
243+
resourceData.SetId(uuid.NewString())
244+
return nil
245+
},
246+
ReadContext: func(c context.Context, resourceData *schema.ResourceData, i interface{}) diag.Diagnostics {
247+
return nil
248+
},
249+
DeleteContext: func(ctx context.Context, rd *schema.ResourceData, i interface{}) diag.Diagnostics {
250+
return nil
251+
},
252+
Schema: map[string]*schema.Schema{
253+
"agent_id": {
254+
Type: schema.TypeString,
255+
Description: `The "id" property of a "coder_agent" resource to associate with.`,
256+
ForceNew: true,
257+
Required: true,
258+
},
259+
"command": {
260+
Type: schema.TypeString,
261+
Description: "A command to run in a terminal opening this app. In the web, " +
262+
"this will open in a new tab. In the CLI, this will SSH and execute the command. " +
263+
"Either \"command\" or \"target\" may be specified, but not both.",
264+
ConflictsWith: []string{"target"},
265+
Optional: true,
266+
ForceNew: true,
267+
},
268+
"icon": {
269+
Type: schema.TypeString,
270+
Description: "A URL to an icon that will display in the dashboard. View built-in " +
271+
"icons here: https://github.com/coder/coder/tree/main/site/static/icons. Use a " +
272+
"built-in icon with `data.coder_workspace.me.access_url + \"/icons/<path>\"`.",
273+
ForceNew: true,
274+
Optional: true,
275+
ValidateFunc: func(i interface{}, s string) ([]string, []error) {
276+
_, err := url.Parse(s)
277+
if err != nil {
278+
return nil, []error{err}
279+
}
280+
return nil, nil
281+
},
282+
},
283+
"name": {
284+
Type: schema.TypeString,
285+
Description: "A display name to identify the app.",
286+
ForceNew: true,
287+
Optional: true,
288+
},
289+
"target": {
290+
Type: schema.TypeString,
291+
Description: "A URL to be proxied to from inside the workspace. " +
292+
"Either \"command\" or \"target\" may be specified, but not both.",
293+
ForceNew: true,
294+
Optional: true,
295+
ConflictsWith: []string{"command"},
296+
},
297+
},
298+
},
230299
},
231300
}
232301
}

internal/provider/provider_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,47 @@ func TestAgentInstance(t *testing.T) {
134134
}},
135135
})
136136
}
137+
138+
func TestApp(t *testing.T) {
139+
t.Parallel()
140+
resource.Test(t, resource.TestCase{
141+
Providers: map[string]*schema.Provider{
142+
"coder": provider.New(),
143+
},
144+
IsUnitTest: true,
145+
Steps: []resource.TestStep{{
146+
Config: `
147+
provider "coder" {
148+
}
149+
resource "coder_agent" "dev" {
150+
os = "linux"
151+
arch = "amd64"
152+
}
153+
resource "coder_app" "code-server" {
154+
agent_id = coder_agent.dev.id
155+
name = "code-server"
156+
icon = "builtin:vim"
157+
target = "http://localhost:13337"
158+
}
159+
`,
160+
Check: func(state *terraform.State) error {
161+
require.Len(t, state.Modules, 1)
162+
require.Len(t, state.Modules[0].Resources, 2)
163+
resource := state.Modules[0].Resources["coder_app.code-server"]
164+
require.NotNil(t, resource)
165+
for _, key := range []string{
166+
"agent_id",
167+
"name",
168+
"icon",
169+
"target",
170+
} {
171+
value := resource.Primary.Attributes[key]
172+
t.Logf("%q = %q", key, value)
173+
require.NotNil(t, value)
174+
require.Greater(t, len(value), 0)
175+
}
176+
return nil
177+
},
178+
}},
179+
})
180+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy