k6ctl 1.2.0 → 1.3.1
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 +204 -4
- package/dist/cli.js +3 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/delete.d.ts +1 -1
- package/dist/commands/delete.d.ts.map +1 -1
- package/dist/commands/delete.js +10 -4
- package/dist/commands/delete.js.map +1 -1
- package/dist/commands/run.d.ts +1 -0
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +137 -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 +9 -3
- package/dist/services/kubernetes.service.d.ts.map +1 -1
- package/dist/services/kubernetes.service.js +121 -2
- 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/utils/testRunManifestBuilder.d.ts +2 -1
- package/dist/utils/testRunManifestBuilder.d.ts.map +1 -1
- package/dist/utils/testRunManifestBuilder.js +34 -0
- package/dist/utils/testRunManifestBuilder.js.map +1 -1
- package/package.json +2 -1
- package/src/cli.ts +3 -2
- package/src/commands/delete.ts +11 -5
- package/src/commands/run.ts +158 -2
- package/src/commands/status.ts +4 -0
- package/src/services/kubernetes.service.ts +141 -5
- 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/utils/testRunManifestBuilder.ts +39 -1
- package/test/integration/kubernetes.service.test.ts +61 -5
- package/test/unit/kubernetes.service.test.ts +13 -8
- package/test/unit/script.service.test.ts +11 -4
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import logger from '../utils/logger';
|
|
2
|
-
import type { ArchivedFile, ConfigMapResult } from '../types/kubernetes.types';
|
|
2
|
+
import type { ArchivedFile, ConfigMapResult, VolumeClaimResult } from '../types/kubernetes.types';
|
|
3
3
|
import { K6Config } from '../types/config.types';
|
|
4
4
|
import { TestRunManifest } from '../types/testRunManifest.types';
|
|
5
5
|
|
|
@@ -124,3 +124,41 @@ function addPrometheusEnvVars(cfg: K6Config, envMap: Map<string, RunnerEnvVar>)
|
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
|
+
|
|
128
|
+
export function buildTestRunManifestWithVolumeClaim(
|
|
129
|
+
volumeClaimResult: VolumeClaimResult,
|
|
130
|
+
cfg: K6Config,
|
|
131
|
+
envFromLoader?: Record<string, string>
|
|
132
|
+
): TestRunManifest {
|
|
133
|
+
const testName = volumeClaimResult.volumeClaimName.replace('archive-', 'test-');
|
|
134
|
+
const argumentsString = buildArgumentsString(cfg.arguments, cfg, testName);
|
|
135
|
+
logger.debug('Constructed arguments string for TestRun manifest (volume):', argumentsString);
|
|
136
|
+
const testRun: TestRunManifest = {
|
|
137
|
+
apiVersion: `${K6_GROUP}/${K6_VERSION}`,
|
|
138
|
+
kind: K6_KIND,
|
|
139
|
+
metadata: {
|
|
140
|
+
name: testName,
|
|
141
|
+
namespace: volumeClaimResult.namespace,
|
|
142
|
+
},
|
|
143
|
+
spec: {
|
|
144
|
+
parallelism: cfg.parallelism,
|
|
145
|
+
arguments: argumentsString,
|
|
146
|
+
quiet: String(cfg.quiet),
|
|
147
|
+
...(cfg.cleanup === true ? { cleanup: K6_CLEANUP_LABEL } : {}),
|
|
148
|
+
separate: cfg.separate,
|
|
149
|
+
runner: {
|
|
150
|
+
image: cfg.runner?.image,
|
|
151
|
+
env: buildRunnerEnv(cfg, envFromLoader),
|
|
152
|
+
...(cfg.runner?.resources ? { resources: cfg.runner.resources } : {}),
|
|
153
|
+
},
|
|
154
|
+
script: {
|
|
155
|
+
volumeClaim: {
|
|
156
|
+
name: volumeClaimResult.volumeClaimName,
|
|
157
|
+
file: volumeClaimResult.archiveFilename,
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
logger.debug('Constructed TestRun manifest (volume):', JSON.stringify(testRun, null, 2));
|
|
163
|
+
return testRun;
|
|
164
|
+
}
|
|
@@ -3,19 +3,18 @@ import { join, resolve } from 'node:path';
|
|
|
3
3
|
import { existsSync } from 'node:fs';
|
|
4
4
|
import { createDefaultKubernetesService } from '../../src/services/kubernetes.service';
|
|
5
5
|
import { ScriptService } from '../../src/services/script.service';
|
|
6
|
-
import { ConfigMapResult } from '../../src/types/kubernetes.types';
|
|
6
|
+
import { ConfigMapResult, VolumeClaimResult } from '../../src/types/kubernetes.types';
|
|
7
7
|
import { ArchiveResult } from '../../src/types/script.types';
|
|
8
8
|
import logger from '../../src/utils/logger';
|
|
9
9
|
import { TestRunManifest } from '../../src/types/testRunManifest.types';
|
|
10
10
|
import { K6Config } from '../../src/types/config.types';
|
|
11
11
|
import { loadK6Config } from '../../src/utils/configLoader';
|
|
12
|
-
import { buildTestRunManifest } from '../../src/utils/testRunManifestBuilder';
|
|
12
|
+
import { buildTestRunManifest, buildTestRunManifestWithVolumeClaim } from '../../src/utils/testRunManifestBuilder';
|
|
13
13
|
import { saveLastRun } from '../../src/utils/lastRunStore';
|
|
14
14
|
import { status } from '../../src/commands/status';
|
|
15
15
|
import { logs } from '../../src/commands/logs';
|
|
16
16
|
import { deleteLastRun } from '../../src/commands/delete';
|
|
17
17
|
|
|
18
|
-
|
|
19
18
|
const samplesPath = resolve(__dirname, '..', 'samples');
|
|
20
19
|
const scriptSample1 = join(samplesPath, 'k6_script_sample_1.js');
|
|
21
20
|
const scriptSample2 = join(samplesPath, 'k6_script_sample_2.js');
|
|
@@ -27,8 +26,11 @@ const kubernetesService = createDefaultKubernetesService();
|
|
|
27
26
|
let archiveOutput: ArchiveResult;
|
|
28
27
|
let configMapResult: ConfigMapResult;
|
|
29
28
|
|
|
30
|
-
|
|
29
|
+
let volumeArchiveOutput: ArchiveResult;
|
|
30
|
+
let volumeClaimResult: VolumeClaimResult;
|
|
31
|
+
let volumeTestRunManifest: TestRunManifest;
|
|
31
32
|
|
|
33
|
+
describe('KubernetesService integration tests', () => {
|
|
32
34
|
test('create config map from archived script', async () => {
|
|
33
35
|
archiveOutput = await scriptService.archiveTest(scriptSample2);
|
|
34
36
|
expect(existsSync(archiveOutput.archivePath)).toBe(true);
|
|
@@ -70,4 +72,58 @@ describe('KubernetesService integration tests', () => {
|
|
|
70
72
|
// Clean up the TestRun after successful creation and execution
|
|
71
73
|
await deleteLastRun({ namespace: testRunManifest.metadata.namespace });
|
|
72
74
|
}, 120000);
|
|
73
|
-
|
|
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,14 +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
|
-
|
|
15
|
-
|
|
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
|
+
});
|
|
16
21
|
|
|
17
22
|
jest.mock('@kubernetes/client-node', () => {
|
|
18
23
|
const mockApiClient = {
|
|
@@ -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', () => {
|