From b2e87ef03813c9e558f0cccd98a4f2747f68461e Mon Sep 17 00:00:00 2001 From: Garrett Delfosse Date: Fri, 26 Apr 2024 18:34:15 +0000 Subject: [PATCH 01/23] feat: Add github-upload-public-key module --- github-upload-public-key/main.tf | 27 ++++++++++++++ github-upload-public-key/run.sh | 64 ++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 github-upload-public-key/main.tf create mode 100755 github-upload-public-key/run.sh diff --git a/github-upload-public-key/main.tf b/github-upload-public-key/main.tf new file mode 100644 index 0000000..f06b055 --- /dev/null +++ b/github-upload-public-key/main.tf @@ -0,0 +1,27 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + coder = { + source = "coder/coder" + version = ">= 0.12" + } + } +} + +variable "agent_id" { + type = string + description = "The ID of a Coder agent." +} + +resource "coder_script" "github_upload_public_key" { + agent_id = var.agent_id + script = templatefile("${path.module}/run.sh", { + CODER_OWNER_SESSION_TOKEN : data.coder_workspace.me.owner_session_token, + CODER_ACCESS_URL : data.coder_workspace.me.access_url + }) + display_name = "Github Upload Public Key" + icon = "/icon/github.svg" + run_on_start = true + start_blocks_login = true +} \ No newline at end of file diff --git a/github-upload-public-key/run.sh b/github-upload-public-key/run.sh new file mode 100755 index 0000000..8ea4a74 --- /dev/null +++ b/github-upload-public-key/run.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash + +set -e + +CODER_ACCESS_URL="${CODER_ACCESS_URL}" +CODER_OWNER_SESSION_TOKEN="${CODER_OWNER_SESSION_TOKEN}" + +if [ -z "$CODER_ACCESS_URL" ]; then + echo "No coder access url specified!" + exit 1 +fi + +if [ -z "$CODER_OWNER_SESSION_TOKEN" ]; then + echo "No coder owner session token specified!" + exit 1 +fi + +if [ -z "$GITHUB_TOKEN" ]; then + echo "No GITHUB_TOKEN in the workspace environment!" + exit 1 +fi + +PUBLIC_KEY_NAME="$CODER_ACCESS_URL Workspaces" + +echo "Fetching Coder public SSH key..." +PUBLIC_KEY=$(curl "$CODER_ACCESS_URL/api/v2/users/me/gitsshkey" \ + -H 'accept: application/json' \ + -H "cookie: coder_session_token=$CODER_OWNER_SESSION_TOKEN" \ + --fail \ + -s \ + | jq -r '.public_key' +) + +if [ -z "$PUBLIC_KEY" ]; then + echo "No Coder public SSH key found!" + exit 1 +fi + +echo "Fetching GitHub public SSH keys..." +GITHUB_MATCH=$(curl \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer $GITHUB_TOKEN" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + --fail \ + -s \ + https://api.github.com/user/keys \ + | jq -r --arg PUBLIC_KEY "$PUBLIC_KEY" '.[] | select(.key == $PUBLIC_KEY) | .key' +) + +if [ "$PUBLIC_KEY" = "$GITHUB_MATCH" ]; then + echo "Coder public SSH key is already uploaded to GitHub!" + exit 0 +fi +echo "Coder public SSH key not found in GitHub keys!" +echo "Uploading Coder public SSH key to GitHub..." +curl -L \ + -X POST \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer $GITHUB_TOKEN" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + https://api.github.com/user/keys \ + -d "{\"title\":\"$PUBLIC_KEY_NAME\",\"key\":\"$PUBLIC_KEY\"}" + +echo "Coder public SSH key uploaded to GitHub!" \ No newline at end of file From c9e418aaf5993b878d6c00e99fdff12391b94bf4 Mon Sep 17 00:00:00 2001 From: Garrett Delfosse Date: Sat, 27 Apr 2024 18:42:08 +0000 Subject: [PATCH 02/23] improve status code handling and add readme --- .icons/github.svg | 1 + github-upload-public-key/README.md | 70 ++++++++++++++++++++++++++++++ github-upload-public-key/main.tf | 3 +- github-upload-public-key/run.sh | 55 +++++++++++++++++------ 4 files changed, 114 insertions(+), 15 deletions(-) create mode 100644 .icons/github.svg create mode 100644 github-upload-public-key/README.md diff --git a/.icons/github.svg b/.icons/github.svg new file mode 100644 index 0000000..d5e6491 --- /dev/null +++ b/.icons/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/github-upload-public-key/README.md b/github-upload-public-key/README.md new file mode 100644 index 0000000..9e4a7b1 --- /dev/null +++ b/github-upload-public-key/README.md @@ -0,0 +1,70 @@ +--- +display_name: Github Upload Public Key +description: Automates uploading Coder public key to Github so users don't have to. +icon: ../.icons/github.svg +maintainer_github: f0ssel +verified: false +tags: [helper] +--- + +# github-upload-public-key + + + +```tf +module "github-upload-public-key" { + source = "registry.coder.com/modules/github-upload-public-key/coder" + version = "1.0.13" + agent_id = coder_agent.example.id +} +``` + + + +## Examples + +### Example 1 + +Install the Dracula theme from [OpenVSX](https://open-vsx.org/): + +```tf +module "MODULE_NAME" { + source = "registry.coder.com/modules/MODULE_NAME/coder" + version = "1.0.2" + agent_id = coder_agent.example.id + extensions = [ + "dracula-theme.theme-dracula" + ] +} +``` + +Enter the `.` into the extensions array and code-server will automatically install on start. + +### Example 2 + +Configure VS Code's [settings.json](https://code.visualstudio.com/docs/getstarted/settings#_settingsjson) file: + +```tf +module "MODULE_NAME" { + source = "registry.coder.com/modules/MODULE_NAME/coder" + version = "1.0.2" + agent_id = coder_agent.example.id + extensions = [ "dracula-theme.theme-dracula" ] + settings = { + "workbench.colorTheme" = "Dracula" + } +} +``` + +### Example 3 + +Run code-server in the background, don't fetch it from GitHub: + +```tf +module "MODULE_NAME" { + source = "registry.coder.com/modules/MODULE_NAME/coder" + version = "1.0.2" + agent_id = coder_agent.example.id + offline = true +} +``` diff --git a/github-upload-public-key/main.tf b/github-upload-public-key/main.tf index f06b055..7728f45 100644 --- a/github-upload-public-key/main.tf +++ b/github-upload-public-key/main.tf @@ -18,10 +18,9 @@ resource "coder_script" "github_upload_public_key" { agent_id = var.agent_id script = templatefile("${path.module}/run.sh", { CODER_OWNER_SESSION_TOKEN : data.coder_workspace.me.owner_session_token, - CODER_ACCESS_URL : data.coder_workspace.me.access_url + CODER_ACCESS_URL : data.coder_workspace.me.access_url, }) display_name = "Github Upload Public Key" icon = "/icon/github.svg" run_on_start = true - start_blocks_login = true } \ No newline at end of file diff --git a/github-upload-public-key/run.sh b/github-upload-public-key/run.sh index 8ea4a74..e973824 100755 --- a/github-upload-public-key/run.sh +++ b/github-upload-public-key/run.sh @@ -17,19 +17,28 @@ fi if [ -z "$GITHUB_TOKEN" ]; then echo "No GITHUB_TOKEN in the workspace environment!" + echo "The user must be authenticated with Github before this script can be run." exit 1 fi -PUBLIC_KEY_NAME="$CODER_ACCESS_URL Workspaces" - echo "Fetching Coder public SSH key..." -PUBLIC_KEY=$(curl "$CODER_ACCESS_URL/api/v2/users/me/gitsshkey" \ +PUBLIC_KEY_RESPONSE=$(curl -L -s \ + -w "%{http_code}" \ -H 'accept: application/json' \ -H "cookie: coder_session_token=$CODER_OWNER_SESSION_TOKEN" \ - --fail \ - -s \ - | jq -r '.public_key' + "$CODER_ACCESS_URL/api/v2/users/me/gitsshkey" ) +PUBLIC_KEY_RESPONSE_STATUS=$(tail -n1 <<< "$PUBLIC_KEY_RESPONSE") +PUBLIC_KEY_BODY=$(sed \$d <<< "$PUBLIC_KEY_RESPONSE") + +if [ "$PUBLIC_KEY_RESPONSE_STATUS" -ne 200 ]; then + echo "Failed to fetch Coder public SSH key with status code $PUBLIC_KEY_RESPONSE_STATUS!" + echo "$PUBLIC_KEY_BODY" + exit 1 +fi + +PUBLIC_KEY=$(jq -r '.public_key' <<< "$PUBLIC_KEY_BODY") +echo "Coder public SSH key found!" if [ -z "$PUBLIC_KEY" ]; then echo "No Coder public SSH key found!" @@ -37,28 +46,48 @@ if [ -z "$PUBLIC_KEY" ]; then fi echo "Fetching GitHub public SSH keys..." -GITHUB_MATCH=$(curl \ +GITHUB_KEYS_RESPONSE=$(curl -L -s \ + -w "%{http_code}" \ -H "Accept: application/vnd.github+json" \ -H "Authorization: Bearer $GITHUB_TOKEN" \ -H "X-GitHub-Api-Version: 2022-11-28" \ - --fail \ - -s \ - https://api.github.com/user/keys \ - | jq -r --arg PUBLIC_KEY "$PUBLIC_KEY" '.[] | select(.key == $PUBLIC_KEY) | .key' + https://api.github.com/user/keys ) +GITHUB_KEYS_RESPONSE_STATUS=$(tail -n1 <<< "$GITHUB_KEYS_RESPONSE") +GITHUB_KEYS_RESPONSE_BODY=$(sed \$d <<< "$GITHUB_KEYS_RESPONSE") + +if [ "$GITHUB_KEYS_RESPONSE_STATUS" -ne 200 ]; then + echo "Failed to fetch Coder public SSH key with status code $GITHUB_KEYS_RESPONSE_STATUS!" + echo "$GITHUB_KEYS_RESPONSE_BODY" + exit 1 +fi + +GITHUB_MATCH=$(jq -r --arg PUBLIC_KEY "$PUBLIC_KEY" '.[] | select(.key == $PUBLIC_KEY) | .key' <<< "$GITHUB_KEYS_RESPONSE_BODY") if [ "$PUBLIC_KEY" = "$GITHUB_MATCH" ]; then echo "Coder public SSH key is already uploaded to GitHub!" exit 0 fi + echo "Coder public SSH key not found in GitHub keys!" echo "Uploading Coder public SSH key to GitHub..." -curl -L \ +CODER_PUBLIC_KEY_NAME="$CODER_ACCESS_URL Workspaces" +UPLOAD_RESPONSE=$(curl -L -s \ -X POST \ + -w "%{http_code}" \ -H "Accept: application/vnd.github+json" \ -H "Authorization: Bearer $GITHUB_TOKEN" \ -H "X-GitHub-Api-Version: 2022-11-28" \ https://api.github.com/user/keys \ - -d "{\"title\":\"$PUBLIC_KEY_NAME\",\"key\":\"$PUBLIC_KEY\"}" + -d "{\"title\":\"$CODER_PUBLIC_KEY_NAME\",\"key\":\"nah\"}" +) +UPLOAD_RESPONSE_STATUS=$(tail -n1 <<< "$UPLOAD_RESPONSE") +UPLOAD_RESPONSE_BODY=$(sed \$d <<< "$UPLOAD_RESPONSE") + +if [ "$UPLOAD_RESPONSE_STATUS" -ne 201 ]; then + echo "Failed to upload Coder public SSH key with status code $UPLOAD_RESPONSE_STATUS!" + echo "$UPLOAD_RESPONSE_BODY" + exit 1 +fi echo "Coder public SSH key uploaded to GitHub!" \ No newline at end of file From edc163b5f2bd8052cf13bfb5fa42c77575ed8768 Mon Sep 17 00:00:00 2001 From: Garrett Delfosse Date: Sat, 27 Apr 2024 18:43:38 +0000 Subject: [PATCH 03/23] fix testing --- github-upload-public-key/run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/github-upload-public-key/run.sh b/github-upload-public-key/run.sh index e973824..68f30d7 100755 --- a/github-upload-public-key/run.sh +++ b/github-upload-public-key/run.sh @@ -79,7 +79,7 @@ UPLOAD_RESPONSE=$(curl -L -s \ -H "Authorization: Bearer $GITHUB_TOKEN" \ -H "X-GitHub-Api-Version: 2022-11-28" \ https://api.github.com/user/keys \ - -d "{\"title\":\"$CODER_PUBLIC_KEY_NAME\",\"key\":\"nah\"}" + -d "{\"title\":\"$CODER_PUBLIC_KEY_NAME\",\"key\":\"$PUBLIC_KEY\"}" ) UPLOAD_RESPONSE_STATUS=$(tail -n1 <<< "$UPLOAD_RESPONSE") UPLOAD_RESPONSE_BODY=$(sed \$d <<< "$UPLOAD_RESPONSE") From 1273378ca88af0984fc073c52536aba3b5d8d845 Mon Sep 17 00:00:00 2001 From: Garrett Delfosse Date: Sun, 28 Apr 2024 17:25:42 -0400 Subject: [PATCH 04/23] Update README.md --- github-upload-public-key/README.md | 49 +----------------------------- 1 file changed, 1 insertion(+), 48 deletions(-) diff --git a/github-upload-public-key/README.md b/github-upload-public-key/README.md index 9e4a7b1..208e36a 100644 --- a/github-upload-public-key/README.md +++ b/github-upload-public-key/README.md @@ -20,51 +20,4 @@ module "github-upload-public-key" { ``` - -## Examples - -### Example 1 - -Install the Dracula theme from [OpenVSX](https://open-vsx.org/): - -```tf -module "MODULE_NAME" { - source = "registry.coder.com/modules/MODULE_NAME/coder" - version = "1.0.2" - agent_id = coder_agent.example.id - extensions = [ - "dracula-theme.theme-dracula" - ] -} -``` - -Enter the `.` into the extensions array and code-server will automatically install on start. - -### Example 2 - -Configure VS Code's [settings.json](https://code.visualstudio.com/docs/getstarted/settings#_settingsjson) file: - -```tf -module "MODULE_NAME" { - source = "registry.coder.com/modules/MODULE_NAME/coder" - version = "1.0.2" - agent_id = coder_agent.example.id - extensions = [ "dracula-theme.theme-dracula" ] - settings = { - "workbench.colorTheme" = "Dracula" - } -} -``` - -### Example 3 - -Run code-server in the background, don't fetch it from GitHub: - -```tf -module "MODULE_NAME" { - source = "registry.coder.com/modules/MODULE_NAME/coder" - version = "1.0.2" - agent_id = coder_agent.example.id - offline = true -} -``` + From e8ce194ff702f3c272ba60725ec344a76ea26175 Mon Sep 17 00:00:00 2001 From: Garrett Delfosse Date: Tue, 30 Apr 2024 16:23:20 +0000 Subject: [PATCH 05/23] use code cli for token and update readme --- github-upload-public-key/README.md | 21 ++++++++++++++++----- github-upload-public-key/run.sh | 10 ++++++++-- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/github-upload-public-key/README.md b/github-upload-public-key/README.md index 208e36a..4317e02 100644 --- a/github-upload-public-key/README.md +++ b/github-upload-public-key/README.md @@ -3,13 +3,13 @@ display_name: Github Upload Public Key description: Automates uploading Coder public key to Github so users don't have to. icon: ../.icons/github.svg maintainer_github: f0ssel -verified: false -tags: [helper] +verified: true +tags: [helper, git] --- # github-upload-public-key - +Templates that utilize Github External Auth can automatically ensure that the Coder public key is uploaded to Github so that users can clone repositories without needing to upload the public key themselves. ```tf module "github-upload-public-key" { @@ -19,5 +19,16 @@ module "github-upload-public-key" { } ``` - - +# Requirements + +Github External Auth must be enabled in the workspace for this module to work. The Github app that is configured for external auth must have both read and write permissions to "Git SSH keys" in order to upload the public key. Additionally, a Coder admin must also have the `admin:public_key` scope added to the external auth configuration of the Coder deployment. For example: + +``` +CODER_EXTERNAL_AUTH_0_ID="USER_DEFINED_ID" +CODER_EXTERNAL_AUTH_0_TYPE=github +CODER_EXTERNAL_AUTH_0_CLIENT_ID=xxxxxx +CODER_EXTERNAL_AUTH_0_CLIENT_SECRET=xxxxxxx +CODER_EXTERNAL_AUTH_0_SCOPES="repo,workflow,admin:public_key" +``` + +Note that the default scopes if not provided are `repo,workflow`. diff --git a/github-upload-public-key/run.sh b/github-upload-public-key/run.sh index 68f30d7..1e39ec2 100755 --- a/github-upload-public-key/run.sh +++ b/github-upload-public-key/run.sh @@ -15,11 +15,17 @@ if [ -z "$CODER_OWNER_SESSION_TOKEN" ]; then exit 1 fi +echo "Fetching GitHub token..." +GITHUB_TOKEN=$(coder external-auth access-token github) +if [ $? -ne 0 ]; then + echo "Failed to fetch GitHub token!" + exit 1 +fi if [ -z "$GITHUB_TOKEN" ]; then - echo "No GITHUB_TOKEN in the workspace environment!" - echo "The user must be authenticated with Github before this script can be run." + echo "No GitHub token found!" exit 1 fi +echo "GitHub token found!" echo "Fetching Coder public SSH key..." PUBLIC_KEY_RESPONSE=$(curl -L -s \ From 2e4378858433dd433beb5d9d8b02a83066f7b6e1 Mon Sep 17 00:00:00 2001 From: Garrett Delfosse Date: Tue, 30 Apr 2024 16:23:59 +0000 Subject: [PATCH 06/23] heading --- github-upload-public-key/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/github-upload-public-key/README.md b/github-upload-public-key/README.md index 4317e02..1a63d05 100644 --- a/github-upload-public-key/README.md +++ b/github-upload-public-key/README.md @@ -11,6 +11,8 @@ tags: [helper, git] Templates that utilize Github External Auth can automatically ensure that the Coder public key is uploaded to Github so that users can clone repositories without needing to upload the public key themselves. +# Example + ```tf module "github-upload-public-key" { source = "registry.coder.com/modules/github-upload-public-key/coder" From 428f386c4c3665ce14bb96b467a23a519351c576 Mon Sep 17 00:00:00 2001 From: Garrett Delfosse Date: Tue, 30 Apr 2024 16:29:10 +0000 Subject: [PATCH 07/23] add troubleshooting --- github-upload-public-key/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/github-upload-public-key/README.md b/github-upload-public-key/README.md index 1a63d05..e581fb6 100644 --- a/github-upload-public-key/README.md +++ b/github-upload-public-key/README.md @@ -33,4 +33,4 @@ CODER_EXTERNAL_AUTH_0_CLIENT_SECRET=xxxxxxx CODER_EXTERNAL_AUTH_0_SCOPES="repo,workflow,admin:public_key" ``` -Note that the default scopes if not provided are `repo,workflow`. +Note that the default scopes if not provided are `repo,workflow`. If the module is failing to complete after updating the external auth configuration, instruct users of the module to "Unlink" and "Link" their Github account in the External Auth user settings page to get the new scopes. \ No newline at end of file From 5a33af28ace261807232c50909cf6ea1d7646edd Mon Sep 17 00:00:00 2001 From: Garrett Delfosse Date: Tue, 30 Apr 2024 16:37:28 +0000 Subject: [PATCH 08/23] fmt --- github-upload-public-key/README.md | 4 +-- github-upload-public-key/main.tf | 8 +++--- github-upload-public-key/run.sh | 43 ++++++++++++++++-------------- 3 files changed, 29 insertions(+), 26 deletions(-) diff --git a/github-upload-public-key/README.md b/github-upload-public-key/README.md index e581fb6..98c27c1 100644 --- a/github-upload-public-key/README.md +++ b/github-upload-public-key/README.md @@ -11,7 +11,7 @@ tags: [helper, git] Templates that utilize Github External Auth can automatically ensure that the Coder public key is uploaded to Github so that users can clone repositories without needing to upload the public key themselves. -# Example +# Example ```tf module "github-upload-public-key" { @@ -33,4 +33,4 @@ CODER_EXTERNAL_AUTH_0_CLIENT_SECRET=xxxxxxx CODER_EXTERNAL_AUTH_0_SCOPES="repo,workflow,admin:public_key" ``` -Note that the default scopes if not provided are `repo,workflow`. If the module is failing to complete after updating the external auth configuration, instruct users of the module to "Unlink" and "Link" their Github account in the External Auth user settings page to get the new scopes. \ No newline at end of file +Note that the default scopes if not provided are `repo,workflow`. If the module is failing to complete after updating the external auth configuration, instruct users of the module to "Unlink" and "Link" their Github account in the External Auth user settings page to get the new scopes. diff --git a/github-upload-public-key/main.tf b/github-upload-public-key/main.tf index 7728f45..313d716 100644 --- a/github-upload-public-key/main.tf +++ b/github-upload-public-key/main.tf @@ -18,9 +18,9 @@ resource "coder_script" "github_upload_public_key" { agent_id = var.agent_id script = templatefile("${path.module}/run.sh", { CODER_OWNER_SESSION_TOKEN : data.coder_workspace.me.owner_session_token, - CODER_ACCESS_URL : data.coder_workspace.me.access_url, + CODER_ACCESS_URL : data.coder_workspace.me.access_url, }) - display_name = "Github Upload Public Key" - icon = "/icon/github.svg" - run_on_start = true + display_name = "Github Upload Public Key" + icon = "/icon/github.svg" + run_on_start = true } \ No newline at end of file diff --git a/github-upload-public-key/run.sh b/github-upload-public-key/run.sh index 1e39ec2..fa8e115 100755 --- a/github-upload-public-key/run.sh +++ b/github-upload-public-key/run.sh @@ -28,11 +28,12 @@ fi echo "GitHub token found!" echo "Fetching Coder public SSH key..." -PUBLIC_KEY_RESPONSE=$(curl -L -s \ - -w "%{http_code}" \ - -H 'accept: application/json' \ - -H "cookie: coder_session_token=$CODER_OWNER_SESSION_TOKEN" \ - "$CODER_ACCESS_URL/api/v2/users/me/gitsshkey" +PUBLIC_KEY_RESPONSE=$( + curl -L -s \ + -w "%{http_code}" \ + -H 'accept: application/json' \ + -H "cookie: coder_session_token=$CODER_OWNER_SESSION_TOKEN" \ + "$CODER_ACCESS_URL/api/v2/users/me/gitsshkey" ) PUBLIC_KEY_RESPONSE_STATUS=$(tail -n1 <<< "$PUBLIC_KEY_RESPONSE") PUBLIC_KEY_BODY=$(sed \$d <<< "$PUBLIC_KEY_RESPONSE") @@ -52,12 +53,13 @@ if [ -z "$PUBLIC_KEY" ]; then fi echo "Fetching GitHub public SSH keys..." -GITHUB_KEYS_RESPONSE=$(curl -L -s \ - -w "%{http_code}" \ - -H "Accept: application/vnd.github+json" \ - -H "Authorization: Bearer $GITHUB_TOKEN" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ - https://api.github.com/user/keys +GITHUB_KEYS_RESPONSE=$( + curl -L -s \ + -w "%{http_code}" \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer $GITHUB_TOKEN" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + https://api.github.com/user/keys ) GITHUB_KEYS_RESPONSE_STATUS=$(tail -n1 <<< "$GITHUB_KEYS_RESPONSE") GITHUB_KEYS_RESPONSE_BODY=$(sed \$d <<< "$GITHUB_KEYS_RESPONSE") @@ -78,14 +80,15 @@ fi echo "Coder public SSH key not found in GitHub keys!" echo "Uploading Coder public SSH key to GitHub..." CODER_PUBLIC_KEY_NAME="$CODER_ACCESS_URL Workspaces" -UPLOAD_RESPONSE=$(curl -L -s \ - -X POST \ - -w "%{http_code}" \ - -H "Accept: application/vnd.github+json" \ - -H "Authorization: Bearer $GITHUB_TOKEN" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ - https://api.github.com/user/keys \ - -d "{\"title\":\"$CODER_PUBLIC_KEY_NAME\",\"key\":\"$PUBLIC_KEY\"}" +UPLOAD_RESPONSE=$( + curl -L -s \ + -X POST \ + -w "%{http_code}" \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer $GITHUB_TOKEN" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + https://api.github.com/user/keys \ + -d "{\"title\":\"$CODER_PUBLIC_KEY_NAME\",\"key\":\"$PUBLIC_KEY\"}" ) UPLOAD_RESPONSE_STATUS=$(tail -n1 <<< "$UPLOAD_RESPONSE") UPLOAD_RESPONSE_BODY=$(sed \$d <<< "$UPLOAD_RESPONSE") @@ -96,4 +99,4 @@ if [ "$UPLOAD_RESPONSE_STATUS" -ne 201 ]; then exit 1 fi -echo "Coder public SSH key uploaded to GitHub!" \ No newline at end of file +echo "Coder public SSH key uploaded to GitHub!" From cff60c4a7ed1c9b8d1504923c1deac9ce5160cd0 Mon Sep 17 00:00:00 2001 From: Garrett Delfosse Date: Tue, 30 Apr 2024 16:53:47 +0000 Subject: [PATCH 09/23] add auth id var --- github-upload-public-key/README.md | 5 +++++ github-upload-public-key/main.tf | 7 +++++++ github-upload-public-key/run.sh | 8 +++++++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/github-upload-public-key/README.md b/github-upload-public-key/README.md index 98c27c1..21d0287 100644 --- a/github-upload-public-key/README.md +++ b/github-upload-public-key/README.md @@ -14,10 +14,15 @@ Templates that utilize Github External Auth can automatically ensure that the Co # Example ```tf +data "coder_external_auth" "github" { + id = "myauthid" +} + module "github-upload-public-key" { source = "registry.coder.com/modules/github-upload-public-key/coder" version = "1.0.13" agent_id = coder_agent.example.id + external_auth_id = data.coder_external_auth.github.id } ``` diff --git a/github-upload-public-key/main.tf b/github-upload-public-key/main.tf index 313d716..e370357 100644 --- a/github-upload-public-key/main.tf +++ b/github-upload-public-key/main.tf @@ -14,11 +14,18 @@ variable "agent_id" { description = "The ID of a Coder agent." } +variable "external_auth_id" { + type = string + description = "The ID of the GitHub external auth." + default = "github" +} + resource "coder_script" "github_upload_public_key" { agent_id = var.agent_id script = templatefile("${path.module}/run.sh", { CODER_OWNER_SESSION_TOKEN : data.coder_workspace.me.owner_session_token, CODER_ACCESS_URL : data.coder_workspace.me.access_url, + GITHUB_EXTERNAL_AUTH_ID : var.external_auth_id, }) display_name = "Github Upload Public Key" icon = "/icon/github.svg" diff --git a/github-upload-public-key/run.sh b/github-upload-public-key/run.sh index fa8e115..b31a9d3 100755 --- a/github-upload-public-key/run.sh +++ b/github-upload-public-key/run.sh @@ -4,6 +4,7 @@ set -e CODER_ACCESS_URL="${CODER_ACCESS_URL}" CODER_OWNER_SESSION_TOKEN="${CODER_OWNER_SESSION_TOKEN}" +GITHUB_EXTERNAL_AUTH_ID="${GITHUB_EXTERNAL_AUTH_ID}" if [ -z "$CODER_ACCESS_URL" ]; then echo "No coder access url specified!" @@ -15,8 +16,13 @@ if [ -z "$CODER_OWNER_SESSION_TOKEN" ]; then exit 1 fi +if [ -z "$GITHUB_EXTERNAL_AUTH_ID" ]; then + echo "No GitHub external auth id specified!" + exit 1 +fi + echo "Fetching GitHub token..." -GITHUB_TOKEN=$(coder external-auth access-token github) +GITHUB_TOKEN=$(coder external-auth access-token $GITHUB_EXTERNAL_AUTH_ID) if [ $? -ne 0 ]; then echo "Failed to fetch GitHub token!" exit 1 From 5030fcb9887c82db58b4bd052b833feb621ddf0a Mon Sep 17 00:00:00 2001 From: Garrett Delfosse Date: Tue, 30 Apr 2024 16:59:20 +0000 Subject: [PATCH 10/23] add coder workspace me --- github-upload-public-key/main.tf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/github-upload-public-key/main.tf b/github-upload-public-key/main.tf index e370357..cd22c1f 100644 --- a/github-upload-public-key/main.tf +++ b/github-upload-public-key/main.tf @@ -20,6 +20,8 @@ variable "external_auth_id" { default = "github" } +data "coder_workspace" "me" {} + resource "coder_script" "github_upload_public_key" { agent_id = var.agent_id script = templatefile("${path.module}/run.sh", { From a45706ad3ad64ded3ca67e2670b85c78cc97d32b Mon Sep 17 00:00:00 2001 From: Garrett Delfosse Date: Tue, 30 Apr 2024 17:03:21 +0000 Subject: [PATCH 11/23] fix Invalid template control keyword --- github-upload-public-key/run.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/github-upload-public-key/run.sh b/github-upload-public-key/run.sh index b31a9d3..f7c1e16 100755 --- a/github-upload-public-key/run.sh +++ b/github-upload-public-key/run.sh @@ -36,7 +36,7 @@ echo "GitHub token found!" echo "Fetching Coder public SSH key..." PUBLIC_KEY_RESPONSE=$( curl -L -s \ - -w "%{http_code}" \ + -w "%%{http_code}" \ -H 'accept: application/json' \ -H "cookie: coder_session_token=$CODER_OWNER_SESSION_TOKEN" \ "$CODER_ACCESS_URL/api/v2/users/me/gitsshkey" @@ -61,7 +61,7 @@ fi echo "Fetching GitHub public SSH keys..." GITHUB_KEYS_RESPONSE=$( curl -L -s \ - -w "%{http_code}" \ + -w "%%{http_code}" \ -H "Accept: application/vnd.github+json" \ -H "Authorization: Bearer $GITHUB_TOKEN" \ -H "X-GitHub-Api-Version: 2022-11-28" \ @@ -89,7 +89,7 @@ CODER_PUBLIC_KEY_NAME="$CODER_ACCESS_URL Workspaces" UPLOAD_RESPONSE=$( curl -L -s \ -X POST \ - -w "%{http_code}" \ + -w "%%{http_code}" \ -H "Accept: application/vnd.github+json" \ -H "Authorization: Bearer $GITHUB_TOKEN" \ -H "X-GitHub-Api-Version: 2022-11-28" \ From 180e10c3eec1891ef7119973b46ec3071a271e4b Mon Sep 17 00:00:00 2001 From: Garrett Delfosse Date: Tue, 30 Apr 2024 17:08:48 +0000 Subject: [PATCH 12/23] require curl and jq --- github-upload-public-key/README.md | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/github-upload-public-key/README.md b/github-upload-public-key/README.md index 21d0287..60525d9 100644 --- a/github-upload-public-key/README.md +++ b/github-upload-public-key/README.md @@ -11,23 +11,18 @@ tags: [helper, git] Templates that utilize Github External Auth can automatically ensure that the Coder public key is uploaded to Github so that users can clone repositories without needing to upload the public key themselves. -# Example - ```tf -data "coder_external_auth" "github" { - id = "myauthid" -} - module "github-upload-public-key" { source = "registry.coder.com/modules/github-upload-public-key/coder" version = "1.0.13" agent_id = coder_agent.example.id - external_auth_id = data.coder_external_auth.github.id } ``` # Requirements +This module requires `curl` and `jq` to be installed inside your workspace. + Github External Auth must be enabled in the workspace for this module to work. The Github app that is configured for external auth must have both read and write permissions to "Git SSH keys" in order to upload the public key. Additionally, a Coder admin must also have the `admin:public_key` scope added to the external auth configuration of the Coder deployment. For example: ``` @@ -39,3 +34,20 @@ CODER_EXTERNAL_AUTH_0_SCOPES="repo,workflow,admin:public_key" ``` Note that the default scopes if not provided are `repo,workflow`. If the module is failing to complete after updating the external auth configuration, instruct users of the module to "Unlink" and "Link" their Github account in the External Auth user settings page to get the new scopes. + +# Example + +Using a coder github external auth with a non-default id: (default is `github`) + +```tf +data "coder_external_auth" "github" { + id = "myauthid" +} + +module "github-upload-public-key" { + source = "registry.coder.com/modules/github-upload-public-key/coder" + version = "1.0.13" + agent_id = coder_agent.example.id + external_auth_id = data.coder_external_auth.github.id +} +``` \ No newline at end of file From 46bf422d619d6f90327bf04e5b5b755ee4fdb3c8 Mon Sep 17 00:00:00 2001 From: Garrett Delfosse Date: Tue, 30 Apr 2024 17:14:40 +0000 Subject: [PATCH 13/23] maintainer --- github-upload-public-key/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/github-upload-public-key/README.md b/github-upload-public-key/README.md index 60525d9..d776619 100644 --- a/github-upload-public-key/README.md +++ b/github-upload-public-key/README.md @@ -2,7 +2,7 @@ display_name: Github Upload Public Key description: Automates uploading Coder public key to Github so users don't have to. icon: ../.icons/github.svg -maintainer_github: f0ssel +maintainer_github: coder verified: true tags: [helper, git] --- @@ -45,9 +45,9 @@ data "coder_external_auth" "github" { } module "github-upload-public-key" { - source = "registry.coder.com/modules/github-upload-public-key/coder" - version = "1.0.13" - agent_id = coder_agent.example.id + source = "registry.coder.com/modules/github-upload-public-key/coder" + version = "1.0.13" + agent_id = coder_agent.example.id external_auth_id = data.coder_external_auth.github.id } -``` \ No newline at end of file +``` From 36fa871e7ba1354f46b3256f573b8147504fc1f4 Mon Sep 17 00:00:00 2001 From: Garrett Delfosse Date: Wed, 1 May 2024 15:19:50 -0400 Subject: [PATCH 14/23] add tests --- CONTRIBUTING.md | 2 + github-upload-public-key/main.test.ts | 89 +++++++++++++++++++++++++++ github-upload-public-key/main.tf | 26 +++++++- github-upload-public-key/run.sh | 22 ++++--- slackme/main.test.ts | 10 +-- test.ts | 11 +++- 6 files changed, 139 insertions(+), 21 deletions(-) create mode 100644 github-upload-public-key/main.test.ts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6aa77cf..198eede 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,6 +10,8 @@ To create a new module, clone this repository and run: A suite of test-helpers exists to run `terraform apply` on modules with variables, and test script output against containers. +The testing suite must be able to run docker containers with the `--network=host` flag, which typically requires running the tests on linux as this flag does not apply to Docker Desktop for MacOS and Windows. MacOS users can work around this by using something like [Orbstack](https://orbstack.dev/) instead of Docker Desktop. + Reference existing `*.test.ts` files for implementation. ```shell diff --git a/github-upload-public-key/main.test.ts b/github-upload-public-key/main.test.ts new file mode 100644 index 0000000..8b6dca7 --- /dev/null +++ b/github-upload-public-key/main.test.ts @@ -0,0 +1,89 @@ +import { describe, expect, it } from "bun:test"; +import { createJSONResponse, execContainer, findResourceInstance, runContainer, runTerraformApply, runTerraformInit, testRequiredVariables, writeCoder } from "../test"; +import { Server, serve } from "bun"; + +describe("github-upload-public-key", async () => { + await runTerraformInit(import.meta.dir); + + testRequiredVariables(import.meta.dir, { + agent_id: "foo", + }); + + it("creates new key if one does not exist", async () => { + const { instance, id } = await setupContainer(); + await writeCoder(id, "echo foo"); + let exec = await execContainer(id, ["bash", "-c", instance.script]); + expect(exec.stdout).toContain("Coder public SSH key uploaded to GitHub!") + expect(exec.exitCode).toBe(0); + }); + + it("does nothing if one already exists", async () => { + const { instance, id } = await setupContainer(); + await writeCoder(id, "echo findkey"); + let exec = await execContainer(id, ["bash", "-c", instance.script]); + expect(exec.stdout).toContain("Coder public SSH key is already uploaded to GitHub!") + expect(exec.exitCode).toBe(0); + }); +}); + +const setupContainer = async ( + image = "lorello/alpine-bash", + vars: Record = {}, + ) => { + const server = await setupServer(); + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + // trim the trailing slash on the URL + access_url: server.url.toString().slice(0, -1), + owner_session_token: "bar", + github_api_url: server.url.toString().slice(0, -1), + ...vars, + }); + const instance = findResourceInstance(state, "coder_script"); + const id = await runContainer(image); + return { id, instance }; +}; + +const setupServer = async (): Promise => { + let url: URL; + const fakeSlackHost = serve({ + fetch: (req) => { + url = new URL(req.url); + if (url.pathname === "/api/v2/users/me/gitsshkey") { + return createJSONResponse({ + public_key: "exists", + }); + } + + if (url.pathname === "/user/keys") { + if (req.method === "POST") { + return createJSONResponse({ + key: "created", + }, 201); + } + + // case: key already exists + if (req.headers.get("Authorization") == "Bearer findkey") { + return createJSONResponse([{ + key: "foo", + }, { + key: "exists", + }]); + } + + // case: key does not exist + return createJSONResponse([{ + key: "foo", + }]); + } + + + return createJSONResponse({ + error: "not_found" + }, 404); + }, + port: 0, + }); + + return fakeSlackHost; +} \ No newline at end of file diff --git a/github-upload-public-key/main.tf b/github-upload-public-key/main.tf index cd22c1f..7d18ddb 100644 --- a/github-upload-public-key/main.tf +++ b/github-upload-public-key/main.tf @@ -20,14 +20,34 @@ variable "external_auth_id" { default = "github" } +variable "github_api_url" { + type = string + description = "The URL of the GitHub instance." + default = "https://api.github.com" +} + +// Optional variables mostly for testing purposes, will normally come from data.coder_workspace.me +variable "access_url" { + type = string + description = "The access URL of the workspace." + default = "" +} + +variable "owner_session_token" { + type = string + description = "The owner session token of the workspace." + default = "" +} + data "coder_workspace" "me" {} resource "coder_script" "github_upload_public_key" { agent_id = var.agent_id script = templatefile("${path.module}/run.sh", { - CODER_OWNER_SESSION_TOKEN : data.coder_workspace.me.owner_session_token, - CODER_ACCESS_URL : data.coder_workspace.me.access_url, - GITHUB_EXTERNAL_AUTH_ID : var.external_auth_id, + CODER_OWNER_SESSION_TOKEN : var.owner_session_token != "" ? var.owner_session_token : data.coder_workspace.me.owner_session_token, + CODER_ACCESS_URL : var.access_url != "" ? var.access_url : data.coder_workspace.me.access_url, + CODER_EXTERNAL_AUTH_ID : var.external_auth_id, + GITHUB_API_URL : var.github_api_url, }) display_name = "Github Upload Public Key" icon = "/icon/github.svg" diff --git a/github-upload-public-key/run.sh b/github-upload-public-key/run.sh index f7c1e16..6dabefa 100755 --- a/github-upload-public-key/run.sh +++ b/github-upload-public-key/run.sh @@ -4,7 +4,8 @@ set -e CODER_ACCESS_URL="${CODER_ACCESS_URL}" CODER_OWNER_SESSION_TOKEN="${CODER_OWNER_SESSION_TOKEN}" -GITHUB_EXTERNAL_AUTH_ID="${GITHUB_EXTERNAL_AUTH_ID}" +CODER_EXTERNAL_AUTH_ID="${CODER_EXTERNAL_AUTH_ID}" +GITHUB_API_URL="${GITHUB_API_URL}" if [ -z "$CODER_ACCESS_URL" ]; then echo "No coder access url specified!" @@ -16,13 +17,18 @@ if [ -z "$CODER_OWNER_SESSION_TOKEN" ]; then exit 1 fi -if [ -z "$GITHUB_EXTERNAL_AUTH_ID" ]; then +if [ -z "$CODER_EXTERNAL_AUTH_ID" ]; then echo "No GitHub external auth id specified!" exit 1 fi +if [ -z "$GITHUB_API_URL" ]; then + echo "No GitHub API URL specified!" + exit 1 +fi + echo "Fetching GitHub token..." -GITHUB_TOKEN=$(coder external-auth access-token $GITHUB_EXTERNAL_AUTH_ID) +GITHUB_TOKEN=$(coder external-auth access-token $CODER_EXTERNAL_AUTH_ID) if [ $? -ne 0 ]; then echo "Failed to fetch GitHub token!" exit 1 @@ -36,7 +42,7 @@ echo "GitHub token found!" echo "Fetching Coder public SSH key..." PUBLIC_KEY_RESPONSE=$( curl -L -s \ - -w "%%{http_code}" \ + -w "\n%%{http_code}" \ -H 'accept: application/json' \ -H "cookie: coder_session_token=$CODER_OWNER_SESSION_TOKEN" \ "$CODER_ACCESS_URL/api/v2/users/me/gitsshkey" @@ -61,11 +67,11 @@ fi echo "Fetching GitHub public SSH keys..." GITHUB_KEYS_RESPONSE=$( curl -L -s \ - -w "%%{http_code}" \ + -w "\n%%{http_code}" \ -H "Accept: application/vnd.github+json" \ -H "Authorization: Bearer $GITHUB_TOKEN" \ -H "X-GitHub-Api-Version: 2022-11-28" \ - https://api.github.com/user/keys + $GITHUB_API_URL/user/keys ) GITHUB_KEYS_RESPONSE_STATUS=$(tail -n1 <<< "$GITHUB_KEYS_RESPONSE") GITHUB_KEYS_RESPONSE_BODY=$(sed \$d <<< "$GITHUB_KEYS_RESPONSE") @@ -89,11 +95,11 @@ CODER_PUBLIC_KEY_NAME="$CODER_ACCESS_URL Workspaces" UPLOAD_RESPONSE=$( curl -L -s \ -X POST \ - -w "%%{http_code}" \ + -w "\n%%{http_code}" \ -H "Accept: application/vnd.github+json" \ -H "Authorization: Bearer $GITHUB_TOKEN" \ -H "X-GitHub-Api-Version: 2022-11-28" \ - https://api.github.com/user/keys \ + $GITHUB_API_URL/user/keys \ -d "{\"title\":\"$CODER_PUBLIC_KEY_NAME\",\"key\":\"$PUBLIC_KEY\"}" ) UPLOAD_RESPONSE_STATUS=$(tail -n1 <<< "$UPLOAD_RESPONSE") diff --git a/slackme/main.test.ts b/slackme/main.test.ts index 53c6b2e..402a690 100644 --- a/slackme/main.test.ts +++ b/slackme/main.test.ts @@ -8,6 +8,7 @@ import { runTerraformApply, runTerraformInit, testRequiredVariables, + writeCoder, } from "../test"; describe("slackme", async () => { @@ -119,15 +120,6 @@ const setupContainer = async ( return { id, instance }; }; -const writeCoder = async (id: string, script: string) => { - const exec = await execContainer(id, [ - "sh", - "-c", - `echo '${script}' > /usr/bin/coder && chmod +x /usr/bin/coder`, - ]); - expect(exec.exitCode).toBe(0); -}; - const assertSlackMessage = async (opts: { command: string; format?: string; diff --git a/test.ts b/test.ts index 97416cf..c2eb65e 100644 --- a/test.ts +++ b/test.ts @@ -222,4 +222,13 @@ export const createJSONResponse = (obj: object, statusCode = 200): Response => { }, status: statusCode, }) -} \ No newline at end of file +} + +export const writeCoder = async (id: string, script: string) => { + const exec = await execContainer(id, [ + "sh", + "-c", + `echo '${script}' > /usr/bin/coder && chmod +x /usr/bin/coder`, + ]); + expect(exec.exitCode).toBe(0); +}; From aced7547bce2304f83802b6d51bfc2932badcfef Mon Sep 17 00:00:00 2001 From: Garrett Delfosse Date: Wed, 1 May 2024 15:20:22 -0400 Subject: [PATCH 15/23] fmt --- CONTRIBUTING.md | 2 +- github-upload-public-key/main.test.ts | 129 +++++++++++++++----------- github-upload-public-key/main.tf | 2 +- 3 files changed, 77 insertions(+), 56 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 198eede..749791f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,7 +10,7 @@ To create a new module, clone this repository and run: A suite of test-helpers exists to run `terraform apply` on modules with variables, and test script output against containers. -The testing suite must be able to run docker containers with the `--network=host` flag, which typically requires running the tests on linux as this flag does not apply to Docker Desktop for MacOS and Windows. MacOS users can work around this by using something like [Orbstack](https://orbstack.dev/) instead of Docker Desktop. +The testing suite must be able to run docker containers with the `--network=host` flag, which typically requires running the tests on linux as this flag does not apply to Docker Desktop for MacOS and Windows. MacOS users can work around this by using something like [Orbstack](https://orbstack.dev/) instead of Docker Desktop. Reference existing `*.test.ts` files for implementation. diff --git a/github-upload-public-key/main.test.ts b/github-upload-public-key/main.test.ts index 8b6dca7..b4e83a5 100644 --- a/github-upload-public-key/main.test.ts +++ b/github-upload-public-key/main.test.ts @@ -1,5 +1,14 @@ import { describe, expect, it } from "bun:test"; -import { createJSONResponse, execContainer, findResourceInstance, runContainer, runTerraformApply, runTerraformInit, testRequiredVariables, writeCoder } from "../test"; +import { + createJSONResponse, + execContainer, + findResourceInstance, + runContainer, + runTerraformApply, + runTerraformInit, + testRequiredVariables, + writeCoder, +} from "../test"; import { Server, serve } from "bun"; describe("github-upload-public-key", async () => { @@ -13,7 +22,7 @@ describe("github-upload-public-key", async () => { const { instance, id } = await setupContainer(); await writeCoder(id, "echo foo"); let exec = await execContainer(id, ["bash", "-c", instance.script]); - expect(exec.stdout).toContain("Coder public SSH key uploaded to GitHub!") + expect(exec.stdout).toContain("Coder public SSH key uploaded to GitHub!"); expect(exec.exitCode).toBe(0); }); @@ -21,69 +30,81 @@ describe("github-upload-public-key", async () => { const { instance, id } = await setupContainer(); await writeCoder(id, "echo findkey"); let exec = await execContainer(id, ["bash", "-c", instance.script]); - expect(exec.stdout).toContain("Coder public SSH key is already uploaded to GitHub!") + expect(exec.stdout).toContain( + "Coder public SSH key is already uploaded to GitHub!", + ); expect(exec.exitCode).toBe(0); }); }); const setupContainer = async ( - image = "lorello/alpine-bash", - vars: Record = {}, - ) => { - const server = await setupServer(); - const state = await runTerraformApply(import.meta.dir, { - agent_id: "foo", - // trim the trailing slash on the URL - access_url: server.url.toString().slice(0, -1), - owner_session_token: "bar", - github_api_url: server.url.toString().slice(0, -1), - ...vars, - }); - const instance = findResourceInstance(state, "coder_script"); - const id = await runContainer(image); - return { id, instance }; + image = "lorello/alpine-bash", + vars: Record = {}, +) => { + const server = await setupServer(); + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + // trim the trailing slash on the URL + access_url: server.url.toString().slice(0, -1), + owner_session_token: "bar", + github_api_url: server.url.toString().slice(0, -1), + ...vars, + }); + const instance = findResourceInstance(state, "coder_script"); + const id = await runContainer(image); + return { id, instance }; }; const setupServer = async (): Promise => { - let url: URL; - const fakeSlackHost = serve({ - fetch: (req) => { - url = new URL(req.url); - if (url.pathname === "/api/v2/users/me/gitsshkey") { - return createJSONResponse({ - public_key: "exists", - }); - } - - if (url.pathname === "/user/keys") { - if (req.method === "POST") { - return createJSONResponse({ - key: "created", - }, 201); - } + let url: URL; + const fakeSlackHost = serve({ + fetch: (req) => { + url = new URL(req.url); + if (url.pathname === "/api/v2/users/me/gitsshkey") { + return createJSONResponse({ + public_key: "exists", + }); + } - // case: key already exists - if (req.headers.get("Authorization") == "Bearer findkey") { - return createJSONResponse([{ - key: "foo", - }, { - key: "exists", - }]); - } + if (url.pathname === "/user/keys") { + if (req.method === "POST") { + return createJSONResponse( + { + key: "created", + }, + 201, + ); + } - // case: key does not exist - return createJSONResponse([{ - key: "foo", - }]); + // case: key already exists + if (req.headers.get("Authorization") == "Bearer findkey") { + return createJSONResponse([ + { + key: "foo", + }, + { + key: "exists", + }, + ]); } + // case: key does not exist + return createJSONResponse([ + { + key: "foo", + }, + ]); + } - return createJSONResponse({ - error: "not_found" - }, 404); - }, - port: 0, - }); + return createJSONResponse( + { + error: "not_found", + }, + 404, + ); + }, + port: 0, + }); - return fakeSlackHost; -} \ No newline at end of file + return fakeSlackHost; +}; diff --git a/github-upload-public-key/main.tf b/github-upload-public-key/main.tf index 7d18ddb..5c6501e 100644 --- a/github-upload-public-key/main.tf +++ b/github-upload-public-key/main.tf @@ -23,7 +23,7 @@ variable "external_auth_id" { variable "github_api_url" { type = string description = "The URL of the GitHub instance." - default = "https://api.github.com" + default = "https://api.github.com" } // Optional variables mostly for testing purposes, will normally come from data.coder_workspace.me From 67fef297da32005afa624101a1c6c0afad0d5323 Mon Sep 17 00:00:00 2001 From: Garrett Delfosse Date: Wed, 1 May 2024 15:43:26 -0400 Subject: [PATCH 16/23] increase test timeout --- github-upload-public-key/main.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/github-upload-public-key/main.test.ts b/github-upload-public-key/main.test.ts index b4e83a5..5c71464 100644 --- a/github-upload-public-key/main.test.ts +++ b/github-upload-public-key/main.test.ts @@ -24,10 +24,12 @@ describe("github-upload-public-key", async () => { let exec = await execContainer(id, ["bash", "-c", instance.script]); expect(exec.stdout).toContain("Coder public SSH key uploaded to GitHub!"); expect(exec.exitCode).toBe(0); - }); + // we need to increase timeout to pull the container + }, 10000); it("does nothing if one already exists", async () => { const { instance, id } = await setupContainer(); + // use keyword to make server return a existing key await writeCoder(id, "echo findkey"); let exec = await execContainer(id, ["bash", "-c", instance.script]); expect(exec.stdout).toContain( From a239212f0b7afad4903b34eba9febf1f6e9f8718 Mon Sep 17 00:00:00 2001 From: Garrett Delfosse Date: Wed, 1 May 2024 15:45:25 -0400 Subject: [PATCH 17/23] fmt and increase timeout again --- github-upload-public-key/main.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/github-upload-public-key/main.test.ts b/github-upload-public-key/main.test.ts index 5c71464..45e1b5a 100644 --- a/github-upload-public-key/main.test.ts +++ b/github-upload-public-key/main.test.ts @@ -24,8 +24,8 @@ describe("github-upload-public-key", async () => { let exec = await execContainer(id, ["bash", "-c", instance.script]); expect(exec.stdout).toContain("Coder public SSH key uploaded to GitHub!"); expect(exec.exitCode).toBe(0); - // we need to increase timeout to pull the container - }, 10000); + // we need to increase timeout to pull the container + }, 15000); it("does nothing if one already exists", async () => { const { instance, id } = await setupContainer(); From daed80353076d51dee516922350b2d4f375b1358 Mon Sep 17 00:00:00 2001 From: Garrett Delfosse Date: Thu, 2 May 2024 12:50:36 -0400 Subject: [PATCH 18/23] pr review --- github-upload-public-key/main.test.ts | 32 ++++++++++++++++++------- github-upload-public-key/main.tf | 17 ++------------ github-upload-public-key/run.sh | 34 ++++++++------------------- 3 files changed, 35 insertions(+), 48 deletions(-) diff --git a/github-upload-public-key/main.test.ts b/github-upload-public-key/main.test.ts index 45e1b5a..316f50f 100644 --- a/github-upload-public-key/main.test.ts +++ b/github-upload-public-key/main.test.ts @@ -19,19 +19,37 @@ describe("github-upload-public-key", async () => { }); it("creates new key if one does not exist", async () => { - const { instance, id } = await setupContainer(); + const { instance, id, server } = await setupContainer(); await writeCoder(id, "echo foo"); - let exec = await execContainer(id, ["bash", "-c", instance.script]); + let exec = await execContainer(id, [ + "env", + "CODER_ACCESS_URL=" + server.url.toString().slice(0, -1), + "GITHUB_API_URL=" + server.url.toString().slice(0, -1), + "CODER_OWNER_SESSION_TOKEN=foo", + "CODER_EXTERNAL_AUTH_ID=github", + "bash", + "-c", + instance.script, + ]); expect(exec.stdout).toContain("Coder public SSH key uploaded to GitHub!"); expect(exec.exitCode).toBe(0); // we need to increase timeout to pull the container }, 15000); it("does nothing if one already exists", async () => { - const { instance, id } = await setupContainer(); + const { instance, id, server } = await setupContainer(); // use keyword to make server return a existing key await writeCoder(id, "echo findkey"); - let exec = await execContainer(id, ["bash", "-c", instance.script]); + let exec = await execContainer(id, [ + "env", + "CODER_ACCESS_URL=" + server.url.toString().slice(0, -1), + "GITHUB_API_URL=" + server.url.toString().slice(0, -1), + "CODER_OWNER_SESSION_TOKEN=foo", + "CODER_EXTERNAL_AUTH_ID=github", + "bash", + "-c", + instance.script, + ]); expect(exec.stdout).toContain( "Coder public SSH key is already uploaded to GitHub!", ); @@ -46,15 +64,11 @@ const setupContainer = async ( const server = await setupServer(); const state = await runTerraformApply(import.meta.dir, { agent_id: "foo", - // trim the trailing slash on the URL - access_url: server.url.toString().slice(0, -1), - owner_session_token: "bar", - github_api_url: server.url.toString().slice(0, -1), ...vars, }); const instance = findResourceInstance(state, "coder_script"); const id = await runContainer(image); - return { id, instance }; + return { id, instance, server }; }; const setupServer = async (): Promise => { diff --git a/github-upload-public-key/main.tf b/github-upload-public-key/main.tf index 5c6501e..b35d246 100644 --- a/github-upload-public-key/main.tf +++ b/github-upload-public-key/main.tf @@ -26,26 +26,13 @@ variable "github_api_url" { default = "https://api.github.com" } -// Optional variables mostly for testing purposes, will normally come from data.coder_workspace.me -variable "access_url" { - type = string - description = "The access URL of the workspace." - default = "" -} - -variable "owner_session_token" { - type = string - description = "The owner session token of the workspace." - default = "" -} - data "coder_workspace" "me" {} resource "coder_script" "github_upload_public_key" { agent_id = var.agent_id script = templatefile("${path.module}/run.sh", { - CODER_OWNER_SESSION_TOKEN : var.owner_session_token != "" ? var.owner_session_token : data.coder_workspace.me.owner_session_token, - CODER_ACCESS_URL : var.access_url != "" ? var.access_url : data.coder_workspace.me.access_url, + CODER_OWNER_SESSION_TOKEN : data.coder_workspace.me.owner_session_token, + CODER_ACCESS_URL : data.coder_workspace.me.access_url, CODER_EXTERNAL_AUTH_ID : var.external_auth_id, GITHUB_API_URL : var.github_api_url, }) diff --git a/github-upload-public-key/run.sh b/github-upload-public-key/run.sh index 6dabefa..9083040 100755 --- a/github-upload-public-key/run.sh +++ b/github-upload-public-key/run.sh @@ -2,44 +2,34 @@ set -e -CODER_ACCESS_URL="${CODER_ACCESS_URL}" -CODER_OWNER_SESSION_TOKEN="${CODER_OWNER_SESSION_TOKEN}" -CODER_EXTERNAL_AUTH_ID="${CODER_EXTERNAL_AUTH_ID}" -GITHUB_API_URL="${GITHUB_API_URL}" - if [ -z "$CODER_ACCESS_URL" ]; then - echo "No coder access url specified!" + echo "No CODER_ACCESS_URL specified!" exit 1 fi if [ -z "$CODER_OWNER_SESSION_TOKEN" ]; then - echo "No coder owner session token specified!" + echo "No CODER_OWNER_SESSION_TOKEN specified!" exit 1 fi if [ -z "$CODER_EXTERNAL_AUTH_ID" ]; then - echo "No GitHub external auth id specified!" + echo "No CODER_EXTERNAL_AUTH_ID specified!" exit 1 fi if [ -z "$GITHUB_API_URL" ]; then - echo "No GitHub API URL specified!" + echo "No GITHUB_API_URL specified!" exit 1 fi echo "Fetching GitHub token..." GITHUB_TOKEN=$(coder external-auth access-token $CODER_EXTERNAL_AUTH_ID) if [ $? -ne 0 ]; then - echo "Failed to fetch GitHub token!" - exit 1 -fi -if [ -z "$GITHUB_TOKEN" ]; then - echo "No GitHub token found!" + printf "Authenticate with Github to automatically upload Coder public key:\n$GITHUB_TOKEN\n" exit 1 fi -echo "GitHub token found!" -echo "Fetching Coder public SSH key..." +echo "Fetching public key from Coder..." PUBLIC_KEY_RESPONSE=$( curl -L -s \ -w "\n%%{http_code}" \ @@ -55,16 +45,13 @@ if [ "$PUBLIC_KEY_RESPONSE_STATUS" -ne 200 ]; then echo "$PUBLIC_KEY_BODY" exit 1 fi - PUBLIC_KEY=$(jq -r '.public_key' <<< "$PUBLIC_KEY_BODY") -echo "Coder public SSH key found!" - if [ -z "$PUBLIC_KEY" ]; then echo "No Coder public SSH key found!" exit 1 fi -echo "Fetching GitHub public SSH keys..." +echo "Fetching public keys from GitHub..." GITHUB_KEYS_RESPONSE=$( curl -L -s \ -w "\n%%{http_code}" \ @@ -85,12 +72,11 @@ fi GITHUB_MATCH=$(jq -r --arg PUBLIC_KEY "$PUBLIC_KEY" '.[] | select(.key == $PUBLIC_KEY) | .key' <<< "$GITHUB_KEYS_RESPONSE_BODY") if [ "$PUBLIC_KEY" = "$GITHUB_MATCH" ]; then - echo "Coder public SSH key is already uploaded to GitHub!" + echo "Your Coder public key is already on GitHub!" exit 0 fi -echo "Coder public SSH key not found in GitHub keys!" -echo "Uploading Coder public SSH key to GitHub..." +echo "Your Coder public key is not in GitHub. Adding it now..." CODER_PUBLIC_KEY_NAME="$CODER_ACCESS_URL Workspaces" UPLOAD_RESPONSE=$( curl -L -s \ @@ -111,4 +97,4 @@ if [ "$UPLOAD_RESPONSE_STATUS" -ne 201 ]; then exit 1 fi -echo "Coder public SSH key uploaded to GitHub!" +echo "Your Coder public key has been added to GitHub!" From 4bdb428244fac594131c7545891cde35796d4361 Mon Sep 17 00:00:00 2001 From: Garrett Delfosse Date: Thu, 2 May 2024 12:55:00 -0400 Subject: [PATCH 19/23] fix test --- github-upload-public-key/main.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/github-upload-public-key/main.test.ts b/github-upload-public-key/main.test.ts index 316f50f..d7ce193 100644 --- a/github-upload-public-key/main.test.ts +++ b/github-upload-public-key/main.test.ts @@ -31,7 +31,7 @@ describe("github-upload-public-key", async () => { "-c", instance.script, ]); - expect(exec.stdout).toContain("Coder public SSH key uploaded to GitHub!"); + expect(exec.stdout).toContain("Your Coder public key has been added to GitHub!"); expect(exec.exitCode).toBe(0); // we need to increase timeout to pull the container }, 15000); @@ -51,7 +51,7 @@ describe("github-upload-public-key", async () => { instance.script, ]); expect(exec.stdout).toContain( - "Coder public SSH key is already uploaded to GitHub!", + "Your Coder public key is already on GitHub!", ); expect(exec.exitCode).toBe(0); }); From 85e73c2071f815cfb921f7c47450071b43124ef6 Mon Sep 17 00:00:00 2001 From: Garrett Delfosse Date: Thu, 2 May 2024 12:55:52 -0400 Subject: [PATCH 20/23] fmt --- github-upload-public-key/main.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/github-upload-public-key/main.test.ts b/github-upload-public-key/main.test.ts index d7ce193..fb1b977 100644 --- a/github-upload-public-key/main.test.ts +++ b/github-upload-public-key/main.test.ts @@ -31,7 +31,9 @@ describe("github-upload-public-key", async () => { "-c", instance.script, ]); - expect(exec.stdout).toContain("Your Coder public key has been added to GitHub!"); + expect(exec.stdout).toContain( + "Your Coder public key has been added to GitHub!", + ); expect(exec.exitCode).toBe(0); // we need to increase timeout to pull the container }, 15000); From c068082e6b2b5e080e391d5570b40455dcc7f8e7 Mon Sep 17 00:00:00 2001 From: Garrett Delfosse Date: Thu, 2 May 2024 13:03:58 -0400 Subject: [PATCH 21/23] pr comments --- CONTRIBUTING.md | 2 +- github-upload-public-key/run.sh | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 749791f..557171e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,7 +10,7 @@ To create a new module, clone this repository and run: A suite of test-helpers exists to run `terraform apply` on modules with variables, and test script output against containers. -The testing suite must be able to run docker containers with the `--network=host` flag, which typically requires running the tests on linux as this flag does not apply to Docker Desktop for MacOS and Windows. MacOS users can work around this by using something like [Orbstack](https://orbstack.dev/) instead of Docker Desktop. +The testing suite must be able to run docker containers with the `--network=host` flag, which typically requires running the tests on Linux as this flag does not apply to Docker Desktop for MacOS and Windows. MacOS users can work around this by using something like [colima](https://github.com/abiosoft/colima) or [Orbstack](https://orbstack.dev/) instead of Docker Desktop. Reference existing `*.test.ts` files for implementation. diff --git a/github-upload-public-key/run.sh b/github-upload-public-key/run.sh index 9083040..da39e10 100755 --- a/github-upload-public-key/run.sh +++ b/github-upload-public-key/run.sh @@ -3,22 +3,22 @@ set -e if [ -z "$CODER_ACCESS_URL" ]; then - echo "No CODER_ACCESS_URL specified!" + echo "CODER_ACCESS_URL is empty!" exit 1 fi if [ -z "$CODER_OWNER_SESSION_TOKEN" ]; then - echo "No CODER_OWNER_SESSION_TOKEN specified!" + echo "CODER_OWNER_SESSION_TOKEN is empty!" exit 1 fi if [ -z "$CODER_EXTERNAL_AUTH_ID" ]; then - echo "No CODER_EXTERNAL_AUTH_ID specified!" + echo "CODER_EXTERNAL_AUTH_ID is empty!" exit 1 fi if [ -z "$GITHUB_API_URL" ]; then - echo "No GITHUB_API_URL specified!" + echo "GITHUB_API_URL is empty!" exit 1 fi From 282e1f8c57efaecee9ff1e3f54342a77212856f2 Mon Sep 17 00:00:00 2001 From: Garrett Delfosse Date: Thu, 2 May 2024 14:21:01 -0400 Subject: [PATCH 22/23] take env and then interpolate --- github-upload-public-key/run.sh | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/github-upload-public-key/run.sh b/github-upload-public-key/run.sh index da39e10..2fb8d26 100755 --- a/github-upload-public-key/run.sh +++ b/github-upload-public-key/run.sh @@ -3,23 +3,35 @@ set -e if [ -z "$CODER_ACCESS_URL" ]; then - echo "CODER_ACCESS_URL is empty!" - exit 1 + if [ -z "${CODER_ACCESS_URL}" ]; then + echo "CODER_ACCESS_URL is empty!" + exit 1 + fi + CODER_ACCESS_URL=${CODER_ACCESS_URL} fi if [ -z "$CODER_OWNER_SESSION_TOKEN" ]; then - echo "CODER_OWNER_SESSION_TOKEN is empty!" - exit 1 + if [ -z "${CODER_OWNER_SESSION_TOKEN}" ]; then + echo "CODER_OWNER_SESSION_TOKEN is empty!" + exit 1 + fi + CODER_OWNER_SESSION_TOKEN=${CODER_OWNER_SESSION_TOKEN} fi if [ -z "$CODER_EXTERNAL_AUTH_ID" ]; then - echo "CODER_EXTERNAL_AUTH_ID is empty!" - exit 1 + if [ -z "${CODER_EXTERNAL_AUTH_ID}" ]; then + echo "CODER_EXTERNAL_AUTH_ID is empty!" + exit 1 + fi + CODER_EXTERNAL_AUTH_ID=${CODER_EXTERNAL_AUTH_ID} fi if [ -z "$GITHUB_API_URL" ]; then - echo "GITHUB_API_URL is empty!" - exit 1 + if [ -z "${GITHUB_API_URL}" ]; then + echo "GITHUB_API_URL is empty!" + exit 1 + fi + GITHUB_API_URL=${GITHUB_API_URL} fi echo "Fetching GitHub token..." From 873207fddf90a6468da0663c939d5bf0786f1a59 Mon Sep 17 00:00:00 2001 From: Garrett Delfosse Date: Thu, 2 May 2024 14:33:08 -0400 Subject: [PATCH 23/23] remove set -e --- github-upload-public-key/run.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/github-upload-public-key/run.sh b/github-upload-public-key/run.sh index 2fb8d26..a382a40 100755 --- a/github-upload-public-key/run.sh +++ b/github-upload-public-key/run.sh @@ -1,7 +1,5 @@ #!/usr/bin/env bash -set -e - if [ -z "$CODER_ACCESS_URL" ]; then if [ -z "${CODER_ACCESS_URL}" ]; then echo "CODER_ACCESS_URL is empty!"