k6ctl 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +168 -4
- package/dist/cli.js +10 -5
- package/dist/cli.js.map +1 -1
- package/dist/commands/delete.d.ts +4 -1
- package/dist/commands/delete.d.ts.map +1 -1
- package/dist/commands/delete.js +26 -5
- package/dist/commands/delete.js.map +1 -1
- package/dist/commands/list.js +6 -6
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/run.d.ts +2 -0
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +191 -1
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +4 -0
- package/dist/commands/status.js.map +1 -1
- package/dist/services/kubernetes.service.d.ts +12 -5
- package/dist/services/kubernetes.service.d.ts.map +1 -1
- package/dist/services/kubernetes.service.js +134 -6
- package/dist/services/kubernetes.service.js.map +1 -1
- package/dist/services/script.service.d.ts +3 -1
- package/dist/services/script.service.d.ts.map +1 -1
- package/dist/services/script.service.js +115 -1
- package/dist/services/script.service.js.map +1 -1
- package/dist/types/kubernetes.types.d.ts +5 -0
- package/dist/types/kubernetes.types.d.ts.map +1 -1
- package/dist/types/lastRun.types.d.ts +2 -1
- package/dist/types/lastRun.types.d.ts.map +1 -1
- package/dist/types/script.types.d.ts +39 -0
- package/dist/types/script.types.d.ts.map +1 -1
- package/dist/types/testRunManifest.types.d.ts +2 -4
- package/dist/types/testRunManifest.types.d.ts.map +1 -1
- package/dist/utils/testRunManifestBuilder.d.ts +14 -1
- package/dist/utils/testRunManifestBuilder.d.ts.map +1 -1
- package/dist/utils/testRunManifestBuilder.js +70 -14
- package/dist/utils/testRunManifestBuilder.js.map +1 -1
- package/package.json +2 -1
- package/src/cli.ts +10 -5
- package/src/commands/delete.ts +35 -8
- package/src/commands/list.ts +7 -7
- package/src/commands/run.ts +216 -2
- package/src/commands/status.ts +4 -0
- package/src/services/kubernetes.service.ts +161 -16
- package/src/services/script.service.ts +102 -4
- package/src/types/kubernetes.types.ts +6 -0
- package/src/types/lastRun.types.ts +2 -1
- package/src/types/script.types.ts +40 -0
- package/src/types/testRunManifest.types.ts +3 -4
- package/src/utils/testRunManifestBuilder.ts +80 -18
- package/test/integration/kubernetes.service.test.ts +63 -9
- package/test/unit/kubernetes.service.test.ts +15 -7
- package/test/unit/script.service.test.ts +11 -4
|
@@ -1,23 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { describe, expect, test } from '@jest/globals';
|
|
2
2
|
import { join, resolve } from 'node:path';
|
|
3
3
|
import { existsSync } from 'node:fs';
|
|
4
|
-
import {
|
|
5
|
-
import { createDefaultKubernetesService, printConfigMapsTable } from '../../src/services/kubernetes.service';
|
|
4
|
+
import { createDefaultKubernetesService } from '../../src/services/kubernetes.service';
|
|
6
5
|
import { ScriptService } from '../../src/services/script.service';
|
|
7
|
-
import { ConfigMapResult } from '../../src/types/kubernetes.types';
|
|
6
|
+
import { ConfigMapResult, VolumeClaimResult } from '../../src/types/kubernetes.types';
|
|
8
7
|
import { ArchiveResult } from '../../src/types/script.types';
|
|
9
8
|
import logger from '../../src/utils/logger';
|
|
10
9
|
import { TestRunManifest } from '../../src/types/testRunManifest.types';
|
|
11
10
|
import { K6Config } from '../../src/types/config.types';
|
|
12
11
|
import { loadK6Config } from '../../src/utils/configLoader';
|
|
13
|
-
import { buildTestRunManifest } from '../../src/utils/testRunManifestBuilder';
|
|
14
|
-
import { printTestRunsTable, printPodsTable } from '../../src/services/kubernetes.service';
|
|
12
|
+
import { buildTestRunManifest, buildTestRunManifestWithVolumeClaim } from '../../src/utils/testRunManifestBuilder';
|
|
15
13
|
import { saveLastRun } from '../../src/utils/lastRunStore';
|
|
16
14
|
import { status } from '../../src/commands/status';
|
|
17
15
|
import { logs } from '../../src/commands/logs';
|
|
18
16
|
import { deleteLastRun } from '../../src/commands/delete';
|
|
19
17
|
|
|
20
|
-
|
|
21
18
|
const samplesPath = resolve(__dirname, '..', 'samples');
|
|
22
19
|
const scriptSample1 = join(samplesPath, 'k6_script_sample_1.js');
|
|
23
20
|
const scriptSample2 = join(samplesPath, 'k6_script_sample_2.js');
|
|
@@ -29,8 +26,11 @@ const kubernetesService = createDefaultKubernetesService();
|
|
|
29
26
|
let archiveOutput: ArchiveResult;
|
|
30
27
|
let configMapResult: ConfigMapResult;
|
|
31
28
|
|
|
32
|
-
|
|
29
|
+
let volumeArchiveOutput: ArchiveResult;
|
|
30
|
+
let volumeClaimResult: VolumeClaimResult;
|
|
31
|
+
let volumeTestRunManifest: TestRunManifest;
|
|
33
32
|
|
|
33
|
+
describe('KubernetesService integration tests', () => {
|
|
34
34
|
test('create config map from archived script', async () => {
|
|
35
35
|
archiveOutput = await scriptService.archiveTest(scriptSample2);
|
|
36
36
|
expect(existsSync(archiveOutput.archivePath)).toBe(true);
|
|
@@ -72,4 +72,58 @@ describe('KubernetesService integration tests', () => {
|
|
|
72
72
|
// Clean up the TestRun after successful creation and execution
|
|
73
73
|
await deleteLastRun({ namespace: testRunManifest.metadata.namespace });
|
|
74
74
|
}, 120000);
|
|
75
|
-
|
|
75
|
+
|
|
76
|
+
test('archives a script and creates PVC', async () => {
|
|
77
|
+
volumeArchiveOutput = await scriptService.archiveTest(scriptSample1);
|
|
78
|
+
expect(existsSync(volumeArchiveOutput.archivePath)).toBe(true);
|
|
79
|
+
|
|
80
|
+
volumeClaimResult = await kubernetesService.createPVCWithArchive(volumeArchiveOutput, 'default');
|
|
81
|
+
|
|
82
|
+
expect(volumeClaimResult).toBeDefined();
|
|
83
|
+
expect(volumeClaimResult.volumeClaimName).toMatch(/^archive-/);
|
|
84
|
+
expect(volumeClaimResult.namespace).toBe('default');
|
|
85
|
+
expect(volumeClaimResult.archiveFilename).toMatch(/\.tar$/);
|
|
86
|
+
|
|
87
|
+
// createPVCWithArchive deletes the local archive file after uploading
|
|
88
|
+
expect(existsSync(volumeArchiveOutput.archivePath)).toBe(false);
|
|
89
|
+
|
|
90
|
+
logger.info(`PVC created: ${JSON.stringify(volumeClaimResult, null, 2)}`);
|
|
91
|
+
}, 120000);
|
|
92
|
+
|
|
93
|
+
test('creates and runs a TestRun from the PVC', async () => {
|
|
94
|
+
const cfg: K6Config = loadK6Config();
|
|
95
|
+
logger.debug('Loaded K6 Config:', JSON.stringify(cfg, null, 2));
|
|
96
|
+
|
|
97
|
+
volumeTestRunManifest = buildTestRunManifestWithVolumeClaim(volumeClaimResult, cfg);
|
|
98
|
+
|
|
99
|
+
expect(volumeTestRunManifest.spec.script.volumeClaim).toBeDefined();
|
|
100
|
+
expect(volumeTestRunManifest.spec.script.configMap).toBeUndefined();
|
|
101
|
+
expect(volumeTestRunManifest.spec.script.volumeClaim?.name).toBe(volumeClaimResult.volumeClaimName);
|
|
102
|
+
expect(volumeTestRunManifest.spec.script.volumeClaim?.file).toBe(volumeClaimResult.archiveFilename);
|
|
103
|
+
|
|
104
|
+
const response = await kubernetesService.createTestRun(volumeTestRunManifest);
|
|
105
|
+
expect(response).toBeDefined();
|
|
106
|
+
logger.info('TestRun result:', JSON.stringify(response));
|
|
107
|
+
|
|
108
|
+
await saveLastRun({
|
|
109
|
+
testRunName: volumeTestRunManifest.metadata.name,
|
|
110
|
+
namespace: volumeTestRunManifest.metadata.namespace,
|
|
111
|
+
volumeClaimName: volumeClaimResult.volumeClaimName,
|
|
112
|
+
scriptPath: volumeArchiveOutput.scriptPath,
|
|
113
|
+
createdAt: new Date().toISOString(),
|
|
114
|
+
});
|
|
115
|
+
logger.info(`Last run saved: ${volumeTestRunManifest.metadata.name} (namespace: ${volumeTestRunManifest.metadata.namespace})`);
|
|
116
|
+
|
|
117
|
+
// Wait for the TestRun to be created and start running
|
|
118
|
+
await new Promise((resolve) => setTimeout(resolve, 60000));
|
|
119
|
+
|
|
120
|
+
// Check status command output
|
|
121
|
+
await status({ namespace: volumeTestRunManifest.metadata.namespace });
|
|
122
|
+
|
|
123
|
+
// Check logs command output
|
|
124
|
+
await logs({ namespace: volumeTestRunManifest.metadata.namespace });
|
|
125
|
+
|
|
126
|
+
// Clean up the TestRun and PVC
|
|
127
|
+
await deleteLastRun({ namespace: volumeTestRunManifest.metadata.namespace });
|
|
128
|
+
}, 120000);
|
|
129
|
+
});
|
|
@@ -5,13 +5,19 @@ import logger from '../../src/utils/logger';
|
|
|
5
5
|
import { KubernetesService, createDefaultKubernetesService } from '../../src/services/kubernetes.service';
|
|
6
6
|
import type { ArchivedFile } from '../../src/types/kubernetes.types';
|
|
7
7
|
|
|
8
|
-
jest.mock('fs', () =>
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
jest.mock('fs', () => {
|
|
9
|
+
const actualFs = jest.requireActual<typeof import('fs')>('fs');
|
|
10
|
+
return {
|
|
11
|
+
...actualFs,
|
|
12
|
+
existsSync: jest.fn(),
|
|
13
|
+
promises: {
|
|
14
|
+
...actualFs.promises,
|
|
15
|
+
stat: jest.fn(),
|
|
16
|
+
readFile: jest.fn(),
|
|
17
|
+
unlink: jest.fn(),
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
});
|
|
15
21
|
|
|
16
22
|
jest.mock('@kubernetes/client-node', () => {
|
|
17
23
|
const mockApiClient = {
|
|
@@ -49,6 +55,7 @@ jest.mock('../../src/utils/logger', () => ({
|
|
|
49
55
|
const mockedExistsSync = existsSync as unknown as jest.Mock;
|
|
50
56
|
const mockedStat = fs_promises.stat as unknown as jest.Mock;
|
|
51
57
|
const mockedReadFile = fs_promises.readFile as unknown as jest.Mock;
|
|
58
|
+
const mockedUnlink = fs_promises.unlink as unknown as jest.MockedFunction<typeof fs_promises.unlink>;
|
|
52
59
|
const mockedLoggerInfo = logger.info as unknown as jest.Mock;
|
|
53
60
|
|
|
54
61
|
const mockedK8sModule = k8s as unknown as {
|
|
@@ -119,6 +126,7 @@ describe('KubernetesService', () => {
|
|
|
119
126
|
});
|
|
120
127
|
|
|
121
128
|
test('creates configmap with binaryData and returns namespace/name', async () => {
|
|
129
|
+
mockedUnlink.mockResolvedValue(undefined);
|
|
122
130
|
mockedExistsSync.mockReturnValue(true);
|
|
123
131
|
mockedStat.mockImplementation(async () => ({ size: 512 }));
|
|
124
132
|
mockedReadFile.mockImplementation(async () => 'YmFzZTY0');
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, jest, test } from '@jest/globals';
|
|
2
2
|
import { ScriptService } from '../../src/services/script.service';
|
|
3
|
-
import { existsSync } from 'fs';
|
|
3
|
+
import { existsSync, statSync } from 'fs';
|
|
4
4
|
import type { ExecFn } from '../../src/types/script.types';
|
|
5
5
|
|
|
6
|
-
jest.mock('fs', () =>
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
jest.mock('fs', () => {
|
|
7
|
+
const actualFs = jest.requireActual<typeof import('fs')>('fs');
|
|
8
|
+
return {
|
|
9
|
+
...actualFs,
|
|
10
|
+
existsSync: jest.fn(),
|
|
11
|
+
statSync: jest.fn(),
|
|
12
|
+
};
|
|
13
|
+
});
|
|
9
14
|
|
|
10
15
|
jest.mock('../../src/utils/logger', () => ({
|
|
11
16
|
__esModule: true,
|
|
@@ -13,6 +18,7 @@ jest.mock('../../src/utils/logger', () => ({
|
|
|
13
18
|
}));
|
|
14
19
|
|
|
15
20
|
const mockedExistsSync = existsSync as unknown as jest.Mock;
|
|
21
|
+
const mockedStatSync = statSync as unknown as jest.Mock;
|
|
16
22
|
|
|
17
23
|
describe('ScriptService', () => {
|
|
18
24
|
let mockExec: jest.MockedFunction<ExecFn>;
|
|
@@ -22,6 +28,7 @@ describe('ScriptService', () => {
|
|
|
22
28
|
jest.resetAllMocks();
|
|
23
29
|
mockExec = jest.fn<ExecFn>();
|
|
24
30
|
service = new ScriptService(mockExec);
|
|
31
|
+
mockedStatSync.mockReturnValue({ size: 1024 });
|
|
25
32
|
});
|
|
26
33
|
|
|
27
34
|
describe('archiveTest', () => {
|