Skip to content
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

chore: add cancellation #4

Merged
merged 1 commit into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
21 changes: 11 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,25 +135,26 @@ curl "http://localhost:9000/test/c47a3487-2f9f-433c-ab5a-82b196fff7e1/results"
curl "http://localhost:9000/test/c47a3487-2f9f-433c-ab5a-82b196fff7e1/jmeter.log"
```

### `DELETE /test/<test-run-id>` -- Remove Test Run
Removes the test run with the given ID and its related data including resuls, so use with caution.
```bash
curl -X DELETE http://localhost:9000/test/c47a3487-2f9f-433c-ab5a-82b196fff7e1
```
> **Note** that if the test is still running, you need to confirm the deletion by adding `?confirm=true`, e.g.
### `DELETE /test/<test-run-id>[?confirm=true]` -- Cancel Test Run or Remove Test And Results
If confirmed (`?confirm=true`), removes the test run with the given ID and its related data including results, so use with caution. If a test is running it is first cancelled. E.g.:
```bash
curl -X DELETE http://localhost:9000/test/c47a3487-2f9f-433c-ab5a-82b196fff7e1?confirm=true
```

### `DELETE /` -- Remove All Test Runs
Removes all tests and their related data including resuls, so use with extreme caution.
If not confirmed, a running test is simply cancelled. E.g.:
```bash
curl -X DELETE http://localhost:9000/test
curl -X DELETE http://localhost:9000/test/c47a3487-2f9f-433c-ab5a-82b196fff7e1
```
> **Note** that if a test is still running, you need to confirm the deletion by adding `?confirm=true`, e.g.
### `DELETE /test[?confirm=true]` -- Cancel All Test Runs or Delete All Tests
If confirmed (`?confirm=true`), removes all tests and their related data including results, so use with extreme caution. All running tests are cancelled before deletion. E.g.:
```bash
curl -X DELETE http://localhost:9000/test?confirm=true
```

If not confirmed, all running tests are simply cancelled. E.g.
```bash
curl -X DELETE http://localhost:9000/test
```

### `GET /prometheus` -- Get Metrics
Exposes the metrics using [Prometheus](https://prometheus.io/) format.
37 changes: 25 additions & 12 deletions src/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,19 +89,19 @@ export class Controller {
}
}

private _exportRun(id: string) {
const run = this._getTest(id)!.run;
this._writeMetadata({ ...run, status: TestRunStatus.cancelled });
this._moveToResults(id);
private _exportTestRun(run: TestRun) {
this._writeMetadata(run);
this._moveToResults(run.id);
}

private _cancelTest(id: string) {
const test = this._getTest(id)!;
private _cancelTest(test: Test) {
const id = test.run.id;
console.warn(`[WARN] Test ${id} is running...`);
const process = test.process;
console.warn(`[WARN] Killing pid ${process?.pid}...`);
const killed = process?.kill;
console.warn(killed ? `[WARN] Test ${id} was cancelled.` : `Failed to kill test ${id} (pid: ${process?.pid}).`);
return this._upsertTest({run: {...test.run, status: TestRunStatus.cancelled} as TestRun, process: undefined} as Test);
}

private _writeMetadata(run: TestRun) {
Expand Down Expand Up @@ -149,10 +149,7 @@ export class Controller {

public async exportTestRuns() {
const runs = await this._getSubDirectories(this._config.tempFolder);
runs.forEach(id => {
this._cancelTest(id);
this._exportRun(id);
});
runs.forEach(id => this.cancelTest(id));
}

public testExists(id: string): boolean {
Expand All @@ -164,14 +161,22 @@ export class Controller {
return !!test && test.run.status === TestRunStatus.running;
}

public cancelTest(id: string) {
const test = this._getTest(id);
if (test) {
this._exportTestRun(this._cancelTest(test).run);
}
}

public deleteTest(id: string) {
const testData = path.join(this._config.testFolder, id);
const runData = path.join(this._config.tempFolder, id);

const exists = this.testExists(id);
if (exists) {
if (this.testRunning(id)) {
this._cancelTest(id);
const test = this._getTest(id)!;
this._cancelTest(test);
}
delete this._testsById[id];
} else {
Expand All @@ -195,10 +200,18 @@ export class Controller {
return exists || testDataExists || runDataExists;
}

public deleteAllTestRuns() {
public deleteAllTests() {
this._tests.map(x => this.deleteTest(x.run.id));
}

public cancelAllRunningTests() {
this._tests.map(x => {
if (x.process && !x.process.exitCode) {
this.cancelTest(x.run.id)
}
});
}

public async getTestRunStatus(id: string, limit: number = 1000) {
const test = this._getTest(id);
if (!test) throw new Error(`Test ${id} does not exist.`);
Expand Down
38 changes: 25 additions & 13 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,14 +157,23 @@ server.delete('/test', { schema: { querystring: { confirm: { type: 'boolean' } }
}

const parameters = request.query as { confirm?: boolean };
const cancelOnly = !parameters.confirm;

try {
if (controller.runningCount > 0 && !parameters.confirm) {
return reply.status(405).send("Cannot delete all tests as some are still running.\nHint:pass query parameter '?confirm=true'.\n");
} else {
controller.deleteAllTestRuns();
const anyTestRunning = controller.runningCount > 0;
if (anyTestRunning) {
controller.cancelAllRunningTests();
}

if (!cancelOnly) {
controller.deleteAllTests();
return reply.send('All tests deleted\n');
}

return anyTestRunning
? reply.send('All running tests cancelled\n')
: reply.status(405).send("No tests cancelled nor deleted.\nHint:pass query parameter '?confirm=true' to actually delete all tests.\n");

} catch (error) {
return reply.send({ msg: 'Cannot delete all tests\n', error: error });
}
Expand All @@ -177,18 +186,21 @@ server.delete('/test/:id', { schema: { querystring: { confirm: { type: 'boolean'

const { id } = request.params as { id: string };
const parameters = request.query as { confirm?: boolean };
const cancelOnly = !parameters.confirm;

try {
if (controller.testRunning(id) && !parameters.confirm) {
return reply.status(405).send(`Test ${id} is still running.\nHint:pass query parameter '?confirm=true'.\n`);
} else {
const deleted = controller.deleteTest(id);
if (deleted) {
return reply.send(`Test ${id} deleted\n`);
} else {
return reply.status(404).send(`Test ${id} not found\n`);
}
if (controller.testRunning(id)) {
controller.cancelTest(id);
}

if (cancelOnly) {
return reply.send(`Test ${id} cancelled\n`);
}

return controller.deleteTest(id)
? reply.send(`Test ${id} deleted\n`)
: reply.status(404).send(`Test ${id} not found\n`);

} catch (error) {
return reply.send({ msg: `Cannot delete test ${id}\n`, error: error });
}
Expand Down