Skip to content

chore: enable terraform provisioners in e2e by default #13134

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 24 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -222,5 +222,7 @@
"go.testFlags": ["-short", "-coverpkg=./..."],
// We often use a version of TypeScript that's ahead of the version shipped
// with VS Code.
"typescript.tsdk": "./site/node_modules/typescript/lib"
"typescript.tsdk": "./site/node_modules/typescript/lib",
// Playwright tests in VSCode will open a browser to live "view" the test.
"playwright.reuseBrowser": true
Comment on lines -225 to +227
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is required to run a "live" debugger in VSCode with the chrome window open the whole time. Otherwise the window keeps opening and closing for each snapshot step.

}
4 changes: 4 additions & 0 deletions site/e2e/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ export const requireEnterpriseTests = Boolean(
);
export const enterpriseLicense = process.env.CODER_E2E_ENTERPRISE_LICENSE ?? "";

// Disabling terraform tests is optional for environments without Docker + Terraform.
// By default, we opt into these tests.
export const requireTerraformTests = !process.env.CODER_E2E_DISABLE_TERRAFORM;
Comment on lines +42 to +44
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to include this option if the Terraform stuff ever gets a bit messy. Right now for example docker containers are leaky.


// Fake experiments to verify that site presents them as enabled.
export const e2eFakeExperiment1 = "e2e-fake-experiment-1";
export const e2eFakeExperiment2 = "e2e-fake-experiment-2";
52 changes: 44 additions & 8 deletions site/e2e/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
enterpriseLicense,
prometheusPort,
requireEnterpriseTests,
requireTerraformTests,
} from "./constants";
import { expectUrl } from "./expectUrl";
import {
Expand All @@ -43,6 +44,15 @@ export function requiresEnterpriseLicense() {
test.skip(!enterpriseLicense);
}

// requiresTerraform by default is enabled.
export function requiresTerraform() {
if (requireTerraformTests) {
return;
}

test.skip(!requireTerraformTests);
}

// createWorkspace creates a workspace for a template.
// It does not wait for it to be running, but it does navigate to the page.
export const createWorkspace = async (
Expand Down Expand Up @@ -149,25 +159,51 @@ export const verifyParameters = async (
}
};

// StarterTemplates are ids of starter templates that can be used in place of
// the responses payload. These starter templates will require real provisioners.
export enum StarterTemplates {
STARTER_DOCKER = "docker",
}
Comment on lines +158 to +162
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably add ways to add more to the templates later. For now, just using the starter template is easier.


function isStarterTemplate(
input: EchoProvisionerResponses | StarterTemplates | undefined,
): input is StarterTemplates {
if (!input) {
return false;
}
return typeof input === "string";
}

// createTemplate navigates to the /templates/new page and uploads a template
// with the resources provided in the responses argument.
export const createTemplate = async (
page: Page,
responses?: EchoProvisionerResponses,
responses?: EchoProvisionerResponses | StarterTemplates,
): Promise<string> => {
// Required to have templates submit their provisioner type as echo!
await page.addInitScript({
content: "window.playwright = true",
content: `window.playwrightProvisionerType = ${
// Starter templates use the terraform type.
isStarterTemplate(responses) ? "terraform" : "echo"
}`,
});

await page.goto("/templates/new", { waitUntil: "domcontentloaded" });
let path = "/templates/new";
if (isStarterTemplate(responses)) {
path += `?exampleId=${responses}`;
}

await page.goto(path, { waitUntil: "domcontentloaded" });
await expectUrl(page).toHavePathName("/templates/new");

await page.getByTestId("file-upload").setInputFiles({
buffer: await createTemplateVersionTar(responses),
mimeType: "application/x-tar",
name: "template.tar",
});
if (!isStarterTemplate(responses)) {
await page.getByTestId("file-upload").setInputFiles({
buffer: await createTemplateVersionTar(responses),
mimeType: "application/x-tar",
name: "template.tar",
});
}

const name = randomName();
await page.getByLabel("Name *").fill(name);
await page.getByTestId("form-submit").click();
Expand Down
26 changes: 24 additions & 2 deletions site/e2e/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { defineConfig } from "@playwright/test";
import { execSync } from "child_process";
import * as path from "path";
import {
coderMain,
Expand All @@ -7,13 +8,33 @@ import {
e2eFakeExperiment1,
e2eFakeExperiment2,
gitAuth,
requireTerraformTests,
} from "./constants";

export const wsEndpoint = process.env.CODER_E2E_WS_ENDPOINT;

// This is where auth cookies are stored!
export const storageState = path.join(__dirname, ".auth.json");

if (requireTerraformTests) {
try {
// If running terraform tests, verify the requirements exist in the
// environment.
//
// These execs will throw an error if the status code is non-zero.
// So if both these work, then we can launch terraform provisioners.
execSync("terraform --version");
execSync("docker --version");
} catch (e) {
throw new Error(
"Terraform provisioners require docker & terraform. " +
"At least one of these is not present in the runtime environment. To check yourself:\n" +
"\t$ terraform --version\n" +
"\t$ docker --version",
);
}
}

const localURL = (port: number, path: string): string => {
return `http://localhost:${port}${path}`;
};
Expand Down Expand Up @@ -54,13 +75,14 @@ export default defineConfig({
`go run -tags embed ${coderMain} server`,
"--global-config $(mktemp -d -t e2e-XXXXXXXXXX)",
`--access-url=http://localhost:${coderPort}`,
`--http-address=localhost:${coderPort}`,
`--http-address=0.0.0.0:${coderPort}`,
"--in-memory",
"--telemetry=false",
"--dangerous-disable-rate-limits",
"--provisioner-daemons 10",
// TODO: Enable some terraform provisioners
"--provisioner-types=echo",
`--provisioner-types=echo${requireTerraformTests ? ",terraform" : ""}`,
`--provisioner-daemons=10`,
"--web-terminal-renderer=dom",
"--pprof-enable",
]
Expand Down
42 changes: 42 additions & 0 deletions site/e2e/tests/createWorkspace.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { test, expect } from "@playwright/test";
import {
StarterTemplates,
createTemplate,
createWorkspace,
echoResponsesWithParameters,
requiresTerraform,
verifyParameters,
} from "../helpers";
import { beforeCoderTest } from "../hooks";
Expand Down Expand Up @@ -147,3 +149,43 @@ test("create workspace with disable_param search params", async ({ page }) => {
await expect(page.getByLabel(/First parameter/i)).toBeDisabled();
await expect(page.getByLabel(/Second parameter/i)).toBeDisabled();
});

test("create docker workspace", async ({ page }) => {
test.skip(
false,
"creating docker containers is currently leaky. They are not cleaned up when the tests are over.",
);
requiresTerraform();
const template = await createTemplate(page, StarterTemplates.STARTER_DOCKER);

const _ = await createWorkspace(page, template);

// The workspace agents must be ready before we try to interact with the workspace.
await page.waitForSelector(
`//div[@role="status"][@data-testid="agent-status-ready"]`,
{
state: "visible",
},
);

// Wait for the terminal button to be visible, and click it.
const terminalButton =
"//a[@data-testid='terminal'][normalize-space()='Terminal']";
await page.waitForSelector(terminalButton, {
state: "visible",
});

// We can't click the terminal button because that opens a new tab.
// So grab the href, and manually navigate.
const terminalPageURL = await page.getAttribute(terminalButton, "href");
expect(terminalPageURL).not.toBeNull();
await page.goto(terminalPageURL!, {
waitUntil: "domcontentloaded",
});
await page.waitForSelector(
`//textarea[contains(@class,"xterm-helper-textarea")]`,
{
state: "visible",
},
);
});
4 changes: 2 additions & 2 deletions site/src/pages/CreateTemplatePage/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import { calculateAutostopRequirementDaysValue } from "utils/schedule";
import type { CreateTemplateData } from "./CreateTemplateForm";

const provisioner: ProvisionerType =
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Playwright needs to use a different provisioner type!
typeof (window as any).playwright !== "undefined" ? "echo" : "terraform";
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Playwright might need to use a different provisioner type!
(window as any).playwrightProvisionerType || "terraform";

export const newTemplate = (formData: CreateTemplateData) => {
const { autostop_requirement_days_of_week, autostop_requirement_weeks } =
Expand Down
Loading