-
Notifications
You must be signed in to change notification settings - Fork 59
feat: add module for Web RDP #262
Changes from all commits
c5c521f
1197e6b
12fd16f
bf06e8d
0e7644b
9f8eee5
ec922c7
748a180
ac648cc
8913567
7de78d2
53083a5
b93471a
20795aa
ff96b3f
aab5e55
29209d5
452f41a
c7aa825
047ccd6
d530d68
0b6975c
14e3fc5
fba0f84
d5cfadb
8195cf4
652fc6b
7022711
5ec1b20
c7a4fce
1a0a865
ef4c87e
a9a75b6
f3c30ab
8aff87f
b09c4cb
5f418c3
b283ac3
aebf095
f335cd3
33d44fd
b280764
83ecba2
264584e
de00f63
7d366ff
6409ee2
25c9000
5869eb8
90e15cd
05a20a9
f82c7fd
f369697
4ab7257
8262b29
16f96d3
78c9480
78f91a5
ec2c8ed
7a8483d
d9d1be0
a381c3e
c59eb0c
fd2f91c
13a8877
b4153a6
49f0605
a8580fe
b23d853
3f8f618
894e507
d98bfcb
aebdc9b
e8ee02c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,8 +29,10 @@ export const runContainer = async ( | |
return containerID.trim(); | ||
}; | ||
|
||
// executeScriptInContainer finds the only "coder_script" | ||
// resource in the given state and runs it in a container. | ||
/** | ||
* Finds the only "coder_script" resource in the given state and runs it in a | ||
* container. | ||
*/ | ||
export const executeScriptInContainer = async ( | ||
state: TerraformState, | ||
image: string, | ||
|
@@ -76,27 +78,30 @@ export const execContainer = async ( | |
}; | ||
}; | ||
|
||
type JsonValue = | ||
| string | ||
| number | ||
| boolean | ||
| null | ||
| JsonValue[] | ||
| { [key: string]: JsonValue }; | ||
|
||
type TerraformStateResource = { | ||
type: string; | ||
name: string; | ||
provider: string; | ||
instances: [{ attributes: Record<string, any> }]; | ||
}; | ||
|
||
export interface TerraformState { | ||
outputs: { | ||
[key: string]: { | ||
type: string; | ||
value: any; | ||
}; | ||
} | ||
resources: [ | ||
{ | ||
type: string; | ||
name: string; | ||
provider: string; | ||
instances: [ | ||
{ | ||
attributes: { | ||
[key: string]: any; | ||
}; | ||
}, | ||
]; | ||
}, | ||
]; | ||
}; | ||
|
||
resources: [TerraformStateResource, ...TerraformStateResource[]]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Redefined this so that |
||
} | ||
|
||
export interface CoderScriptAttributes { | ||
|
@@ -105,10 +110,11 @@ export interface CoderScriptAttributes { | |
url: string; | ||
} | ||
|
||
// findResourceInstance finds the first instance of the given resource | ||
// type in the given state. If name is specified, it will only find | ||
// the instance with the given name. | ||
export const findResourceInstance = <T extends "coder_script" | string>( | ||
/** | ||
* finds the first instance of the given resource type in the given state. If | ||
* name is specified, it will only find the instance with the given name. | ||
*/ | ||
export const findResourceInstance = <T extends string>( | ||
state: TerraformState, | ||
type: T, | ||
name?: string, | ||
|
@@ -131,12 +137,13 @@ export const findResourceInstance = <T extends "coder_script" | string>( | |
return resource.instances[0].attributes as any; | ||
}; | ||
|
||
// testRequiredVariables creates a test-case | ||
// for each variable provided and ensures that | ||
// the apply fails without it. | ||
export const testRequiredVariables = ( | ||
/** | ||
* Creates a test-case for each variable provided and ensures that the apply | ||
* fails without it. | ||
*/ | ||
export const testRequiredVariables = <TVars extends Record<string, string>>( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a type parameter so that you can define what properties you expect to be valid for the function, so that you can more easily catch accidental typos |
||
dir: string, | ||
vars: Record<string, string>, | ||
vars: TVars, | ||
) => { | ||
// Ensures that all required variables are provided. | ||
it("required variables", async () => { | ||
|
@@ -165,16 +172,25 @@ export const testRequiredVariables = ( | |
}); | ||
}; | ||
|
||
// runTerraformApply runs terraform apply in the given directory | ||
// with the given variables. It is fine to run in parallel with | ||
// other instances of this function, as it uses a random state file. | ||
export const runTerraformApply = async ( | ||
/** | ||
* Runs terraform apply in the given directory with the given variables. It is | ||
* fine to run in parallel with other instances of this function, as it uses a | ||
* random state file. | ||
*/ | ||
export const runTerraformApply = async < | ||
TVars extends Readonly<Record<string, string | boolean>>, | ||
>( | ||
dir: string, | ||
vars: Record<string, string>, | ||
env: Record<string, string> = {}, | ||
vars: TVars, | ||
env?: Record<string, string>, | ||
): Promise<TerraformState> => { | ||
const stateFile = `${dir}/${crypto.randomUUID()}.tfstate`; | ||
Object.keys(vars).forEach((key) => (env[`TF_VAR_${key}`] = vars[key])); | ||
|
||
const combinedEnv = env === undefined ? {} : { ...env }; | ||
for (const [key, value] of Object.entries(vars)) { | ||
combinedEnv[`TF_VAR_${key}`] = String(value); | ||
} | ||
|
||
const proc = spawn( | ||
[ | ||
"terraform", | ||
|
@@ -188,22 +204,26 @@ export const runTerraformApply = async ( | |
], | ||
{ | ||
cwd: dir, | ||
env, | ||
env: combinedEnv, | ||
stderr: "pipe", | ||
stdout: "pipe", | ||
}, | ||
); | ||
|
||
const text = await readableStreamToText(proc.stderr); | ||
const exitCode = await proc.exited; | ||
if (exitCode !== 0) { | ||
throw new Error(text); | ||
} | ||
|
||
const content = await readFile(stateFile, "utf8"); | ||
await unlink(stateFile); | ||
return JSON.parse(content); | ||
}; | ||
|
||
// runTerraformInit runs terraform init in the given directory. | ||
/** | ||
* Runs terraform init in the given directory. | ||
*/ | ||
export const runTerraformInit = async (dir: string) => { | ||
const proc = spawn(["terraform", "init"], { | ||
cwd: dir, | ||
|
@@ -221,8 +241,8 @@ export const createJSONResponse = (obj: object, statusCode = 200): Response => { | |
"Content-Type": "application/json", | ||
}, | ||
status: statusCode, | ||
}) | ||
} | ||
}); | ||
}; | ||
|
||
export const writeCoder = async (id: string, script: string) => { | ||
const exec = await execContainer(id, [ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
--- | ||
display_name: Windows RDP | ||
description: RDP Server and Web Client, powered by Devolutions Gateway | ||
icon: ../.icons/desktop.svg | ||
maintainer_github: coder | ||
verified: true | ||
tags: [windows, rdp, web, desktop] | ||
--- | ||
|
||
# Windows RDP | ||
|
||
Enable Remote Desktop + a web based client on Windows workspaces, powered by [devolutions-gateway](https://github.com/Devolutions/devolutions-gateway). | ||
|
||
```tf | ||
# AWS example. See below for examples of using this module with other providers | ||
module "windows_rdp" { | ||
source = "registry.coder.com/coder/module/windows-rdp" | ||
version = "1.0.16" | ||
count = data.coder_workspace.me.start_count | ||
agent_id = resource.coder_agent.main.id | ||
resource_id = resource.aws_instance.dev.id | ||
} | ||
``` | ||
|
||
## Video | ||
|
||
https://github.com/coder/modules/assets/28937484/fb5f4a55-7b69-4550-ab62-301e13a4be02 | ||
|
||
## Examples | ||
|
||
### With AWS | ||
|
||
```tf | ||
module "windows_rdp" { | ||
source = "registry.coder.com/coder/module/windows-rdp" | ||
version = "1.0.16" | ||
count = data.coder_workspace.me.start_count | ||
agent_id = resource.coder_agent.main.id | ||
resource_id = resource.aws_instance.dev.id | ||
} | ||
``` | ||
|
||
### With Google Cloud | ||
|
||
```tf | ||
module "windows_rdp" { | ||
source = "registry.coder.com/coder/module/windows-rdp" | ||
version = "1.0.16" | ||
count = data.coder_workspace.me.start_count | ||
agent_id = resource.coder_agent.main.id | ||
resource_id = resource.google_compute_instance.dev[0].id | ||
} | ||
``` | ||
|
||
## Roadmap | ||
|
||
- [ ] Test on Microsoft Azure. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated the comment formatting so that these would be public, and would show up when someone hovers over the function definition
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TIL:
/**
are public comments and//
are private 🤦