8000 Adding basic test for prebuilds, abstracting template import · coder/coder@8f051e6 · GitHub
[go: up one dir, main page]

Skip to content
8000

Commit 8f051e6

Browse files
committed
Adding basic test for prebuilds, abstracting template import
Signed-off-by: Danny Kopping <danny@coder.com>
1 parent d6515ae commit 8f051e6

File tree

8 files changed

+336
-83
lines changed

8 files changed

+336
-83
lines changed

site/e2e/helpers.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { type ChildProcess, exec, spawn } from "node:child_process";
22
import { randomUUID } from "node:crypto";
3+
import fs from "node:fs";
34
import net from "node:net";
5+
import * as os from "node:os";
46
import path from "node:path";
57
import { Duplex } from "node:stream";
68
import { type BrowserContext, type Page, expect, test } from "@playwright/test";
@@ -10,6 +12,7 @@ import type {
1012
WorkspaceBuildParameter,
1113
} from "api/typesGenerated";
1214
import express from "express";
15+
import JSZip from "jszip";
1316
import capitalize from "lodash/capitalize";
1417
import * as ssh from "ssh2";
1518
import { TarWriter } from "utils/tar";
@@ -1127,3 +1130,84 @@ export async function createOrganization(page: Page): Promise<{
11271130

11281131
return { name, displayName, description };
11291132
}
1133+
1134+
// TODO: convert to test fixture and dispose after each test.
1135+
export async function importTemplate(
1136+
page: Page,
1137+
templateName: string,
1138+
files: string[],
1139+
orgName = defaultOrganizationName,
1140+
): Promise<string> {
1141+
// Create a ZIP from the given input files.
1142+
const tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), templateName));
1143+
const templatePath = path.join(tmpdir, `${templateName}.zip`);
1144+
await createZIP(templatePath, files);
1145+
1146+
// Create new template.
1 6D40 147+
await page.goto("/templates/new", { waitUntil: "domcontentloaded" });
1148+
await page.getByTestId("drop-zone").click();
1149+
1150+
// Select the template file.
1151+
const [fileChooser] = await Promise.all([
1152+
page.waitForEvent("filechooser"),
1153+
page.getByTestId("drop-zone").click(),
1154+
]);
1155+
await fileChooser.setFiles(templatePath);
1156+
1157+
// Set name and submit.
1158+
await page.locator("input[name=name]").fill(templateName);
1159+
1160+
// If the organization picker is present on the page, select the default
1161+
// organization.
1162+
const orgPicker = page.getByLabel("Belongs to *");
1163+
const organizationsEnabled = await orgPicker.isVisible();
1164+
if (organizationsEnabled) {
1165+
if (orgName !== defaultOrganizationName) {
1166+
throw new Error(
1167+
`No provisioners registered for ${orgName}, creating this template will fail`,
1168+
);
1169+
}
1170+
1171+
await orgPicker.click();
1172+
await page.getByText(orgName, { exact: true }).click();
1173+
}
1174+
1175+
await page.getByRole("button", { name: "Save" }).click();
1176+
1177+
await page.waitForURL(`/templates/${orgName}/${templateName}/files`, {
1178+
timeout: 120_000,
1179+
});
1180+
return templateName;
1181+
}
1182+
1183+
async function createZIP(
1184+
outpath: string,
1185+
inputFiles: string[],
1186+
): Promise<{ path: string; length: number }> {
1187+
const zip = new JSZip();
1188+
1189+
let found = false;
1190+
for (const file of inputFiles) {
1191+
if (!fs.existsSync(file)) {
1192+
console.warn(`${file} not found, not including in zip`);
1193+
continue;
1194+
}
1195+
found = true;
1196+
1197+
const contents = fs.readFileSync(file);
1198+
zip.file(path.basename(file), contents);
1199+
}
1200+
1201+
if (!found) {
1202+
throw new Error(`no files found to zip into ${outpath}`);
1203+
}
1204+
1205+
zip
1206+
.generateNodeStream({ type: "nodebuffer", streamFiles: true })
1207+
.pipe(fs.createWriteStream(outpath));
1208+
1209+
return {
1210+
path: outpath,
1211+
length: zip.length,
1212+
};
1213+
}

site/e2e/playwright.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ export default defineConfig({
111111
gitAuth.validatePath,
112112
),
113113
CODER_PPROF_ADDRESS: `127.0.0.1:${coderdPProfPort}`,
114-
CODER_EXPERIMENTS: `${e2eFakeExperiment1},${e2eFakeExperiment2}`,
114+
CODER_EXPERIMENTS: `${e2eFakeExperiment1},${e2eFakeExperiment2},${process.env.CODER_EXPERIMENTS}`,
115115

116116
// Tests for Deployment / User Authentication / OIDC
117117
CODER_OIDC_ISSUER_URL: "https://accounts.google.com",

site/e2e/tests/presets/template/main.tf renamed to site/e2e/tests/presets/basic-presets-with-prebuild/main.tf

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
terraform {
22
required_providers {
33
coder = {
4-
source = "coder/coder"
4+
source = "coder/coder"
55
version = "2.1.3"
66
}
77
docker = {
8-
source = "kreuzwerker/docker"
8+
source = "kreuzwerker/docker"
99
version = "3.0.2"
1010
}
1111
}
@@ -66,9 +66,9 @@ resource "coder_agent" "main" {
6666
# You can remove this block if you'd prefer to configure Git manually or using
6767
# dotfiles. (see docs/dotfiles.md)
6868
env = {
69-
GIT_AUTHOR_NAME = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name)
69+
GIT_AUTHOR_NAME = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name)
7070
GIT_AUTHOR_EMAIL = "${data.coder_workspace_owner.me.email}"
71-
GIT_COMMITTER_NAME = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name)
71+
GIT_COMMITTER_NAME = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name)
7272
GIT_COMMITTER_EMAIL = "${data.coder_workspace_owner.me.email}"
7373
}
7474

@@ -96,12 +96,12 @@ resource "coder_agent" "main" {
9696

9797
# See https://registry.coder.com/modules/jetbrains-gateway
9898
module "jetbrains_gateway" {
99-
count = data.coder_workspace.me.start_count
99+
count = data.coder_workspace.me.start_count
100100
source = "registry.coder.com/modules/jetbrains-gateway/coder"
101101

102102
# JetBrains IDEs to make available for the user to select
103103
jetbrains_ides = ["IU", "PY", "WS", "PS", "RD", "CL", "GO", "RM"]
104-
default = "IU"
104+
default = "IU"
105105

106106
# Default folder to open when starting a JetBrains IDE
107107
folder = "/home/coder"
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
terraform {
2+
required_providers {
3+
coder = {
4+
source = "coder/coder"
5+
version = "2.1.3"
6+
}
7+
docker = {
8+
source = "kreuzwerker/docker"
9+
version = "3.0.2"
10+
}
11+
}
12+
}
13+
14+
variable "docker_socket" {
15+
default = ""
16+
description = "(Optional) Docker socket URI"
17+
type = string
18+
}
19+
20+
provider "docker" {
21+
# Defaulting to null if the variable is an empty string lets us have an optional variable without having to set our own default
22+
host = var.docker_socket != "" ? var.docker_socket : null
23+
}
24+
25+
data "coder_provisioner" "me" {}
26+
data "coder_workspace" "me" {}
27+
data "coder_workspace_owner" "me" {}
28+
29+
data "coder_workspace_preset" "goland" {
30+
name = "I Like GoLand"
31+
parameters = {
32+
"jetbrains_ide" = "GO"
33+
}
34+
}
35+
36+
data "coder_workspace_preset" "python" {
37+
name = "Some Like PyCharm"
38+
parameters = {
39+
"jetbrains_ide" = "PY"
40+
}
41+
}
42+
43+
resource "coder_agent" "main" {
44+
arch = data.coder_provisioner.me.arch
45+
os = "linux"
46+
startup_script = <<-EOT
47+
set -e
48+
49+
# Prepare user home with default files on first start!
50+
if [ ! -f ~/.init_done ]; then
51+
cp -rT /etc/skel ~
52+
touch ~/.init_done
53+
fi
54+
55+
# Add any commands that should be executed at workspace startup (e.g install requirements, start a program, etc) here
56+
EOT
57+
58+
# These environment variables allow you to make Git commits right away after creating a
59+
# workspace. Note that they take precedence over configuration defined in ~/.gitconfig!
60+
# You can remove this block if you'd prefer to configure Git manually or using
61+
# dotfiles. (see docs/dotfiles.md)
62+
env = {
63+
GIT_AUTHOR_NAME = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name)
64+
GIT_AUTHOR_EMAIL = "${data.coder_workspace_owner.me.email}"
65+
GIT_COMMITTER_NAME = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name)
66+
GIT_COMMITTER_EMAIL = "${data.coder_workspace_owner.me.email}"
67+
}
68+
69+
# The following metadata blocks are optional. They are used to display
70+
# information about your workspace in the dashboard. You can remove them
71+
# if you don't want to display any information.
72+
# For basic resources, you can use the `coder stat` command.
73+
# If you need more control, you can write your own script.
74+
metadata {
75+
display_name = "Is Prebuild"
76+
key = "prebuild"
77+
script = "echo ${data.coder_workspace.me.prebuild_count}"
78+
interval = 10
79+
timeout = 1
80+
}
81+
82+
metadata {
83+
display_name = "Hostname"
84+
key = "hostname"
85+
script = "hostname"
86+
interval = 10
87+
timeout = 1
88+
}
89+
}
90+
91+
# See https://registry.coder.com/modules/jetbrains-gateway
92+
module "jetbrains_gateway" {
93+
count = data.coder_workspace.me.start_count
94+
source = "registry.coder.com/modules/jetbrains-gateway/coder"
95+
96+
# JetBrains IDEs to make available for the user to select
97+
jetbrains_ides = ["IU", "PY", "WS", "PS", "RD", "CL", "GO", "RM"]
98+
default = "IU"
99+
100+
# Default folder to open when starting a JetBrains IDE
101+
folder = "/home/coder"
102+
103+
# This ensures that the latest version of the module gets downloaded, you can also pin the module version to prevent breaking changes in production.
104+
version = ">= 1.0.0"
105+
106+
agent_id = coder_agent.main.id
107+
agent_name = "main"
108+
order = 2
109+
}
110+
111+
resource "docker_volume" "home_volume" {
112+
name = "coder-${data.coder_workspace.me.id}-home"
113+
# Protect the volume from being deleted due to changes in attributes.
114+
lifecycle {
115+
ignore_changes = all
116+
}
117+
}
118+
119+
resource "docker_container" "workspace" {
120+
lifecycle {
121+
ignore_changes = all
122+
}
123+
124+
network_mode = "host"
125+
126+
count = data.coder_workspace.me.start_count
127+
image = "codercom/enterprise-base:ubuntu"
128+
# Uses lower() to avoid Docker restriction on container names.
129+
name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}"
130+
# Hostname makes the shell more user friendly: coder@my-workspace:~$
131+
hostname = data.coder_workspace.me.name
132+
# Use the docker gateway if the access URL is 127.0.0.1
133+
entrypoint = [
134+
"sh", "-c", replace(coder_agent.main.init_script, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal")
135+
]
136+
env = ["CODER_AGENT_TOKEN=${coder_agent.main.token}"]
137+
host {
138+
host = "host.docker.internal"
139+
ip = "host-gateway"
140+
}
141+
volumes {
142+
container_path = "/home/coder"
143+
volume_name = docker_volume.home_volume.name
144+
read_only = false
145+
}
146+
}

site/e2e/tests/presets/createPreset.spec.ts

Lines changed: 0 additions & 76 deletions
This file was deleted.

0 commit comments

Comments
 (0)
0