Skip to content
Open
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
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ java -jar volta-master/target/volta-master-1.0-SNAPSHOT.jar \
--agent=localhost:7070
```

Stats file (optional): `--stats-out=path`

- Ends with `.csv` → header row + CSV lines (`sample` each second + one `final` row).
- Any other suffix (for example `.jsonl`) → one JSON object per line (serialized `StatsSnapshot`).

Expected output:
```
[RPS: 0 | Success: 0.0% | Avg: 0ms | Errors: 0]
Expand Down
4 changes: 4 additions & 0 deletions volta-master/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@
<artifactId>mockito-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public void run(ApplicationArguments args) {

agentClient.startTest(masterArgs.agentUrl(), masterArgs.testConfig());

statsReporter.startReporting(masterArgs.agentUrl(), masterArgs.testConfig().duration());
statsReporter.startReporting(
masterArgs.agentUrl(), masterArgs.testConfig().duration(), masterArgs.outputFile());
}
}
73 changes: 64 additions & 9 deletions volta-master/src/main/java/com/volta/master/cli/ArgsParser.java
Original file line number Diff line number Diff line change
@@ -1,28 +1,83 @@
package com.volta.master.cli;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.volta.model.TestConfig;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
import org.springframework.boot.ApplicationArguments;

public class ArgsParser {

private static final String USAGE =
"""
Usage: java -jar volta-master.jar --url=<url> --rps=<rps> --duration=<seconds> --agent=<host:port>
Example: java -jar volta-master.jar --url=https://httpbin.org/get --rps=10 --duration=30 --agent=localhost:7070
""";
Usage: java -jar volta-master.jar --url=<url> --rps=<rps> --duration=<seconds> --agent=<host:port>
java -jar volta-master.jar --config=./<config.json(.yaml/.yml)> --agent=<host:port>

Example: java -jar volta-master.jar --url=https://httpbin.org/get --rps=10 --duration=30 --agent=localhost:7070
java -jar volta-master.jar --config=./config.json --agent=localhost:7070 [--stats-out=report.jsonl|report.csv]
""";

public static MasterArgs parse(ApplicationArguments args) {
String url = requireString(args, "url");
int rps = requirePositiveInt(args, "rps");
int duration = requirePositiveInt(args, "duration");
String agent = requireString(args, "agent");

validateUrl(url);
TestConfig testConfig;
if (args.containsOption("config")) {
String configPath = requireString(args, "config");

Path path = Path.of(configPath);
if (!Files.exists(path)) {
throw new ArgsException("Config file not found: " + configPath + "\n" + USAGE);
}
if (!Files.isRegularFile(path)) {
throw new ArgsException("Config path is not a regular file: " + configPath + "\n" + USAGE);
}

ObjectMapper mapper;
String name = path.getFileName().toString().toLowerCase();
if (name.endsWith(".json")) {
mapper = new ObjectMapper();
} else if (name.endsWith(".yaml") || name.endsWith(".yml")) {
mapper = new ObjectMapper(new YAMLFactory());
} else {
throw new ArgsException("Unsupported file format: " + configPath + "\n" + USAGE);
}

try {
testConfig = mapper.readValue(path.toFile(), TestConfig.class);
} catch (IOException e) {
throw new ArgsException(
"Failed to read/parse config: " + configPath + "\n" + e.getMessage() + "\n" + USAGE);
}

if (testConfig.rps() <= 0) {
throw new ArgsException("Argument rps in config must be positive\n" + USAGE);
}
if (testConfig.duration() <= 0) {
throw new ArgsException("Argument duration in config must be positive\n" + USAGE);
}
validateUrl(testConfig.url());

} else {
String url = requireString(args, "url");
int rps = requirePositiveInt(args, "rps");
int duration = requirePositiveInt(args, "duration");

validateUrl(url);
testConfig = new TestConfig(url, rps, duration);
}
String agent = requireString(args, "agent");
validateAgent(agent);

agent = "http://" + agent;

return new MasterArgs(new TestConfig(url, rps, duration), agent);
if (args.containsOption("stats-out")) {
String outputFile = requireString(args, "stats-out");
return new MasterArgs(testConfig, Optional.of(outputFile), agent);
}

return new MasterArgs(testConfig, Optional.empty(), agent);
}

private static String requireString(ApplicationArguments args, String name) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.volta.master.cli;

import com.volta.model.TestConfig;
import java.util.Optional;

public record MasterArgs(TestConfig testConfig, String agentUrl) {}
public record MasterArgs(TestConfig testConfig, Optional<String> outputFile, String agentUrl) {}
Loading
Loading