Skip to content

Commit

Permalink
Use picocli for command line parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
hwbllmnn committed Nov 13, 2023
1 parent 182ee7b commit 1e7c84e
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 68 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:

strategy:
matrix:
java-version: [ 11, 17 ]
java-version: [ 11, 17, 21 ]

steps:
- uses: actions/checkout@v4
Expand Down
30 changes: 21 additions & 9 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
<sonar.host.url>https://sq.terrestris.de</sonar.host.url>
<sonar.login>terrestris</sonar.login>
<sonar.sources>src/main/</sonar.sources>
<junit-jupiter.version>5.10.1</junit-jupiter.version>
</properties>

<profiles>
Expand Down Expand Up @@ -248,6 +249,12 @@

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.kohsuke.metainf-services</groupId>
<artifactId>metainf-services</artifactId>
<version>1.11</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
Expand Down Expand Up @@ -286,30 +293,30 @@
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.1</version>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.10.1</version>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.15.0</version>
</dependency>
<dependency>
<groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId>
<version>1.6.0</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.16.0</version>
</dependency>
<dependency>
<groupId>info.picocli</groupId>
<artifactId>picocli</artifactId>
<version>4.7.5</version>
</dependency>
</dependencies>
</dependencyManagement>

Expand Down Expand Up @@ -351,8 +358,13 @@
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId>
<groupId>info.picocli</groupId>
<artifactId>picocli</artifactId>
</dependency>
<dependency>
<groupId>org.kohsuke.metainf-services</groupId>
<artifactId>metainf-services</artifactId>
<optional>true</optional>
</dependency>
</dependencies>

Expand Down
127 changes: 71 additions & 56 deletions src/main/java/de/terrestris/shogun/migrator/Migrator.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,66 +4,91 @@
import de.terrestris.shogun.migrator.model.HostDto;
import de.terrestris.shogun.migrator.spi.ShogunMigrator;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.cli.*;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;

import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.ServiceLoader;
import java.util.concurrent.Callable;

import static de.terrestris.shogun.migrator.util.ApiUtil.delete;
import static de.terrestris.shogun.migrator.util.ApiUtil.fetch;

@Log4j2
public class Migrator {

private static final Options OPTIONS = new Options()
.addOption(Option.builder()
.hasArg(true)
.argName("type")
.optionalArg(true)
.option("t")
.longOpt("type")
.desc("specify the type of the source (shogun2, boot), default is shogun2. Note that other types may be added via plugins")
.build())
.addOption("h", "help", false, "display this help message and exit")
.addOption("c", "clear", false, "DANGER: delete all layer and application entities from the target system before the migration")
.addRequiredOption("sh", "source-host", true, "the source API endpoint, e.g. https://my-shogun.com/shogun2-webapp/")
.addRequiredOption("su", "source-user", true, "the source admin username")
.addRequiredOption("sp", "source-password", true, "the source admin password")
.addRequiredOption("th", "target-host", true, "the target API endpoint, e.g. https://my-shogun-boot.com/")
.addRequiredOption("tu", "target-user", true, "the target admin username")
.addRequiredOption("tp", "target-password", true, "the target admin password");

private static CommandLine parseOptions(String[] args) throws ParseException {
CommandLineParser parser = new DefaultParser();
return parser.parse(OPTIONS, args);
}
@Command(name = "SHOGun-Migrator", version = "0.0.1", mixinStandardHelpOptions = true)
public class Migrator implements Callable<Boolean> {

private static void printHelp() {
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp(
150,
"java -jar shogun-migrator.jar",
"SHOGun migrator",
OPTIONS,
"Check out the source: https://github.com/terrestris/shogun-migrator/",
true
);
}
enum Type { shogun2, boot }

@Option(
names = {"-t", "--type"},
description = "specify the type of the source (${COMPLETION-CANDIDATES}), default is shogun2. Note that other types may be added via plugins"
)
private Type type = Type.shogun2;

@Option(
names = {"-c", "--clear"},
description = "@|red DANGER|@: delete all layer and application entities from the target system before the migration"
)
private boolean clear = false;

@Option(
names = {"-sh", "--source-host"},
required = true,
description = "the source API endpoint, e.g. https://my-shogun.com/shogun2-webapp/"
)
private String sourceHost;

@Option(
names = {"-su", "--source-user"},
required = true,
description = "the source admin username"
)
private String sourceUser;

private static ShogunMigrator getMigrator(String type) {
@Option(
names = {"-sp", "--source-password"},
required = true,
description = "the source admin password"
)
private String sourcePassword;

@Option(
names = {"-th", "--target-host"},
required = true,
description = "the target API endpoint, e.g. https://my-shogun-boot.com/"
)
private String targetHost;

@Option(
names = {"-tu", "--target-user"},
required = true,
description = "the target admin username"
)
private String targetUser;

@Option(
names = {"-tp", "--target-password"},
required = true,
description = "the target admin password"
)
private String targetPassword;

private static ShogunMigrator getMigrator(Type type) {
ServiceLoader<ShogunMigrator> loader = ServiceLoader.load(ShogunMigrator.class);
for (ShogunMigrator migrator : loader) {
if (migrator.handlesSourceType(type)) {
if (migrator.handlesSourceType(type.toString())) {
return migrator;
}
}
return null;
}

private static void clear(HostDto target) throws IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
private void clear(HostDto target) throws IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
JsonNode node = fetch(target, "applications");
for (JsonNode app : node) {
delete(target, String.format("applications/%s", app.get("id").asInt()));
Expand All @@ -74,21 +99,16 @@ private static void clear(HostDto target) throws IOException, KeyStoreException,
}
}

private static void handleOptions(CommandLine cmd) throws IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
if (cmd.hasOption("h")) {
printHelp();
System.exit(0);
}
HostDto source = new HostDto(cmd.getOptionValue("sh"), cmd.getOptionValue("su"), cmd.getOptionValue("sp"));
@Override
public Boolean call() throws IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
HostDto source = new HostDto(sourceHost, sourceUser, sourcePassword);
if (!source.getHostname().endsWith("/")) {
source.setHostname(source.getHostname() + "/");
}
HostDto target = new HostDto(cmd.getOptionValue("th"), cmd.getOptionValue("tu"), cmd.getOptionValue("tp"));
HostDto target = new HostDto(targetHost, targetUser, targetPassword);
if (!target.getHostname().endsWith("/")) {
target.setHostname(target.getHostname() + "/");
}
String type = cmd.getOptionValue("t", "shogun2");
boolean clear = cmd.hasOption("c");
ShogunMigrator migrator = getMigrator(type);
if (migrator == null) {
log.error("Unable to find migrator for type {}, exiting.", type);
Expand All @@ -101,17 +121,12 @@ private static void handleOptions(CommandLine cmd) throws IOException, KeyStoreE
}
migrator.initialize(source, target);
migrator.migrateApplications(migrator.migrateLayers());
return true;
}

public static void main(String[] args) {
try {
CommandLine cmd = parseOptions(args);
handleOptions(cmd);
} catch (Exception e) {
log.error("Error when migrating applications: {}", e.getMessage());
log.trace("Stack trace:", e);
printHelp();
}
int code = new CommandLine(new Migrator()).execute(args);
System.exit(code);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
import de.terrestris.shogun.migrator.spi.ShogunMigrator;
import de.terrestris.shogun.migrator.util.MigrationException;
import lombok.extern.log4j.Log4j2;
import org.kohsuke.MetaInfServices;

import java.io.IOException;
import java.util.*;

import static de.terrestris.shogun.migrator.util.ApiUtil.*;

@Log4j2
@MetaInfServices
public class BootMigrator implements ShogunMigrator {

public static final String LAYER_ID = "layerId";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import de.terrestris.shogun.migrator.spi.ShogunMigrator;
import de.terrestris.shogun.migrator.util.MigrationException;
import lombok.extern.log4j.Log4j2;
import org.kohsuke.MetaInfServices;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
Expand All @@ -17,6 +18,7 @@
import static de.terrestris.shogun.migrator.util.ApiUtil.*;

@Log4j2
@MetaInfServices
public class Shogun2Migrator implements ShogunMigrator {

public static final String CHILDREN = "children";
Expand Down

This file was deleted.

0 comments on commit 1e7c84e

Please sign in to comment.