@gatling.io/cli 3.11.7 → 3.12.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/package.json +4 -4
- package/src/bundle.ts +36 -0
- package/src/commands/build.ts +30 -0
- package/src/commands/enterpriseDeploy.ts +88 -0
- package/src/commands/enterprisePackage.ts +41 -0
- package/src/commands/enterpriseStart.ts +113 -0
- package/src/commands/index.ts +25 -0
- package/src/commands/install.ts +19 -0
- package/src/commands/options.ts +263 -0
- package/src/commands/recorder.ts +41 -0
- package/src/commands/run.ts +78 -0
- package/src/commands/runOnly.ts +56 -0
- package/src/dependencies/coursier.ts +82 -0
- package/src/dependencies/download.ts +11 -0
- package/src/dependencies/graalVm.ts +74 -0
- package/src/dependencies/index.ts +44 -0
- package/src/dependencies/os.ts +24 -0
- package/src/dependencies/versions.ts +12 -0
- package/src/enterprise.ts +225 -0
- package/src/index.ts +5 -0
- package/src/java.ts +48 -0
- package/src/log.ts +19 -0
- package/src/readline.ts +39 -0
- package/src/run.ts +67 -0
- package/src/simulations.ts +29 -0
- package/target/commands/build.d.ts +3 -0
- package/target/commands/build.js +20 -0
- package/target/commands/enterpriseDeploy.d.ts +3 -0
- package/target/commands/enterpriseDeploy.js +59 -0
- package/target/commands/enterprisePackage.d.ts +3 -0
- package/target/commands/enterprisePackage.js +26 -0
- package/target/commands/enterpriseStart.d.ts +3 -0
- package/target/commands/enterpriseStart.js +75 -0
- package/target/commands/index.d.ts +2 -0
- package/target/commands/index.js +28 -0
- package/target/commands/install.d.ts +3 -0
- package/target/commands/install.js +18 -0
- package/target/commands/options.d.ts +46 -0
- package/target/commands/options.js +169 -0
- package/target/commands/recorder.d.ts +3 -0
- package/target/commands/recorder.js +28 -0
- package/target/commands/run.d.ts +3 -0
- package/target/commands/run.js +51 -0
- package/target/commands/runOnly.d.ts +3 -0
- package/target/commands/runOnly.js +37 -0
- package/target/dependencies/index.d.ts +1 -0
- package/target/dependencies/index.js +3 -1
- package/target/dependencies/versions.js +2 -2
- package/target/enterprise.js +5 -5
- package/target/index.js +2 -337
- package/target/run.js +4 -2
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
gatlingHomeOption,
|
|
5
|
+
gatlingHomeOptionValueWithDefaults,
|
|
6
|
+
resourcesFolderOption,
|
|
7
|
+
resourcesFolderOptionValue,
|
|
8
|
+
sourcesFolderOption,
|
|
9
|
+
sourcesFolderOptionValue,
|
|
10
|
+
typescriptOption,
|
|
11
|
+
typescriptOptionValueWithDefaults
|
|
12
|
+
} from "./options";
|
|
13
|
+
import { findSimulations } from "../simulations";
|
|
14
|
+
import { installRecorder } from "../dependencies";
|
|
15
|
+
import { logger } from "../log";
|
|
16
|
+
import { runRecorder } from "../run";
|
|
17
|
+
|
|
18
|
+
export default (program: Command): void => {
|
|
19
|
+
program
|
|
20
|
+
.command("recorder")
|
|
21
|
+
.description("Run the Gatling recorder")
|
|
22
|
+
.addOption(gatlingHomeOption)
|
|
23
|
+
.addOption(sourcesFolderOption)
|
|
24
|
+
.addOption(typescriptOption)
|
|
25
|
+
.addOption(resourcesFolderOption)
|
|
26
|
+
.action(async (options) => {
|
|
27
|
+
const gatlingHome: string = gatlingHomeOptionValueWithDefaults(options);
|
|
28
|
+
const sourcesFolder: string = sourcesFolderOptionValue(options);
|
|
29
|
+
const resourcesFolder: string = resourcesFolderOptionValue(options);
|
|
30
|
+
|
|
31
|
+
const simulations = await findSimulations(sourcesFolder);
|
|
32
|
+
const typescript = typescriptOptionValueWithDefaults(options, simulations);
|
|
33
|
+
|
|
34
|
+
const { graalvmHome, coursierBinary, jvmClasspath } = await installRecorder({ gatlingHome });
|
|
35
|
+
logger.debug(`graalvmHome=${graalvmHome}`);
|
|
36
|
+
logger.debug(`coursierBinary=${coursierBinary}`);
|
|
37
|
+
logger.debug(`jvmClasspath=${jvmClasspath}`);
|
|
38
|
+
|
|
39
|
+
await runRecorder({ graalvmHome, jvmClasspath, sourcesFolder, typescript, resourcesFolder });
|
|
40
|
+
});
|
|
41
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import {
|
|
3
|
+
bundleFileOption,
|
|
4
|
+
bundleFileOptionValue,
|
|
5
|
+
gatlingHomeOption,
|
|
6
|
+
gatlingHomeOptionValueWithDefaults,
|
|
7
|
+
memoryOption,
|
|
8
|
+
memoryOptionValue,
|
|
9
|
+
nonInteractiveOption,
|
|
10
|
+
nonInteractiveOptionValue,
|
|
11
|
+
parseRunParametersArgument,
|
|
12
|
+
resourcesFolderOption,
|
|
13
|
+
resourcesFolderOptionValue,
|
|
14
|
+
resultsFolderOption,
|
|
15
|
+
resultsFolderOptionValue,
|
|
16
|
+
runParametersArgument,
|
|
17
|
+
simulationOption,
|
|
18
|
+
simulationOptionValueWithDefaults,
|
|
19
|
+
sourcesFolderOption,
|
|
20
|
+
sourcesFolderOptionValue,
|
|
21
|
+
typescriptOption,
|
|
22
|
+
typescriptOptionValueWithDefaults
|
|
23
|
+
} from "./options";
|
|
24
|
+
import { findSimulations } from "../simulations";
|
|
25
|
+
import { installGatlingJs } from "../dependencies";
|
|
26
|
+
import { logger } from "../log";
|
|
27
|
+
import { bundle } from "../bundle";
|
|
28
|
+
import { runSimulation } from "../run";
|
|
29
|
+
|
|
30
|
+
export default (program: Command): void => {
|
|
31
|
+
program
|
|
32
|
+
.command("run")
|
|
33
|
+
.description(
|
|
34
|
+
"Build and run a Gatling simulation, after installing all required components and dependencies for Gatling"
|
|
35
|
+
)
|
|
36
|
+
.addOption(sourcesFolderOption)
|
|
37
|
+
.addOption(simulationOption)
|
|
38
|
+
.addOption(typescriptOption)
|
|
39
|
+
.addOption(bundleFileOption)
|
|
40
|
+
.addOption(resourcesFolderOption)
|
|
41
|
+
.addOption(resultsFolderOption)
|
|
42
|
+
.addOption(gatlingHomeOption)
|
|
43
|
+
.addOption(memoryOption)
|
|
44
|
+
.addOption(nonInteractiveOption)
|
|
45
|
+
.addArgument(runParametersArgument)
|
|
46
|
+
.action(async (args: string[], options) => {
|
|
47
|
+
const gatlingHome = gatlingHomeOptionValueWithDefaults(options);
|
|
48
|
+
const sourcesFolder: string = sourcesFolderOptionValue(options);
|
|
49
|
+
const bundleFile = bundleFileOptionValue(options);
|
|
50
|
+
const resourcesFolder: string = resourcesFolderOptionValue(options);
|
|
51
|
+
const resultsFolder: string = resultsFolderOptionValue(options);
|
|
52
|
+
const memory: number | undefined = memoryOptionValue(options);
|
|
53
|
+
const nonInteractive: boolean = nonInteractiveOptionValue(options);
|
|
54
|
+
const runParameters = parseRunParametersArgument(args);
|
|
55
|
+
|
|
56
|
+
const simulations = await findSimulations(sourcesFolder);
|
|
57
|
+
const typescript = typescriptOptionValueWithDefaults(options, simulations);
|
|
58
|
+
const simulation = simulationOptionValueWithDefaults(options, simulations, !nonInteractive);
|
|
59
|
+
|
|
60
|
+
const { graalvmHome, coursierBinary, jvmClasspath } = await installGatlingJs({ gatlingHome });
|
|
61
|
+
logger.debug(`graalvmHome=${graalvmHome}`);
|
|
62
|
+
logger.debug(`coursierBinary=${coursierBinary}`);
|
|
63
|
+
logger.debug(`jvmClasspath=${jvmClasspath}`);
|
|
64
|
+
|
|
65
|
+
await bundle({ sourcesFolder, bundleFile, typescript, simulations });
|
|
66
|
+
|
|
67
|
+
await runSimulation({
|
|
68
|
+
graalvmHome,
|
|
69
|
+
jvmClasspath,
|
|
70
|
+
simulation,
|
|
71
|
+
bundleFile,
|
|
72
|
+
resourcesFolder,
|
|
73
|
+
resultsFolder,
|
|
74
|
+
memory,
|
|
75
|
+
runParameters
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
bundleFileOption,
|
|
5
|
+
bundleFileOptionValue,
|
|
6
|
+
graalvmHomeMandatoryOption,
|
|
7
|
+
graalvmHomeMandatoryOptionValue,
|
|
8
|
+
jvmClasspathMandatoryOption,
|
|
9
|
+
jvmClasspathMandatoryOptionValue,
|
|
10
|
+
memoryOption,
|
|
11
|
+
memoryOptionValue,
|
|
12
|
+
parseRunParametersArgument,
|
|
13
|
+
resourcesFolderOption,
|
|
14
|
+
resourcesFolderOptionValue,
|
|
15
|
+
resultsFolderOption,
|
|
16
|
+
resultsFolderOptionValue,
|
|
17
|
+
runParametersArgument,
|
|
18
|
+
simulationMandatoryOption,
|
|
19
|
+
simulationMandatoryOptionValue
|
|
20
|
+
} from "./options";
|
|
21
|
+
import { runSimulation } from "../run";
|
|
22
|
+
|
|
23
|
+
export default (program: Command): void => {
|
|
24
|
+
program
|
|
25
|
+
.command("run-only")
|
|
26
|
+
.description("Run a Gatling simulation from an already built bundle")
|
|
27
|
+
.addOption(graalvmHomeMandatoryOption)
|
|
28
|
+
.addOption(jvmClasspathMandatoryOption)
|
|
29
|
+
.addOption(simulationMandatoryOption)
|
|
30
|
+
.addOption(bundleFileOption)
|
|
31
|
+
.addOption(resourcesFolderOption)
|
|
32
|
+
.addOption(resultsFolderOption)
|
|
33
|
+
.addOption(memoryOption)
|
|
34
|
+
.addArgument(runParametersArgument)
|
|
35
|
+
.action(async (args: string[], options) => {
|
|
36
|
+
const graalvmHome: string = graalvmHomeMandatoryOptionValue(options);
|
|
37
|
+
const jvmClasspath: string = jvmClasspathMandatoryOptionValue(options);
|
|
38
|
+
const simulation: string = simulationMandatoryOptionValue(options);
|
|
39
|
+
const bundleFile = bundleFileOptionValue(options);
|
|
40
|
+
const resourcesFolder: string = resourcesFolderOptionValue(options);
|
|
41
|
+
const resultsFolder: string = resultsFolderOptionValue(options);
|
|
42
|
+
const memory: number | undefined = memoryOptionValue(options);
|
|
43
|
+
const runParameters = parseRunParametersArgument(args);
|
|
44
|
+
|
|
45
|
+
await runSimulation({
|
|
46
|
+
graalvmHome,
|
|
47
|
+
jvmClasspath,
|
|
48
|
+
simulation: simulation,
|
|
49
|
+
bundleFile,
|
|
50
|
+
resourcesFolder,
|
|
51
|
+
resultsFolder,
|
|
52
|
+
memory,
|
|
53
|
+
runParameters
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { existsSync } from "fs";
|
|
2
|
+
import fs from "fs/promises";
|
|
3
|
+
|
|
4
|
+
import { downloadFile } from "./download";
|
|
5
|
+
import { logger } from "../log";
|
|
6
|
+
import { versions } from "./versions";
|
|
7
|
+
import { promisify } from "util";
|
|
8
|
+
import { exec } from "child_process";
|
|
9
|
+
import { osType } from "./os";
|
|
10
|
+
|
|
11
|
+
export const installCoursier = async (gatlingHomeDir: string, downloadDir: string): Promise<string> => {
|
|
12
|
+
const coursierRootPath = `${gatlingHomeDir}/coursier/${versions.coursier}`;
|
|
13
|
+
const coursierPath = `${coursierRootPath}/cs`;
|
|
14
|
+
|
|
15
|
+
if (!existsSync(coursierPath)) {
|
|
16
|
+
const jarUrl = `https://github.com/coursier/coursier/releases/download/v${versions.coursier}/coursier`;
|
|
17
|
+
const windowsBatUrl = `https://github.com/coursier/launchers/raw/master/coursier.bat`;
|
|
18
|
+
const downloadPath = `${downloadDir}/cs`;
|
|
19
|
+
|
|
20
|
+
if (existsSync(coursierRootPath)) {
|
|
21
|
+
await fs.rm(coursierRootPath, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
if (existsSync(downloadPath)) {
|
|
24
|
+
await fs.rm(downloadPath);
|
|
25
|
+
}
|
|
26
|
+
await fs.mkdir(coursierRootPath, { recursive: true });
|
|
27
|
+
|
|
28
|
+
logger.info(`Downloading Coursier ${versions.coursier} to ${downloadPath}`);
|
|
29
|
+
await downloadFile(jarUrl, downloadPath);
|
|
30
|
+
if (osType === "Windows_NT") {
|
|
31
|
+
await downloadFile(windowsBatUrl, `${downloadPath}.bat`);
|
|
32
|
+
} else {
|
|
33
|
+
await fs.chmod(downloadPath, 0o744);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
logger.info(`Installing Coursier to ${coursierPath}`);
|
|
37
|
+
await fs.rename(downloadPath, coursierPath);
|
|
38
|
+
if (osType === "Windows_NT") {
|
|
39
|
+
await fs.rename(`${downloadPath}.bat`, `${coursierPath}.bat`);
|
|
40
|
+
}
|
|
41
|
+
} else {
|
|
42
|
+
logger.info(`Coursier ${versions.coursier} already installed at ${coursierPath}`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return coursierPath;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const resolveGatlingJsDependencies = async (coursierPath: string, javaHome: string): Promise<string> => {
|
|
49
|
+
const gatlingDep = `"io.gatling.highcharts:gatling-charts-highcharts:${versions.gatling.core}"`;
|
|
50
|
+
const gatlingAdapterDep = `"io.gatling:gatling-jvm-to-js-adapter:${versions.gatling.jsAdapter}"`;
|
|
51
|
+
const gatlingEnterprisePluginCommonsDep = `"io.gatling:gatling-enterprise-plugin-commons:${versions.gatling.enterprisePluginCommons}"`;
|
|
52
|
+
const graalvmJsDep = `"org.graalvm.polyglot:js-community:${versions.graalvm.js}"`;
|
|
53
|
+
|
|
54
|
+
return await resolveDependencies(
|
|
55
|
+
coursierPath,
|
|
56
|
+
javaHome,
|
|
57
|
+
gatlingDep,
|
|
58
|
+
gatlingAdapterDep,
|
|
59
|
+
gatlingEnterprisePluginCommonsDep,
|
|
60
|
+
graalvmJsDep
|
|
61
|
+
);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const resolveRecorderDependencies = async (coursierPath: string, javaHome: string): Promise<string> => {
|
|
65
|
+
const recorderDep = `io.gatling:gatling-recorder:${versions.gatling.core}`;
|
|
66
|
+
return await resolveDependencies(coursierPath, javaHome, recorderDep);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const resolveDependencies = async (
|
|
70
|
+
coursierPath: string,
|
|
71
|
+
javaHome: string,
|
|
72
|
+
...dependencies: string[]
|
|
73
|
+
): Promise<string> => {
|
|
74
|
+
const command = `"${coursierPath}" fetch --classpath ${dependencies.join(" ")}`;
|
|
75
|
+
|
|
76
|
+
// TODO could add a timeout
|
|
77
|
+
logger.info(`Resolving dependencies with Coursier`);
|
|
78
|
+
const { stdout } = await execAsync(command, { env: { ...process.env, JAVA_HOME: javaHome } });
|
|
79
|
+
return stdout;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const execAsync = promisify(exec);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import stream from "stream";
|
|
4
|
+
import util from "util";
|
|
5
|
+
|
|
6
|
+
const pipeline = util.promisify(stream.pipeline);
|
|
7
|
+
|
|
8
|
+
export const downloadFile = async (url: string, targetFile: string): Promise<void> => {
|
|
9
|
+
const request = await axios.get(url, { responseType: "stream" });
|
|
10
|
+
await pipeline(request.data, fs.createWriteStream(targetFile));
|
|
11
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { existsSync } from "fs";
|
|
2
|
+
import fs from "fs/promises";
|
|
3
|
+
import decompress from "decompress";
|
|
4
|
+
|
|
5
|
+
import { downloadFile } from "./download";
|
|
6
|
+
import { logger } from "../log";
|
|
7
|
+
import { osType, osArch } from "./os";
|
|
8
|
+
import { versions } from "./versions";
|
|
9
|
+
|
|
10
|
+
export const installGraalVm = async (gatlingHomeDir: string, downloadDir: string): Promise<string> => {
|
|
11
|
+
const { os, arch, extension, homePath, binPath } = graalVmPlatformParams();
|
|
12
|
+
const graalvmRootPath = `${gatlingHomeDir}/graalvm/${versions.graalvm.jdk}`;
|
|
13
|
+
const graalvmHomePath = `${graalvmRootPath}${homePath}`;
|
|
14
|
+
const graalvmJavaPath = `${graalvmHomePath}${binPath}`;
|
|
15
|
+
|
|
16
|
+
if (!existsSync(graalvmJavaPath)) {
|
|
17
|
+
const url = `https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-${versions.graalvm.jdk}/graalvm-community-jdk-${versions.graalvm.jdk}_${os}-${arch}_bin.${extension}`;
|
|
18
|
+
const downloadPath = `${downloadDir}/graalvm.${extension}`;
|
|
19
|
+
|
|
20
|
+
if (existsSync(graalvmRootPath)) {
|
|
21
|
+
await fs.rm(graalvmRootPath, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
if (existsSync(downloadPath)) {
|
|
24
|
+
await fs.rm(downloadPath);
|
|
25
|
+
}
|
|
26
|
+
await fs.mkdir(graalvmRootPath, { recursive: true });
|
|
27
|
+
|
|
28
|
+
logger.info(`Downloading GraalVM Community Edition ${versions.graalvm.jdk} to ${downloadPath}`);
|
|
29
|
+
await downloadFile(url, downloadPath);
|
|
30
|
+
|
|
31
|
+
logger.info(`Unpacking GraalVM to ${graalvmRootPath}`);
|
|
32
|
+
await decompress(downloadPath, graalvmRootPath, {
|
|
33
|
+
map: (file) => {
|
|
34
|
+
// Remove first level of file name, because it already contains a root directory
|
|
35
|
+
file.path = file.path.split("/").slice(1).join("/");
|
|
36
|
+
return file;
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
await fs.rm(downloadPath);
|
|
41
|
+
} else {
|
|
42
|
+
logger.info(`GraalVM Community Edition ${versions.graalvm.jdk} already installed at ${graalvmRootPath}`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return graalvmHomePath;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const graalVmPlatformParams = () => {
|
|
49
|
+
if (osType === "Linux") {
|
|
50
|
+
const os = "linux";
|
|
51
|
+
const extension = "tar.gz";
|
|
52
|
+
const homePath = "";
|
|
53
|
+
const binPath = "/bin/java";
|
|
54
|
+
if (osArch === "x64") {
|
|
55
|
+
return { os, arch: "x64", extension, homePath, binPath };
|
|
56
|
+
} else if (osArch === "arm64") {
|
|
57
|
+
return { os, arch: "aarch64", extension, homePath, binPath };
|
|
58
|
+
}
|
|
59
|
+
} else if (osType === "Darwin") {
|
|
60
|
+
const os = "macos";
|
|
61
|
+
const extension = "tar.gz";
|
|
62
|
+
const homePath = "/Contents/Home";
|
|
63
|
+
const binPath = "/bin/java";
|
|
64
|
+
if (osArch === "x64") {
|
|
65
|
+
return { os, arch: "x64", extension, homePath, binPath };
|
|
66
|
+
} else if (osArch === "arm64") {
|
|
67
|
+
return { os, arch: "aarch64", extension, homePath, binPath };
|
|
68
|
+
}
|
|
69
|
+
} else if (osType === "Windows_NT" && osArch === "x64") {
|
|
70
|
+
return { os: "windows", arch: "x64", extension: "zip", homePath: "", binPath: "/bin/java.exe" };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
throw Error(`Operating system type '${osType}' with architecture '${osArch}' is not currently supported.`);
|
|
74
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import fs from "fs/promises";
|
|
2
|
+
|
|
3
|
+
import { installCoursier, resolveGatlingJsDependencies, resolveRecorderDependencies } from "./coursier";
|
|
4
|
+
import { installGraalVm } from "./graalVm";
|
|
5
|
+
|
|
6
|
+
export { versions } from "./versions";
|
|
7
|
+
|
|
8
|
+
export interface DependenciesOptions {
|
|
9
|
+
gatlingHome: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface ResolvedDependencies {
|
|
13
|
+
graalvmHome: string;
|
|
14
|
+
coursierBinary: string;
|
|
15
|
+
jvmClasspath: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const installGatlingJs = async (options: DependenciesOptions): Promise<ResolvedDependencies> => {
|
|
19
|
+
const downloadDir = `${options.gatlingHome}/tmp/download`;
|
|
20
|
+
await fs.mkdir(downloadDir, { recursive: true });
|
|
21
|
+
|
|
22
|
+
const graalvmHomePath = await installGraalVm(options.gatlingHome, downloadDir);
|
|
23
|
+
const coursierPath = await installCoursier(options.gatlingHome, downloadDir);
|
|
24
|
+
const classpath = await resolveGatlingJsDependencies(coursierPath, graalvmHomePath);
|
|
25
|
+
return {
|
|
26
|
+
graalvmHome: graalvmHomePath,
|
|
27
|
+
coursierBinary: coursierPath,
|
|
28
|
+
jvmClasspath: classpath.trim()
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const installRecorder = async (options: DependenciesOptions): Promise<ResolvedDependencies> => {
|
|
33
|
+
const downloadDir = `${options.gatlingHome}/tmp/download`;
|
|
34
|
+
await fs.mkdir(downloadDir, { recursive: true });
|
|
35
|
+
|
|
36
|
+
const graalvmHomePath = await installGraalVm(options.gatlingHome, downloadDir);
|
|
37
|
+
const coursierPath = await installCoursier(options.gatlingHome, downloadDir);
|
|
38
|
+
const classpath = await resolveRecorderDependencies(coursierPath, graalvmHomePath);
|
|
39
|
+
return {
|
|
40
|
+
graalvmHome: graalvmHomePath,
|
|
41
|
+
coursierBinary: coursierPath,
|
|
42
|
+
jvmClasspath: classpath.trim()
|
|
43
|
+
};
|
|
44
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import os from "os";
|
|
2
|
+
|
|
3
|
+
const resolveOsType = () => {
|
|
4
|
+
const t = os.type();
|
|
5
|
+
if (t !== "Darwin" && t !== "Linux" && t !== "Windows_NT") {
|
|
6
|
+
throw Error(`Gatling JS does not support the Operating System '${t}'`);
|
|
7
|
+
}
|
|
8
|
+
return t;
|
|
9
|
+
};
|
|
10
|
+
export const osType: "Darwin" | "Linux" | "Windows_NT" = resolveOsType();
|
|
11
|
+
|
|
12
|
+
const resolveOsArch = () => {
|
|
13
|
+
const a = os.arch();
|
|
14
|
+
if (a !== "x64" && a !== "arm64") {
|
|
15
|
+
throw Error(`Gatling JS does not support the architecture '${a}'`);
|
|
16
|
+
}
|
|
17
|
+
if (osType === "Windows_NT" && a === "arm64") {
|
|
18
|
+
// GraalVM distribution not available for Windows on ARM
|
|
19
|
+
// TODO see if we can recommend a solution
|
|
20
|
+
throw Error(`Gatling JS does not support Windows on the ARM architecture`);
|
|
21
|
+
}
|
|
22
|
+
return a;
|
|
23
|
+
};
|
|
24
|
+
export const osArch: "x64" | "arm64" = resolveOsArch();
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import archiver from "archiver";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import { pipeline } from "stream/promises";
|
|
4
|
+
import { constants as zConstants } from "zlib";
|
|
5
|
+
|
|
6
|
+
import { versions } from "./dependencies";
|
|
7
|
+
import { RunJavaProcessOptions, runJavaProcess } from "./java";
|
|
8
|
+
import { logger } from "./log";
|
|
9
|
+
import { SimulationFile } from "./simulations";
|
|
10
|
+
|
|
11
|
+
export interface EnterprisePackageOptions {
|
|
12
|
+
bundleFile: string;
|
|
13
|
+
resourcesFolder: string;
|
|
14
|
+
packageFile: string;
|
|
15
|
+
simulations: SimulationFile[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const enterprisePackage = async (options: EnterprisePackageOptions): Promise<void> => {
|
|
19
|
+
logger.info(`Packaging a Gatling simulation with options:
|
|
20
|
+
- bundleFile: ${options.bundleFile}
|
|
21
|
+
- packageFile: ${options.packageFile}`);
|
|
22
|
+
|
|
23
|
+
const manifest = generateManifest(options.simulations.map((s) => s.name));
|
|
24
|
+
|
|
25
|
+
const output = fs.createWriteStream(options.packageFile);
|
|
26
|
+
|
|
27
|
+
const archive = archiver("zip", {
|
|
28
|
+
zlib: { level: zConstants.Z_MAX_LEVEL }
|
|
29
|
+
});
|
|
30
|
+
archive.on("warning", (err) => {
|
|
31
|
+
// The pipeline will rethrow errors but not warnings. We don't want to ignore warnings from the archiver, because
|
|
32
|
+
// they include things like 'no such file or directory'.
|
|
33
|
+
throw err;
|
|
34
|
+
});
|
|
35
|
+
archive.file(options.bundleFile, { name: "bundle.js" });
|
|
36
|
+
archive.append(Buffer.from(manifest), { name: "META-INF/MANIFEST.MF" });
|
|
37
|
+
archive.directory(options.resourcesFolder + "/", false);
|
|
38
|
+
archive.finalize();
|
|
39
|
+
|
|
40
|
+
await pipeline(archive, output);
|
|
41
|
+
|
|
42
|
+
logger.info(`Package for Gatling Enterprise created at ${options.packageFile}`);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const generateManifest = (simulationNames: string[]) => {
|
|
46
|
+
const utf8Encoder = new TextEncoder();
|
|
47
|
+
const eol = utf8Encoder.encode("\n");
|
|
48
|
+
const continuation = utf8Encoder.encode("\n ");
|
|
49
|
+
const lines = [
|
|
50
|
+
"Manifest-Version: 1.0",
|
|
51
|
+
"Specification-Vendor: GatlingCorp",
|
|
52
|
+
"Gatling-Context: js",
|
|
53
|
+
`Gatling-Version: ${versions.gatling.core}`,
|
|
54
|
+
"Gatling-Packager: js-cli",
|
|
55
|
+
`Gatling-Packager-Version: ${versions.gatling.jsAdapter}`,
|
|
56
|
+
`Gatling-Simulations: ${simulationNames.join(",")}`,
|
|
57
|
+
`Java-Version: ${versions.graalvm.jdk.split(".")[0]}`
|
|
58
|
+
];
|
|
59
|
+
const pkg = getPackageNameAndVersion();
|
|
60
|
+
lines.push(`Implementation-Title: ${pkg.name}`);
|
|
61
|
+
if (pkg.version !== undefined) {
|
|
62
|
+
lines.push(`Implementation-Version: ${pkg.version}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let totalLength = 0;
|
|
66
|
+
const buffer: Uint8Array[] = [];
|
|
67
|
+
for (const line of lines) {
|
|
68
|
+
let lineLength = 0;
|
|
69
|
+
for (const char of line) {
|
|
70
|
+
const bytes = utf8Encoder.encode(char);
|
|
71
|
+
const byteLength = bytes.byteLength;
|
|
72
|
+
if (lineLength + byteLength > 71) {
|
|
73
|
+
buffer.push(continuation);
|
|
74
|
+
buffer.push(bytes);
|
|
75
|
+
// reset length for the new line (with +1 for leading space)
|
|
76
|
+
lineLength = byteLength + 1;
|
|
77
|
+
totalLength += byteLength + 2;
|
|
78
|
+
} else {
|
|
79
|
+
buffer.push(bytes);
|
|
80
|
+
lineLength += byteLength;
|
|
81
|
+
totalLength += byteLength;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
buffer.push(eol);
|
|
85
|
+
totalLength += 1;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const manifest = new Uint8Array(totalLength);
|
|
89
|
+
let cursor = 0;
|
|
90
|
+
for (const elt of buffer) {
|
|
91
|
+
manifest.set(elt, cursor);
|
|
92
|
+
cursor += elt.byteLength;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return manifest;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const getPackageNameAndVersion = (): { name: string; version?: string } => {
|
|
99
|
+
// npm_package_* env vars are available when launching CLI with npx
|
|
100
|
+
let name = process.env.npm_package_name;
|
|
101
|
+
let version = process.env.npm_package_version;
|
|
102
|
+
// otherwise, try to read from package.json file
|
|
103
|
+
if (name === undefined || version === undefined) {
|
|
104
|
+
if (!fs.existsSync("package.json")) {
|
|
105
|
+
throw Error("package.json not found");
|
|
106
|
+
}
|
|
107
|
+
const pkg = JSON.parse(fs.readFileSync("package.json", "utf-8"));
|
|
108
|
+
if (name === undefined) {
|
|
109
|
+
if (typeof pkg.name === "string") {
|
|
110
|
+
name = pkg.name;
|
|
111
|
+
} else {
|
|
112
|
+
throw Error("package.json does not contain a valid package name");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (version === undefined && typeof pkg.version === "string") {
|
|
116
|
+
version = pkg.version;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return { name: name as string, version: version };
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export interface EnterprisePluginOptions extends RunJavaProcessOptions {
|
|
123
|
+
bundleFile: string;
|
|
124
|
+
resourcesFolder: string;
|
|
125
|
+
resultsFolder: string;
|
|
126
|
+
// Base
|
|
127
|
+
url: string;
|
|
128
|
+
apiToken?: string;
|
|
129
|
+
// Plugin configuration
|
|
130
|
+
controlPlaneUrl?: string;
|
|
131
|
+
nonInteractive: boolean;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const javaArgsFromPluginOptions = (options: EnterprisePluginOptions) => {
|
|
135
|
+
const javaArgs: string[] = [];
|
|
136
|
+
|
|
137
|
+
// Base
|
|
138
|
+
javaArgs.push(`-Dgatling.enterprise.url=${options.url}`);
|
|
139
|
+
if (options.apiToken !== undefined) {
|
|
140
|
+
javaArgs.push(`-Dgatling.enterprise.apiToken=${options.apiToken}`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Plugin configuration
|
|
144
|
+
if (options.controlPlaneUrl !== undefined) {
|
|
145
|
+
javaArgs.push(`-Dgatling.enterprise.controlPlaneUrl=${options.controlPlaneUrl}`);
|
|
146
|
+
}
|
|
147
|
+
javaArgs.push("-Dgatling.enterprise.buildTool=js-cli");
|
|
148
|
+
javaArgs.push(`-Dgatling.enterprise.pluginVersion=${versions.gatling.jsAdapter}`);
|
|
149
|
+
|
|
150
|
+
if (options.nonInteractive) {
|
|
151
|
+
javaArgs.push(`-Dgatling.enterprise.batchMode=true`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return javaArgs;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
export interface EnterpriseDeployOptions extends EnterprisePluginOptions {
|
|
158
|
+
// Descriptor file
|
|
159
|
+
packageDescriptorFilename: string;
|
|
160
|
+
// Deployment info
|
|
161
|
+
packageFile: string;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const javaArgsFromDeployOptions = (options: EnterpriseDeployOptions) => {
|
|
165
|
+
const javaArgs = javaArgsFromPluginOptions(options);
|
|
166
|
+
|
|
167
|
+
// Descriptor file
|
|
168
|
+
javaArgs.push(`-Dgatling.enterprise.baseDirectory=${process.cwd()}`);
|
|
169
|
+
javaArgs.push(`-Dgatling.enterprise.packageDescriptorFilename=${options.packageDescriptorFilename}`);
|
|
170
|
+
|
|
171
|
+
// Deployment info
|
|
172
|
+
javaArgs.push(`-Dgatling.enterprise.packageFile=${options.packageFile}`);
|
|
173
|
+
javaArgs.push(`-Dgatling.enterprise.artifactId=${getPackageNameAndVersion().name}`);
|
|
174
|
+
|
|
175
|
+
return javaArgs;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
export const enterpriseDeploy = async (options: EnterpriseDeployOptions): Promise<void> => {
|
|
179
|
+
const additionalClasspathElements = [options.resourcesFolder];
|
|
180
|
+
const javaArgs = javaArgsFromDeployOptions(options);
|
|
181
|
+
|
|
182
|
+
if (process.env["DEBUG"] === "true") {
|
|
183
|
+
logger.debug("Java arguments:");
|
|
184
|
+
for (let i = 0; i < javaArgs.length; i++) {
|
|
185
|
+
logger.debug(" " + javaArgs[i]);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return runJavaProcess(options, "io.gatling.plugin.cli.EnterpriseDeploy", additionalClasspathElements, javaArgs, []);
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
export interface EnterpriseStartOptions extends EnterpriseDeployOptions {
|
|
193
|
+
enterpriseSimulation?: string;
|
|
194
|
+
runTitle?: string;
|
|
195
|
+
runDescription?: string;
|
|
196
|
+
waitForRunEnd?: boolean;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export const enterpriseStart = async (options: EnterpriseStartOptions): Promise<void> => {
|
|
200
|
+
const additionalClasspathElements = [options.resourcesFolder];
|
|
201
|
+
const javaArgs = javaArgsFromDeployOptions(options);
|
|
202
|
+
|
|
203
|
+
// Start
|
|
204
|
+
if (options.enterpriseSimulation !== undefined) {
|
|
205
|
+
javaArgs.push(`-Dgatling.enterprise.simulationName=${options.enterpriseSimulation}`);
|
|
206
|
+
}
|
|
207
|
+
if (options.runTitle !== undefined) {
|
|
208
|
+
javaArgs.push(`-Dgatling.enterprise.runTitle=${options.runTitle}`);
|
|
209
|
+
}
|
|
210
|
+
if (options.runDescription !== undefined) {
|
|
211
|
+
javaArgs.push(`-Dgatling.enterprise.runDescription=${options.runDescription}`);
|
|
212
|
+
}
|
|
213
|
+
if (options.waitForRunEnd) {
|
|
214
|
+
javaArgs.push("-Dgatling.enterprise.waitForRunEnd=true");
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (process.env["DEBUG"] === "true") {
|
|
218
|
+
logger.debug("Java arguments:");
|
|
219
|
+
for (let i = 0; i < javaArgs.length; i++) {
|
|
220
|
+
logger.debug(" " + javaArgs[i]);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return runJavaProcess(options, "io.gatling.plugin.cli.EnterpriseStart", additionalClasspathElements, javaArgs, []);
|
|
225
|
+
};
|