Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/file-server/zfs/test/create.test.ts
1450 views
1
/*
2
DEVELOPMENT:
3
4
pnpm exec jest --watch create.test.ts
5
6
pnpm exec jest create.test.ts -b
7
*/
8
9
// application/typescript text
10
import { executeCode } from "@cocalc/backend/execute-code";
11
import { createTestPools, deleteTestPools, init, describe, describe0 } from "./util";
12
import {
13
createFilesystem,
14
createBackup,
15
deleteFilesystem,
16
getPools,
17
} from "@cocalc/file-server/zfs";
18
import { filesystemMountpoint } from "@cocalc/file-server/zfs/names";
19
import { readFile, writeFile } from "fs/promises";
20
import { join } from "path";
21
import { exists } from "@cocalc/backend/misc/async-utils-node";
22
import { uuid } from "@cocalc/util/misc";
23
import { map as asyncMap } from "awaiting";
24
25
describe0("test for zfs", () => {
26
it("checks for TEST_ZFS", () => {
27
if (!process.env.TEST_ZFS) {
28
// make sure people aren't silently overconfident...
29
console.log(
30
"WARNing: TEST_ZFS not set, so **SKIPPING ALL ZFS FILE SERVER TESTS!**",
31
);
32
}
33
});
34
});
35
36
describe("creates project, clone project, delete projects", () => {
37
let x: any = null;
38
39
beforeAll(async () => {
40
x = await createTestPools({ count: 1, size: "1G" });
41
await init();
42
});
43
44
afterAll(async () => {
45
if (x != null) {
46
await deleteTestPools(x);
47
}
48
});
49
50
it("verifies there is a pool", async () => {
51
const { stdout } = await executeCode({
52
command: "zpool",
53
args: ["list", x.pools[0]],
54
});
55
expect(stdout).toContain(x.pools[0]);
56
expect(Object.keys(await getPools()).length).toBe(1);
57
});
58
59
const project_id = "00000000-0000-0000-0000-000000000001";
60
it("creates a project", async () => {
61
const project = await createFilesystem({
62
project_id,
63
});
64
expect(project.owner_id).toBe(project_id);
65
});
66
67
it("verify project is in output of zfs list", async () => {
68
const { stdout } = await executeCode({
69
command: "zfs",
70
args: ["list", "-r", x.pools[0]],
71
});
72
expect(stdout).toContain(project_id);
73
});
74
75
const FILE_CONTENT = "hello";
76
const FILENAME = "cocalc.txt";
77
it("write a file to the project", async () => {
78
const path = join(
79
filesystemMountpoint({ project_id, namespace: "default" }),
80
FILENAME,
81
);
82
await writeFile(path, FILE_CONTENT);
83
});
84
85
const project_id2 = "00000000-0000-0000-0000-000000000002";
86
it("clones our project to make a second project", async () => {
87
const project2 = await createFilesystem({
88
project_id: project_id2,
89
clone: { project_id },
90
});
91
expect(project2.owner_id).toBe(project_id2);
92
});
93
94
it("verify clone is in output of zfs list", async () => {
95
const { stdout } = await executeCode({
96
command: "zfs",
97
args: ["list", "-r", x.pools[0]],
98
});
99
expect(stdout).toContain(project_id2);
100
});
101
102
it("read file from the clone", async () => {
103
const path = join(
104
filesystemMountpoint({ project_id: project_id2, namespace: "default" }),
105
FILENAME,
106
);
107
const content = (await readFile(path)).toString();
108
expect(content).toEqual(FILE_CONTENT);
109
});
110
111
let BUP_DIR;
112
it("make a backup of project, so can see that it gets deleted below", async () => {
113
const x = await createBackup({ project_id });
114
BUP_DIR = x.BUP_DIR;
115
expect(await exists(BUP_DIR)).toBe(true);
116
});
117
118
it("attempt to delete first project and get error", async () => {
119
try {
120
await deleteFilesystem({ project_id });
121
throw Error("must throw");
122
} catch (err) {
123
expect(`${err}`).toContain("filesystem has dependent clones");
124
}
125
});
126
127
it("delete second project, then first project, works", async () => {
128
await deleteFilesystem({ project_id: project_id2 });
129
await deleteFilesystem({ project_id });
130
const { stdout } = await executeCode({
131
command: "zfs",
132
args: ["list", "-r", x.pools[0]],
133
});
134
expect(stdout).not.toContain(project_id);
135
expect(stdout).not.toContain(project_id2);
136
});
137
138
it("verifies bup backup is also gone", async () => {
139
expect(await exists(BUP_DIR)).toBe(false);
140
});
141
});
142
143
describe("create two projects with the same project_id at the same time, but in different namespaces", () => {
144
let x: any = null;
145
146
beforeAll(async () => {
147
x = await createTestPools({ count: 2, size: "1G" });
148
await init();
149
});
150
151
afterAll(async () => {
152
if (x != null) {
153
await deleteTestPools(x);
154
}
155
});
156
157
it("there are TWO pools this time", async () => {
158
expect(Object.keys(await getPools()).length).toBe(2);
159
});
160
161
const project_id = "00000000-0000-0000-0000-000000000001";
162
it("creates two projects", async () => {
163
const project = await createFilesystem({
164
project_id,
165
namespace: "default",
166
});
167
expect(project.owner_id).toBe(project_id);
168
169
const project2 = await createFilesystem({
170
project_id,
171
namespace: "test",
172
});
173
expect(project2.owner_id).toBe(project_id);
174
// they are on different pools
175
expect(project.pool).not.toEqual(project2.pool);
176
});
177
178
it("two different entries in zfs list", async () => {
179
const { stdout: stdout0 } = await executeCode({
180
command: "zfs",
181
args: ["list", "-r", x.pools[0]],
182
});
183
expect(stdout0).toContain(project_id);
184
const { stdout: stdout1 } = await executeCode({
185
command: "zfs",
186
args: ["list", "-r", x.pools[1]],
187
});
188
expect(stdout1).toContain(project_id);
189
});
190
});
191
192
describe("test the affinity property when creating projects", () => {
193
let x: any = null;
194
195
beforeAll(async () => {
196
x = await createTestPools({ count: 2, size: "1G" });
197
await init();
198
});
199
200
afterAll(async () => {
201
if (x != null) {
202
await deleteTestPools(x);
203
}
204
});
205
206
const project_id = "00000000-0000-0000-0000-000000000001";
207
const project_id2 = "00000000-0000-0000-0000-000000000002";
208
const affinity = "math100";
209
it("creates two projects with same afinity", async () => {
210
const project = await createFilesystem({
211
project_id,
212
affinity,
213
});
214
expect(project.owner_id).toBe(project_id);
215
216
const project2 = await createFilesystem({
217
project_id: project_id2,
218
affinity,
219
});
220
expect(project2.owner_id).toBe(project_id2);
221
// they are on SAME pools, because of affinity
222
expect(project.pool).toEqual(project2.pool);
223
});
224
});
225
226
describe("do a stress/race condition test creating a larger number of projects on a larger number of pools", () => {
227
let x: any = null;
228
229
const count = 3;
230
const nprojects = 25;
231
232
beforeAll(async () => {
233
x = await createTestPools({ count, size: "1G" });
234
await init();
235
});
236
237
afterAll(async () => {
238
if (x != null) {
239
await deleteTestPools(x);
240
}
241
});
242
243
it(`creates ${nprojects} projects in parallel on ${count} pools`, async () => {
244
const f = async (project_id) => {
245
await createFilesystem({ project_id });
246
};
247
const v: string[] = [];
248
for (let n = 0; n < nprojects; n++) {
249
v.push(uuid());
250
}
251
// doing these in parallel and having it work is an important stress test,
252
// since we will get a bid speedup doing this in production, and there we
253
// will really need it.
254
await asyncMap(v, nprojects, f);
255
});
256
});
257
258