diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 4e82e28..b90c37d 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -8,7 +8,7 @@ jobs:
strategy:
matrix:
- java-version: [ 11, 17 ]
+ java-version: [ 11, 17, 21 ]
steps:
- uses: actions/checkout@v4
diff --git a/pom.xml b/pom.xml
index 5f5f536..0cd06d6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -47,6 +47,7 @@
https://sq.terrestris.de
terrestris
src/main/
+ 5.10.1
@@ -248,6 +249,12 @@
+
+ org.kohsuke.metainf-services
+ metainf-services
+ 1.11
+ true
+
org.apache.httpcomponents
httpcore
@@ -286,13 +293,13 @@
org.junit.jupiter
junit-jupiter
- 5.10.1
+ ${junit-jupiter.version}
test
org.junit.jupiter
junit-jupiter-params
- 5.10.1
+ ${junit-jupiter.version}
test
@@ -300,16 +307,16 @@
commons-io
2.15.0
-
- commons-cli
- commons-cli
- 1.6.0
-
commons-codec
commons-codec
1.16.0
+
+ info.picocli
+ picocli
+ 4.7.5
+
@@ -351,8 +358,13 @@
commons-io
- commons-cli
- commons-cli
+ info.picocli
+ picocli
+
+
+ org.kohsuke.metainf-services
+ metainf-services
+ true
diff --git a/src/main/java/de/terrestris/shogun/migrator/Migrator.java b/src/main/java/de/terrestris/shogun/migrator/Migrator.java
index c22ae95..49aea26 100644
--- a/src/main/java/de/terrestris/shogun/migrator/Migrator.java
+++ b/src/main/java/de/terrestris/shogun/migrator/Migrator.java
@@ -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 {
- 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 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()));
@@ -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);
@@ -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);
}
}
diff --git a/src/main/java/de/terrestris/shogun/migrator/shogun2/BootMigrator.java b/src/main/java/de/terrestris/shogun/migrator/shogun2/BootMigrator.java
index fba663f..602f849 100644
--- a/src/main/java/de/terrestris/shogun/migrator/shogun2/BootMigrator.java
+++ b/src/main/java/de/terrestris/shogun/migrator/shogun2/BootMigrator.java
@@ -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.IOException;
import java.util.*;
@@ -15,6 +16,7 @@
import static de.terrestris.shogun.migrator.util.ApiUtil.*;
@Log4j2
+@MetaInfServices
public class BootMigrator implements ShogunMigrator {
public static final String LAYER_ID = "layerId";
diff --git a/src/main/java/de/terrestris/shogun/migrator/shogun2/Shogun2Migrator.java b/src/main/java/de/terrestris/shogun/migrator/shogun2/Shogun2Migrator.java
index 5226174..9e1180a 100644
--- a/src/main/java/de/terrestris/shogun/migrator/shogun2/Shogun2Migrator.java
+++ b/src/main/java/de/terrestris/shogun/migrator/shogun2/Shogun2Migrator.java
@@ -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;
@@ -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";
diff --git a/src/main/resources/META-INF/services/de.terrestris.shogun.migrator.spi.ShogunMigrator b/src/main/resources/META-INF/services/de.terrestris.shogun.migrator.spi.ShogunMigrator
deleted file mode 100644
index d6ce831..0000000
--- a/src/main/resources/META-INF/services/de.terrestris.shogun.migrator.spi.ShogunMigrator
+++ /dev/null
@@ -1,2 +0,0 @@
-de.terrestris.shogun.migrator.shogun2.Shogun2Migrator
-de.terrestris.shogun.migrator.shogun2.BootMigrator