Improve portability

pull/85/head
Kyle Carberry 2 years ago
parent 751f431767
commit 610947eefb

@ -0,0 +1,94 @@
import { describe, expect, it } from "bun:test";
import {
createJSONResponse,
execContainer,
executeScriptInContainer,
findResourceInstance,
runContainer,
runTerraformApply,
runTerraformInit,
testRequiredVariables,
} from "../test";
import { serve } from "bun";
describe("slackme", async () => {
await runTerraformInit(import.meta.dir);
testRequiredVariables(import.meta.dir, {
agent_id: "foo",
auth_provider_id: "foo",
});
const setupContainer = async (image = "alpine") => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "foo",
auth_provider_id: "foo",
});
const instance = findResourceInstance(state, "coder_script");
const id = await runContainer(image);
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);
};
it("writes to path as executable", async () => {
const { instance, id } = await setupContainer();
await writeCoder(id, "exit 0");
let exec = await execContainer(id, ["sh", "-c", instance.script]);
expect(exec.exitCode).toBe(0);
exec = await execContainer(id, ["sh", "-c", "which slackme"]);
expect(exec.exitCode).toBe(0);
expect(exec.stdout.trim()).toEqual("/usr/bin/slackme");
});
it("prints usage with no command", async () => {
const { instance, id } = await setupContainer();
await writeCoder(id, "echo 👋");
let exec = await execContainer(id, ["sh", "-c", instance.script]);
expect(exec.exitCode).toBe(0);
exec = await execContainer(id, ["sh", "-c", "slackme"]);
expect(exec.stdout.trim()).toStartWith(
"slackme — Send a Slack notification when a command finishes"
);
});
it("displays url when not authenticated", async () => {
const { instance, id } = await setupContainer();
await writeCoder(id, "echo 'some-url' && exit 1");
let exec = await execContainer(id, ["sh", "-c", instance.script]);
expect(exec.exitCode).toBe(0);
exec = await execContainer(id, ["sh", "-c", "slackme echo test"]);
expect(exec.stdout.trim()).toEndWith("some-url");
});
it("curls url when authenticated", async () => {
let url: URL
const fakeSlackHost = serve({
fetch: (req) => {
url = new URL(req.url);
if (url.pathname === "/api/chat.postMessage")
return createJSONResponse({
ok: true,
});
return createJSONResponse({}, 404);
},
port: 0,
});
const { instance, id } = await setupContainer("alpine/curl");
await writeCoder(id, "echo 'token'");
let exec = await execContainer(id, ["sh", "-c", instance.script]);
expect(exec.exitCode).toBe(0);
exec = await execContainer(id, ["sh", "-c", `SLACK_URL="http://${fakeSlackHost.hostname}:${fakeSlackHost.port}" slackme echo test`]);
expect(exec.stdout.trim()).toEndWith("test");
expect(url.pathname).toEqual("/api/chat.postMessage");
expect(url.searchParams.get("channel")).toEqual("token");
});
});

@ -19,19 +19,28 @@ variable "auth_provider_id" {
description = "The ID of an external auth provider." description = "The ID of an external auth provider."
} }
variable "slack_message" {
type = string
description = "The message to send to Slack."
default = "Your command completed!"
}
resource "coder_script" "install_slackme" { resource "coder_script" "install_slackme" {
agent_id = var.agent_id agent_id = var.agent_id
display_name = "install_slackme" display_name = "install_slackme"
run_on_start = true run_on_start = true
script = <<OUTER script = <<OUTER
#!/usr/bin/env bash #!/usr/bin/env bash
set -e
CODER_DIR=$(dirname $(which coder)) CODER_DIR=$(dirname $(which coder))
cat > $CODER_DIR/slackme <<INNER cat > $CODER_DIR/slackme <<INNER
${templatefile("${path.module}/slackme.sh", { ${replace(templatefile("${path.module}/slackme.sh", {
PROVIDER_ID: var.auth_provider_id PROVIDER_ID : var.auth_provider_id,
})} SLACK_MESSAGE : urlencode(var.slack_message),
}), "$", "\\$")}
INNER INNER
chmod +x $CODER_DIR/slackme chmod +x $CODER_DIR/slackme
OUTER OUTER
} }

@ -1,28 +1,38 @@
#!/usr/bin/env node #!/usr/bin/env sh
PROVIDER_ID=${PROVIDER_ID} PROVIDER_ID=${PROVIDER_ID}
SLACK_MESSAGE="${SLACK_MESSAGE}"
SLACK_URL=$${SLACK_URL:-https://slack.com}
BOT_TOKEN=\$(coder external-auth access-token $PROVIDER_ID) usage() {
cat <<EOF
slackme — Send a Slack notification when a command finishes
Usage: slackme <command>
if [ $? -ne 0 ]; then Example: slackme npm run long-build
echo "Authenticate to run commands in the background:" EOF
# The output contains the URL if failed. }
echo $BOT_TOKEN
if [ $# -eq 0 ]; then
usage
exit 1 exit 1
fi fi
USER_ID=\$(coder external-auth access-token $PROVIDER_ID --extra "authed_user.id") BOT_TOKEN=$(coder external-auth access-token $PROVIDER_ID)
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "Failed to get authenticated user ID:" printf "Authenticate with Slack to be notified when a command finishes:\n$BOT_TOKEN\n"
echo $USER_ID
exit 1 exit 1
fi fi
echo "We'll notify you when done!" USER_ID=$(coder external-auth access-token $PROVIDER_ID --extra "authed_user.id")
if [ $? -ne 0 ]; then
printf "Failed to get authenticated user ID:\n$USER_ID\n"
exit 1
fi
# Run all arguments as a command # Run all arguments as a command
$@ $@
set -e
curl --silent -o /dev/null --header "Authorization: Bearer $BOT_TOKEN" \ curl --silent -o /dev/null --header "Authorization: Bearer $BOT_TOKEN" \
"https://slack.com/api/chat.postMessage?channel=$USER_ID&text=Your%20command%20finished!&pretty=1" "$SLACK_URL/api/chat.postMessage?channel=$USER_ID&text=$SLACK_MESSAGE&pretty=1"

@ -13,6 +13,8 @@ export const runContainer = async (
"-d", "-d",
"--label", "--label",
"modules-test=true", "modules-test=true",
"--network",
"host",
"--entrypoint", "--entrypoint",
"sh", "sh",
image, image,

Loading…
Cancel
Save