Skip to content

Commit d6515ae

Browse files
committed
Add integration test for creating and using presets from scratch
Signed-off-by: Danny Kopping <[email protected]>
1 parent 5e32ed2 commit d6515ae

File tree

6 files changed

+235
-6
lines changed

6 files changed

+235
-6
lines changed

coderd/prebuilds/api.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@ package prebuilds
33
import (
44
"context"
55

6-
"github.com/google/uuid"
7-
"golang.org/x/xerrors"
8-
96
"github.com/coder/coder/v2/coderd/database"
7+
"github.com/google/uuid"
108
)
119

1210
type Claimer interface {
@@ -17,7 +15,8 @@ type Claimer interface {
1715
type AGPLPrebuildClaimer struct{}
1816

1917
func (c AGPLPrebuildClaimer) Claim(context.Context, database.Store, uuid.UUID, string, uuid.UUID) (*uuid.UUID, error) {
20-
return nil, xerrors.Errorf("not entitled to claim prebuilds")
18+
// Not entitled to claim prebuilds in AGPL version.
19+
return nil, nil
2120
}
2221

2322
func (c AGPLPrebuildClaimer) Initiator() uuid.UUID {

site/e2e/playwright.config.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ export default defineConfig({
122122
CODER_OIDC_SIGN_IN_TEXT: "Hello",
123123
CODER_OIDC_ICON_URL: "/icon/google.svg",
124124
},
125-
reuseExistingServer: false,
125+
reuseExistingServer: process.env.CODER_E2E_REUSE_EXISTING_SERVER
126+
? Boolean(process.env.CODER_E2E_REUSE_EXISTING_SERVER)
127+
: false,
126128
},
127129
});

site/e2e/setup/preflight.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export default function () {
3636
throw new Error(msg);
3737
}
3838

39-
if (!process.env.CI) {
39+
if (!process.env.CI && !process.env.CODER_E2E_REUSE_EXISTING_SERVER) {
4040
console.info("==> make site/e2e/bin/coder");
4141
execSync("make site/e2e/bin/coder", {
4242
cwd: path.join(__dirname, "../../../"),
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import {expect, test} from "@playwright/test";
2+
import {currentUser, login} from "../../helpers";
3+
import {beforeCoderTest} from "../../hooks";
4+
import path from "node:path";
5+
6+
test.beforeEach(async ({page}) => {
7+
beforeCoderTest(page);
8+
await login(page);
9+
});
10+
11+
test("create template with preset and use in workspace", async ({page, baseURL}) => {
12+
test.setTimeout(120_000);
13+
14+
// Create new template.
15+
await page.goto('/templates/new', {waitUntil: 'domcontentloaded'});
16+
await page.getByTestId('drop-zone').click();
17+
18+
// Select the template file.
19+
const [fileChooser] = await Promise.all([
20+
page.waitForEvent('filechooser'),
21+
page.getByTestId('drop-zone').click()
22+
]);
23+
await fileChooser.setFiles(path.join(__dirname, 'template.zip'));
24+
25+
// Set name and submit.
26+
const templateName = generateRandomName();
27+
await page.locator("input[name=name]").fill(templateName);
28+
await page.getByRole('button', {name: 'Save'}).click();
29+
30+
await page.waitForURL(`/templates/${templateName}/files`, {
31+
timeout: 120_000,
32+
});
33+
34+
// Visit workspace creation page for new template.
35+
await page.goto(`/templates/default/${templateName}/workspace`, {waitUntil: 'domcontentloaded'});
36+
37+
await page.locator('button[aria-label="Preset"]').click();
38+
39+
const preset1 = page.getByText('I Like GoLand');
40+
const preset2 = page.getByText('Some Like PyCharm');
41+
42+
await expect(preset1).toBeVisible();
43+
await expect(preset2).toBeVisible();
44+
45+
// Choose the GoLand preset.
46+
await preset1.click();
47+
48+
// Validate the preset was applied correctly.
49+
await expect(page.locator('input[value="GO"]')).toBeChecked();
50+
51+
// Create a workspace.
52+
const workspaceName = generateRandomName();
53+
await page.locator("input[name=name]").fill(workspaceName);
54+
await page.getByRole('button', {name: 'Create workspace'}).click();
55+
56+
// Wait for the workspace build display to be navigated to.
57+
const user = currentUser(page);
58+
await page.waitForURL(`/@${user.username}/${workspaceName}`, {
59+
timeout: 120_000, // Account for workspace build time.
60+
});
61+
62+
// Visit workspace settings page.
63+
await page.goto(`/@${user.username}/${workspaceName}/settings/parameters`);
64+
65+
// Validate the preset was applied correctly.
66+
await expect(page.locator('input[value="GO"]')).toBeChecked();
67+
});
68+
69+
function generateRandomName() {
70+
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
71+
let name = '';
72+
for (let i = 0; i < 10; i++) {
73+
name += chars.charAt(Math.floor(Math.random() * chars.length));
74+
}
75+
return name;
76+
}

site/e2e/tests/presets/template.zip

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

0 commit comments

Comments
 (0)