Add integration test for creating and using presets from scratch

Signed-off-by: Danny Kopping <danny@coder.com>
This commit is contained in:
Danny Kopping
2025-02-26 14:55:48 +02:00
parent 5e32ed22ac
commit d6515aea91
6 changed files with 235 additions and 6 deletions

View File

@ -3,10 +3,8 @@ package prebuilds
import ( import (
"context" "context"
"github.com/google/uuid"
"golang.org/x/xerrors"
"github.com/coder/coder/v2/coderd/database" "github.com/coder/coder/v2/coderd/database"
"github.com/google/uuid"
) )
type Claimer interface { type Claimer interface {
@ -17,7 +15,8 @@ type Claimer interface {
type AGPLPrebuildClaimer struct{} type AGPLPrebuildClaimer struct{}
func (c AGPLPrebuildClaimer) Claim(context.Context, database.Store, uuid.UUID, string, uuid.UUID) (*uuid.UUID, error) { func (c AGPLPrebuildClaimer) Claim(context.Context, database.Store, uuid.UUID, string, uuid.UUID) (*uuid.UUID, error) {
return nil, xerrors.Errorf("not entitled to claim prebuilds") // Not entitled to claim prebuilds in AGPL version.
return nil, nil
} }
func (c AGPLPrebuildClaimer) Initiator() uuid.UUID { func (c AGPLPrebuildClaimer) Initiator() uuid.UUID {

View File

@ -122,6 +122,8 @@ export default defineConfig({
CODER_OIDC_SIGN_IN_TEXT: "Hello", CODER_OIDC_SIGN_IN_TEXT: "Hello",
CODER_OIDC_ICON_URL: "/icon/google.svg", CODER_OIDC_ICON_URL: "/icon/google.svg",
}, },
reuseExistingServer: false, reuseExistingServer: process.env.CODER_E2E_REUSE_EXISTING_SERVER
? Boolean(process.env.CODER_E2E_REUSE_EXISTING_SERVER)
: false,
}, },
}); });

View File

@ -36,7 +36,7 @@ export default function () {
throw new Error(msg); throw new Error(msg);
} }
if (!process.env.CI) { if (!process.env.CI && !process.env.CODER_E2E_REUSE_EXISTING_SERVER) {
console.info("==> make site/e2e/bin/coder"); console.info("==> make site/e2e/bin/coder");
execSync("make site/e2e/bin/coder", { execSync("make site/e2e/bin/coder", {
cwd: path.join(__dirname, "../../../"), cwd: path.join(__dirname, "../../../"),

View File

@ -0,0 +1,76 @@
import {expect, test} from "@playwright/test";
import {currentUser, login} from "../../helpers";
import {beforeCoderTest} from "../../hooks";
import path from "node:path";
test.beforeEach(async ({page}) => {
beforeCoderTest(page);
await login(page);
});
test("create template with preset and use in workspace", async ({page, baseURL}) => {
test.setTimeout(120_000);
// Create new template.
await page.goto('/templates/new', {waitUntil: 'domcontentloaded'});
await page.getByTestId('drop-zone').click();
// Select the template file.
const [fileChooser] = await Promise.all([
page.waitForEvent('filechooser'),
page.getByTestId('drop-zone').click()
]);
await fileChooser.setFiles(path.join(__dirname, 'template.zip'));
// Set name and submit.
const templateName = generateRandomName();
await page.locator("input[name=name]").fill(templateName);
await page.getByRole('button', {name: 'Save'}).click();
await page.waitForURL(`/templates/${templateName}/files`, {
timeout: 120_000,
});
// Visit workspace creation page for new template.
await page.goto(`/templates/default/${templateName}/workspace`, {waitUntil: 'domcontentloaded'});
await page.locator('button[aria-label="Preset"]').click();
const preset1 = page.getByText('I Like GoLand');
const preset2 = page.getByText('Some Like PyCharm');
await expect(preset1).toBeVisible();
await expect(preset2).toBeVisible();
// Choose the GoLand preset.
await preset1.click();
// Validate the preset was applied correctly.
await expect(page.locator('input[value="GO"]')).toBeChecked();
// Create a workspace.
const workspaceName = generateRandomName();
await page.locator("input[name=name]").fill(workspaceName);
await page.getByRole('button', {name: 'Create workspace'}).click();
// Wait for the workspace build display to be navigated to.
const user = currentUser(page);
await page.waitForURL(`/@${user.username}/${workspaceName}`, {
timeout: 120_000, // Account for workspace build time.
});
// Visit workspace settings page.
await page.goto(`/@${user.username}/${workspaceName}/settings/parameters`);
// Validate the preset was applied correctly.
await expect(page.locator('input[value="GO"]')).toBeChecked();
});
function generateRandomName() {
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
let name = '';
for (let i = 0; i < 10; i++) {
name += chars.charAt(Math.floor(Math.random() * chars.length));
}
return name;
}

Binary file not shown.

View File

@ -0,0 +1,152 @@
terraform {
required_providers {
coder = {
source = "coder/coder"
version = "2.1.3"
}
docker = {
source = "kreuzwerker/docker"
version = "3.0.2"
}
}
}
variable "docker_socket" {
default = ""
description = "(Optional) Docker socket URI"
type = string
}
provider "docker" {
# Defaulting to null if the variable is an empty string lets us have an optional variable without having to set our own default
host = var.docker_socket != "" ? var.docker_socket : null
}
data "coder_provisioner" "me" {}
data "coder_workspace" "me" {}
data "coder_workspace_owner" "me" {}
data "coder_workspace_preset" "goland" {
name = "I Like GoLand"
parameters = {
"jetbrains_ide" = "GO"
}
prebuilds {
instances = 1
}
}
data "coder_workspace_preset" "python" {
name = "Some Like PyCharm"
parameters = {
"jetbrains_ide" = "PY"
}
prebuilds {
instances = 2
}
}
resource "coder_agent" "main" {
arch = data.coder_provisioner.me.arch
os = "linux"
startup_script = <<-EOT
set -e
# Prepare user home with default files on first start!
if [ ! -f ~/.init_done ]; then
cp -rT /etc/skel ~
touch ~/.init_done
fi
# Add any commands that should be executed at workspace startup (e.g install requirements, start a program, etc) here
EOT
# These environment variables allow you to make Git commits right away after creating a
# workspace. Note that they take precedence over configuration defined in ~/.gitconfig!
# You can remove this block if you'd prefer to configure Git manually or using
# dotfiles. (see docs/dotfiles.md)
env = {
GIT_AUTHOR_NAME = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name)
GIT_AUTHOR_EMAIL = "${data.coder_workspace_owner.me.email}"
GIT_COMMITTER_NAME = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name)
GIT_COMMITTER_EMAIL = "${data.coder_workspace_owner.me.email}"
}
# The following metadata blocks are optional. They are used to display
# information about your workspace in the dashboard. You can remove them
# if you don't want to display any information.
# For basic resources, you can use the `coder stat` command.
# If you need more control, you can write your own script.
metadata {
display_name = "Is Prebuild"
key = "prebuild"
script = "echo ${data.coder_workspace.me.prebuild_count}"
interval = 10
timeout = 1
}
metadata {
display_name = "Hostname"
key = "hostname"
script = "hostname"
interval = 10
timeout = 1
}
}
# See https://registry.coder.com/modules/jetbrains-gateway
module "jetbrains_gateway" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/modules/jetbrains-gateway/coder"
# JetBrains IDEs to make available for the user to select
jetbrains_ides = ["IU", "PY", "WS", "PS", "RD", "CL", "GO", "RM"]
default = "IU"
# Default folder to open when starting a JetBrains IDE
folder = "/home/coder"
# This ensures that the latest version of the module gets downloaded, you can also pin the module version to prevent breaking changes in production.
version = ">= 1.0.0"
agent_id = coder_agent.main.id
agent_name = "main"
order = 2
}
resource "docker_volume" "home_volume" {
name = "coder-${data.coder_workspace.me.id}-home"
# Protect the volume from being deleted due to changes in attributes.
lifecycle {
ignore_changes = all
}
}
resource "docker_container" "workspace" {
lifecycle {
ignore_changes = all
}
network_mode = "host"
count = data.coder_workspace.me.start_count
image = "codercom/enterprise-base:ubuntu"
# Uses lower() to avoid Docker restriction on container names.
name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}"
# Hostname makes the shell more user friendly: coder@my-workspace:~$
hostname = data.coder_workspace.me.name
# Use the docker gateway if the access URL is 127.0.0.1
entrypoint = [
"sh", "-c", replace(coder_agent.main.init_script, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal")
]
env = ["CODER_AGENT_TOKEN=${coder_agent.main.token}"]
host {
host = "host.docker.internal"
ip = "host-gateway"
}
volumes {
container_path = "/home/coder"
volume_name = docker_volume.home_volume.name
read_only = false
}
}