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