From 32acf00ea9a2a08e0e22735de820d1f0b70d465b Mon Sep 17 00:00:00 2001 From: thanicz Date: Fri, 18 Oct 2024 11:56:12 +0200 Subject: [PATCH] KNOX-3071: New ability in list-alias to list for multiple clusters. New create-list-alias command to create multiple aliases for multiple clusters and also list them. --- .../org/apache/knox/gateway/util/KnoxCLI.java | 146 +++++++++++++---- .../apache/knox/gateway/util/KnoxCLITest.java | 153 ++++++++++++++++++ 2 files changed, 271 insertions(+), 28 deletions(-) diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/util/KnoxCLI.java b/gateway-server/src/main/java/org/apache/knox/gateway/util/KnoxCLI.java index b2d3b30594..14653d3715 100644 --- a/gateway-server/src/main/java/org/apache/knox/gateway/util/KnoxCLI.java +++ b/gateway-server/src/main/java/org/apache/knox/gateway/util/KnoxCLI.java @@ -136,7 +136,9 @@ public class KnoxCLI extends Configured implements Tool { " [" + TopologyConverter.USAGE + "]\n" + " [" + JWKGenerator.USAGE + "]\n" + " [" + GenerateDescriptorCommand.USAGE + "]\n" + - " [" + TokenMigration.USAGE + "]\n"; + " [" + TokenMigration.USAGE + "]\n" + + " [" + CreateListAliasesCommand.USAGE + "]\n"; + private static final String CLUSTER_STRING_SEPARATOR = ","; /** allows stdout to be captured if necessary */ public PrintStream out = System.out; @@ -275,6 +277,12 @@ private int init(String[] args) throws IOException { printKnoxShellUsage(); return -1; } + } else if (args[i].equals("create-list-aliases")) { + command = new CreateListAliasesCommand(); + if (args.length < 3 || "--help".equals(alias)) { + printKnoxShellUsage(); + return -1; + } } else if (args[i].equals("create-cert")) { command = new CertCreateCommand(); if ((args.length > i + 1) && args[i + 1].equals("--help")) { @@ -337,12 +345,15 @@ private int init(String[] args) throws IOException { } } else if( args[i].equals("list-topologies") ){ command = new ListTopologiesCommand(); - }else if ( args[i].equals("--cluster") || args[i].equals("--topology") ) { + } else if ( args[i].equals("--cluster") || args[i].equals("--topology") ) { if( i+1 >= args.length || args[i+1].startsWith( "-" ) ) { printKnoxShellUsage(); return -1; } this.cluster = args[++i]; + if(command instanceof CreateListAliasesCommand) { + ((CreateListAliasesCommand) command).toMap(this.cluster); + } } else if (args[i].equals("service-test")) { if( i + 1 >= args.length) { printKnoxShellUsage(); @@ -663,6 +674,12 @@ private void printKnoxShellUsage() { out.println(JWKGenerator.USAGE + "\n\n" + JWKGenerator.DESC); out.println(); out.println( div ); + out.println(BatchAliasCreateCommand.USAGE + "\n\n" + BatchAliasCreateCommand.DESC); + out.println(); + out.println( div ); + out.println(CreateListAliasesCommand.USAGE + "\n\n" + CreateListAliasesCommand.DESC); + out.println(); + out.println( div ); } } @@ -701,31 +718,34 @@ protected RemoteConfigurationRegistryClientService getRemoteConfigRegistryClient private class AliasListCommand extends Command { - public static final String USAGE = "list-alias [--cluster clustername]"; + public static final String USAGE = "list-alias [--cluster cluster1,clusterN]"; public static final String DESC = "The list-alias command lists all of the aliases\n" + - "for the given hadoop --cluster. The default\n" + + "for the given hadoop --cluster(s). The default\n" + "--cluster being the gateway itself."; @Override public void execute() throws Exception { AliasService as = getAliasService(); - KeystoreService keystoreService = getKeystoreService(); + KeystoreService keystoreService = getKeystoreService(); if (cluster == null) { cluster = "__gateway"; } - boolean credentialStoreForClusterAvailable = - keystoreService.isCredentialStoreForClusterAvailable(cluster); - if (credentialStoreForClusterAvailable) { - out.println("Listing aliases for: " + cluster); - List aliases = as.getAliasesForCluster(cluster); - for (String alias : aliases) { - out.println(alias); - } - out.println("\n" + aliases.size() + " items."); - } else { - out.println("Invalid cluster name provided: " + cluster); - } + String[] clusters = cluster.split(CLUSTER_STRING_SEPARATOR); + for (String currentCluster : clusters) { + boolean credentialStoreForClusterAvailable = + keystoreService.isCredentialStoreForClusterAvailable(currentCluster); + if (credentialStoreForClusterAvailable) { + out.println("Listing aliases for: " + currentCluster); + List aliases = as.getAliasesForCluster(currentCluster); + for (String alias : aliases) { + out.println(alias); + } + out.println("\n" + aliases.size() + " items."); + } else { + out.println("Invalid cluster name provided: " + currentCluster); + } + } } @Override @@ -1037,8 +1057,8 @@ public class BatchAliasCreateCommand extends Command { + "the --value option or --generate (will create a random secret\n" + "for you) or user will be prompt to provide password."; - private List names = new ArrayList<>(); - private List values = new ArrayList<>(); + protected List names = new ArrayList<>(); + protected List values = new ArrayList<>(); public void addName(String alias) { if (names.contains(alias)) { @@ -1061,6 +1081,23 @@ public void execute() throws Exception { if (cluster == null) { cluster = "__gateway"; } + fillMissingValues(aliases, generated); + as.addAliasesForCluster(cluster, aliases); + printResults(generated, aliases); + } + + protected void printResults(List generated, Map aliases) { + if (!generated.isEmpty()) { + out.println(generated.size() + " alias(es) have been successfully generated: " + generated); + } + List created = new ArrayList<>(aliases.keySet()); + created.removeAll(generated); + if (!created.isEmpty()) { + out.println(created.size() + " alias(es) have been successfully created: " + created); + } + } + + protected void fillMissingValues(Map aliases, List generated) { for (Map.Entry entry : aliases.entrySet()) { if (entry.getValue() == null) { if (Boolean.parseBoolean(generate)) { @@ -1071,15 +1108,6 @@ public void execute() throws Exception { } } } - as.addAliasesForCluster(cluster, aliases); - if (!generated.isEmpty()) { - out.println(generated.size() + " alias(es) have been successfully generated: " + generated); - } - List created = new ArrayList<>(aliases.keySet()); - created.removeAll(generated); - if (!created.isEmpty()) { - out.println(created.size() + " alias(es) have been successfully created: " + created); - } } private Map toMap() { @@ -1096,6 +1124,68 @@ public String getUsage() { } } + public class CreateListAliasesCommand extends BatchAliasCreateCommand { + public static final String USAGE = "create-list-aliases " + + "--alias alias1 [--value value1] " + + "--alias alias2 [--value value2] " + + "--alias aliasN [--value valueN] ... " + + "--cluster cluster1 " + + "--alias aliasN [--value valueN] ..." + + "--cluster clusterN " + + "[--generate]"; + public static final String DESC = "The create-list-aliases command will create multiple aliases\n" + + "and secret pairs within the same credential store for the\n" + + "indicated --cluster(s) otherwise within the gateway\n" + + "credential store. The actual secret may be specified via\n" + + "the --value option or --generate (will create a random secret\n" + + "for you) or user will be prompt to provide password."; + + private final Map> aliasMap = new LinkedHashMap<>(); + + @Override + public void execute() throws Exception { + if (cluster == null || !names.isEmpty()) { + cluster = "__gateway"; + this.toMap(cluster); + } + + AliasService aliasService = getAliasService(); + + for (Map.Entry> aliasesMapEntry : aliasMap.entrySet()) { + List generated = new ArrayList<>(); + this.fillMissingValues(aliasesMapEntry.getValue(), generated); + aliasService.addAliasesForCluster(aliasesMapEntry.getKey(), aliasesMapEntry.getValue()); + this.printResults(generated, aliasesMapEntry.getValue()); + this.listAliasesForCluster(aliasesMapEntry.getKey(), aliasService); + } + } + + private void listAliasesForCluster(String cluster, AliasService as) throws AliasServiceException { + out.println("Listing aliases for: " + cluster); + List aliases = as.getAliasesForCluster(cluster); + for (String alias : aliases) { + out.println(alias); + } + out.println("\n" + aliases.size() + " items."); + } + + private void toMap(String cluster) { + Map parsedAliases = new LinkedHashMap<>(); + for (int i = 0; i < values.size(); i++) { + parsedAliases.put(names.get(i), values.get(i)); + } + + names.clear(); + values.clear(); + aliasMap.put(cluster, parsedAliases); + } + + @Override + public String getUsage() { + return USAGE + ":\n\n" + DESC; + } + } + public static char[] promptUserForPassword() { char[] password = null; Console c = System.console(); diff --git a/gateway-server/src/test/java/org/apache/knox/gateway/util/KnoxCLITest.java b/gateway-server/src/test/java/org/apache/knox/gateway/util/KnoxCLITest.java index a72489eed2..9d6363789b 100644 --- a/gateway-server/src/test/java/org/apache/knox/gateway/util/KnoxCLITest.java +++ b/gateway-server/src/test/java/org/apache/knox/gateway/util/KnoxCLITest.java @@ -1306,6 +1306,159 @@ public void testGeneratingJwk512() throws Exception { testGeneratingJWK(JWSAlgorithm.HS512); } + @Test + public void testListingAliasesForMultipleClusters() throws Exception { + GatewayConfigImpl config = new GatewayConfigImpl(); + + outContent.reset(); + String[] args1 = {"create-alias", "multiplealias", "--value", "multiplealias", "--cluster", "cluster1", "--master", "master"}; + int rc; + KnoxCLI cli = new KnoxCLI(); + cli.setConf(config); + rc = cli.run(args1); + assertEquals(0, rc); + assertTrue(outContent.toString(StandardCharsets.UTF_8.name()), + outContent.toString(StandardCharsets.UTF_8.name()).contains("multiplealias has been successfully " + + "created.")); + + outContent.reset(); + String[] args2 = {"create-alias", "multiplealias2", "--value", "multiplealias2", "--cluster", "test", + "--master", "master"}; + cli = new KnoxCLI(); + cli.setConf( config ); + rc = cli.run(args2); + assertEquals(0, rc); + assertTrue(outContent.toString(StandardCharsets.UTF_8.name()), outContent.toString(StandardCharsets.UTF_8.name()).contains("multiplealias2 has been successfully " + + "created.")); + + outContent.reset(); + String[] args3 = { "list-alias", "--cluster", "cluster1,test", "--master", "master" }; + rc = cli.run(args3); + assertEquals(0, rc); + assertTrue(outContent.toString(StandardCharsets.UTF_8.name()), + outContent.toString(StandardCharsets.UTF_8.name()).contains("multiplealias")); + assertTrue(outContent.toString(StandardCharsets.UTF_8.name()), + outContent.toString(StandardCharsets.UTF_8.name()).contains("multiplealias2")); + + outContent.reset(); + String[] args4 = { "list-alias", "--cluster", "cluster1,test,invalidcluster", "--master", "master" }; + rc = cli.run(args4); + assertEquals(0, rc); + assertTrue(outContent.toString(StandardCharsets.UTF_8.name()), + outContent.toString(StandardCharsets.UTF_8.name()).contains("multiplealias")); + assertTrue(outContent.toString(StandardCharsets.UTF_8.name()), + outContent.toString(StandardCharsets.UTF_8.name()).contains("multiplealias2")); + assertTrue(outContent.toString(StandardCharsets.UTF_8.name()), + outContent.toString(StandardCharsets.UTF_8.name()).contains("Invalid cluster name provided: invalidcluster")); + + outContent.reset(); + String[] args5 = {"delete-alias", "multiplealias", "--cluster", "cluster1", "--master", "master"}; + rc = cli.run(args5); + assertEquals(0, rc); + assertTrue(outContent.toString(StandardCharsets.UTF_8.name()), outContent.toString(StandardCharsets.UTF_8.name()).contains("multiplealias has been successfully " + + "deleted.")); + + outContent.reset(); + String[] args6 = {"delete-alias", "multiplealias2", "--cluster", "test", "--master", "master"}; + rc = cli.run(args6); + assertEquals(0, rc); + assertTrue(outContent.toString(StandardCharsets.UTF_8.name()), outContent.toString(StandardCharsets.UTF_8.name()).contains("multiplealias2 has been successfully " + + "deleted.")); + + outContent.reset(); + rc = cli.run(args3); + assertEquals(0, rc); + assertFalse(outContent.toString(StandardCharsets.UTF_8.name()), outContent.toString(StandardCharsets.UTF_8.name()).contains("multiplealias2")); + } + + @Test + public void testCreateAndListForMultipleClusters() throws Exception { + GatewayConfigImpl config = new GatewayConfigImpl(); + + outContent.reset(); + String[] args1 = {"create-list-aliases", "--alias", "alias1", "--value", "value1", "--cluster", "cluster1", + "--alias", "alias2", "--value", "value2", "--alias", "alias1", "--value", "value1", "--cluster", "cluster2", + "--master", "master"}; + int rc; + KnoxCLI cli = new KnoxCLI(); + cli.setConf(config); + rc = cli.run(args1); + assertEquals(0, rc); + assertTrue(outContent.toString(StandardCharsets.UTF_8.name()), + outContent.toString(StandardCharsets.UTF_8.name()).contains("1 alias(es) have been successfully created: [alias1]")); + assertTrue(outContent.toString(StandardCharsets.UTF_8.name()), + outContent.toString(StandardCharsets.UTF_8.name()).contains("Listing aliases for: cluster1")); + assertTrue(outContent.toString(StandardCharsets.UTF_8.name()), + outContent.toString(StandardCharsets.UTF_8.name()).contains("alias1")); + + assertTrue(outContent.toString(StandardCharsets.UTF_8.name()), + outContent.toString(StandardCharsets.UTF_8.name()).contains("2 alias(es) have been successfully created: [alias2, alias1]")); + assertTrue(outContent.toString(StandardCharsets.UTF_8.name()), + outContent.toString(StandardCharsets.UTF_8.name()).contains("Listing aliases for: cluster2")); + assertTrue(outContent.toString(StandardCharsets.UTF_8.name()), + outContent.toString(StandardCharsets.UTF_8.name()).contains("alias2")); + } + + @Test + public void testCreateAndListForMultipleClustersWithGenerate() throws Exception { + GatewayConfigImpl config = new GatewayConfigImpl(); + + outContent.reset(); + String[] args1 = {"create-list-aliases", "--alias", "alias1", "--cluster", "cluster1", "--alias", + "alias2", "--value", "value2", "--alias", "alias3", "--cluster", "cluster2", + "--master", "master", "--generate"}; + int rc; + KnoxCLI cli = new KnoxCLI(); + cli.setConf(config); + rc = cli.run(args1); + assertEquals(0, rc); + assertTrue(outContent.toString(StandardCharsets.UTF_8.name()), + outContent.toString(StandardCharsets.UTF_8.name()).contains("1 alias(es) have been successfully generated: [alias1]")); + assertTrue(outContent.toString(StandardCharsets.UTF_8.name()), + outContent.toString(StandardCharsets.UTF_8.name()).contains("Listing aliases for: cluster1")); + assertTrue(outContent.toString(StandardCharsets.UTF_8.name()), + outContent.toString(StandardCharsets.UTF_8.name()).contains("alias1")); + + assertTrue(outContent.toString(StandardCharsets.UTF_8.name()), + outContent.toString(StandardCharsets.UTF_8.name()).contains("1 alias(es) have been successfully created: [alias2]")); + assertTrue(outContent.toString(StandardCharsets.UTF_8.name()), + outContent.toString(StandardCharsets.UTF_8.name()).contains("1 alias(es) have been successfully generated: [alias3]")); + assertTrue(outContent.toString(StandardCharsets.UTF_8.name()), + outContent.toString(StandardCharsets.UTF_8.name()).contains("Listing aliases for: cluster2")); + assertTrue(outContent.toString(StandardCharsets.UTF_8.name()), + outContent.toString(StandardCharsets.UTF_8.name()).contains("alias3")); + } + + @Test + public void testCreateAndListForMultipleClustersNoCLuster() throws Exception { + GatewayConfigImpl config = new GatewayConfigImpl(); + + outContent.reset(); + String[] args1 = {"create-list-aliases", "--alias", "alias1", "--cluster", "cluster1", "--alias", + "alias2", "--value", "value2", "--alias", "alias3", + "--master", "master", "--generate"}; + int rc; + KnoxCLI cli = new KnoxCLI(); + cli.setConf(config); + rc = cli.run(args1); + assertEquals(0, rc); + assertTrue(outContent.toString(StandardCharsets.UTF_8.name()), + outContent.toString(StandardCharsets.UTF_8.name()).contains("1 alias(es) have been successfully generated: [alias1]")); + assertTrue(outContent.toString(StandardCharsets.UTF_8.name()), + outContent.toString(StandardCharsets.UTF_8.name()).contains("Listing aliases for: cluster1")); + assertTrue(outContent.toString(StandardCharsets.UTF_8.name()), + outContent.toString(StandardCharsets.UTF_8.name()).contains("alias1")); + + assertTrue(outContent.toString(StandardCharsets.UTF_8.name()), + outContent.toString(StandardCharsets.UTF_8.name()).contains("1 alias(es) have been successfully created: [alias2]")); + assertTrue(outContent.toString(StandardCharsets.UTF_8.name()), + outContent.toString(StandardCharsets.UTF_8.name()).contains("1 alias(es) have been successfully generated: [alias3]")); + assertTrue(outContent.toString(StandardCharsets.UTF_8.name()), + outContent.toString(StandardCharsets.UTF_8.name()).contains("Listing aliases for: __gateway")); + assertTrue(outContent.toString(StandardCharsets.UTF_8.name()), + outContent.toString(StandardCharsets.UTF_8.name()).contains("alias3")); + } + private void testGeneratingJWK(JWSAlgorithm jwkAlgorithm) throws Exception { testGeneratingJWK(jwkAlgorithm, null); }