mirror of
https://github.com/coder/coder.git
synced 2025-07-09 11:45:56 +00:00
Add integration test for creating and using presets from scratch
Signed-off-by: Danny Kopping <danny@coder.com>
This commit is contained in:
@ -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 {
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -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, "../../../"),
|
||||||
|
76
site/e2e/tests/presets/createPreset.spec.ts
Normal file
76
site/e2e/tests/presets/createPreset.spec.ts
Normal 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;
|
||||||
|
}
|
BIN
site/e2e/tests/presets/template.zip
Normal file
BIN
site/e2e/tests/presets/template.zip
Normal file
Binary file not shown.
152
site/e2e/tests/presets/template/main.tf
Normal file
152
site/e2e/tests/presets/template/main.tf
Normal 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
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user