Path: blob/master/src/packages/file-server/zfs/test/create.test.ts
1450 views
/*1DEVELOPMENT:23pnpm exec jest --watch create.test.ts45pnpm exec jest create.test.ts -b6*/78// application/typescript text9import { executeCode } from "@cocalc/backend/execute-code";10import { createTestPools, deleteTestPools, init, describe, describe0 } from "./util";11import {12createFilesystem,13createBackup,14deleteFilesystem,15getPools,16} from "@cocalc/file-server/zfs";17import { filesystemMountpoint } from "@cocalc/file-server/zfs/names";18import { readFile, writeFile } from "fs/promises";19import { join } from "path";20import { exists } from "@cocalc/backend/misc/async-utils-node";21import { uuid } from "@cocalc/util/misc";22import { map as asyncMap } from "awaiting";2324describe0("test for zfs", () => {25it("checks for TEST_ZFS", () => {26if (!process.env.TEST_ZFS) {27// make sure people aren't silently overconfident...28console.log(29"WARNing: TEST_ZFS not set, so **SKIPPING ALL ZFS FILE SERVER TESTS!**",30);31}32});33});3435describe("creates project, clone project, delete projects", () => {36let x: any = null;3738beforeAll(async () => {39x = await createTestPools({ count: 1, size: "1G" });40await init();41});4243afterAll(async () => {44if (x != null) {45await deleteTestPools(x);46}47});4849it("verifies there is a pool", async () => {50const { stdout } = await executeCode({51command: "zpool",52args: ["list", x.pools[0]],53});54expect(stdout).toContain(x.pools[0]);55expect(Object.keys(await getPools()).length).toBe(1);56});5758const project_id = "00000000-0000-0000-0000-000000000001";59it("creates a project", async () => {60const project = await createFilesystem({61project_id,62});63expect(project.owner_id).toBe(project_id);64});6566it("verify project is in output of zfs list", async () => {67const { stdout } = await executeCode({68command: "zfs",69args: ["list", "-r", x.pools[0]],70});71expect(stdout).toContain(project_id);72});7374const FILE_CONTENT = "hello";75const FILENAME = "cocalc.txt";76it("write a file to the project", async () => {77const path = join(78filesystemMountpoint({ project_id, namespace: "default" }),79FILENAME,80);81await writeFile(path, FILE_CONTENT);82});8384const project_id2 = "00000000-0000-0000-0000-000000000002";85it("clones our project to make a second project", async () => {86const project2 = await createFilesystem({87project_id: project_id2,88clone: { project_id },89});90expect(project2.owner_id).toBe(project_id2);91});9293it("verify clone is in output of zfs list", async () => {94const { stdout } = await executeCode({95command: "zfs",96args: ["list", "-r", x.pools[0]],97});98expect(stdout).toContain(project_id2);99});100101it("read file from the clone", async () => {102const path = join(103filesystemMountpoint({ project_id: project_id2, namespace: "default" }),104FILENAME,105);106const content = (await readFile(path)).toString();107expect(content).toEqual(FILE_CONTENT);108});109110let BUP_DIR;111it("make a backup of project, so can see that it gets deleted below", async () => {112const x = await createBackup({ project_id });113BUP_DIR = x.BUP_DIR;114expect(await exists(BUP_DIR)).toBe(true);115});116117it("attempt to delete first project and get error", async () => {118try {119await deleteFilesystem({ project_id });120throw Error("must throw");121} catch (err) {122expect(`${err}`).toContain("filesystem has dependent clones");123}124});125126it("delete second project, then first project, works", async () => {127await deleteFilesystem({ project_id: project_id2 });128await deleteFilesystem({ project_id });129const { stdout } = await executeCode({130command: "zfs",131args: ["list", "-r", x.pools[0]],132});133expect(stdout).not.toContain(project_id);134expect(stdout).not.toContain(project_id2);135});136137it("verifies bup backup is also gone", async () => {138expect(await exists(BUP_DIR)).toBe(false);139});140});141142describe("create two projects with the same project_id at the same time, but in different namespaces", () => {143let x: any = null;144145beforeAll(async () => {146x = await createTestPools({ count: 2, size: "1G" });147await init();148});149150afterAll(async () => {151if (x != null) {152await deleteTestPools(x);153}154});155156it("there are TWO pools this time", async () => {157expect(Object.keys(await getPools()).length).toBe(2);158});159160const project_id = "00000000-0000-0000-0000-000000000001";161it("creates two projects", async () => {162const project = await createFilesystem({163project_id,164namespace: "default",165});166expect(project.owner_id).toBe(project_id);167168const project2 = await createFilesystem({169project_id,170namespace: "test",171});172expect(project2.owner_id).toBe(project_id);173// they are on different pools174expect(project.pool).not.toEqual(project2.pool);175});176177it("two different entries in zfs list", async () => {178const { stdout: stdout0 } = await executeCode({179command: "zfs",180args: ["list", "-r", x.pools[0]],181});182expect(stdout0).toContain(project_id);183const { stdout: stdout1 } = await executeCode({184command: "zfs",185args: ["list", "-r", x.pools[1]],186});187expect(stdout1).toContain(project_id);188});189});190191describe("test the affinity property when creating projects", () => {192let x: any = null;193194beforeAll(async () => {195x = await createTestPools({ count: 2, size: "1G" });196await init();197});198199afterAll(async () => {200if (x != null) {201await deleteTestPools(x);202}203});204205const project_id = "00000000-0000-0000-0000-000000000001";206const project_id2 = "00000000-0000-0000-0000-000000000002";207const affinity = "math100";208it("creates two projects with same afinity", async () => {209const project = await createFilesystem({210project_id,211affinity,212});213expect(project.owner_id).toBe(project_id);214215const project2 = await createFilesystem({216project_id: project_id2,217affinity,218});219expect(project2.owner_id).toBe(project_id2);220// they are on SAME pools, because of affinity221expect(project.pool).toEqual(project2.pool);222});223});224225describe("do a stress/race condition test creating a larger number of projects on a larger number of pools", () => {226let x: any = null;227228const count = 3;229const nprojects = 25;230231beforeAll(async () => {232x = await createTestPools({ count, size: "1G" });233await init();234});235236afterAll(async () => {237if (x != null) {238await deleteTestPools(x);239}240});241242it(`creates ${nprojects} projects in parallel on ${count} pools`, async () => {243const f = async (project_id) => {244await createFilesystem({ project_id });245};246const v: string[] = [];247for (let n = 0; n < nprojects; n++) {248v.push(uuid());249}250// doing these in parallel and having it work is an important stress test,251// since we will get a bid speedup doing this in production, and there we252// will really need it.253await asyncMap(v, nprojects, f);254});255});256257258