Skip to content

Commit

Permalink
KNOX-3071: New ability in list-alias to list for multiple clusters. N…
Browse files Browse the repository at this point in the history
…ew create-list-alias command to create multiple aliases for multiple clusters and also list them.
  • Loading branch information
hanicz committed Oct 31, 2024
1 parent 89b7139 commit 32acf00
Show file tree
Hide file tree
Showing 2 changed files with 271 additions and 28 deletions.
146 changes: 118 additions & 28 deletions gateway-server/src/main/java/org/apache/knox/gateway/util/KnoxCLI.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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")) {
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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 );
}
}

Expand Down Expand Up @@ -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<String> 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<String> 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
Expand Down Expand Up @@ -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<String> names = new ArrayList<>();
private List<String> values = new ArrayList<>();
protected List<String> names = new ArrayList<>();
protected List<String> values = new ArrayList<>();

public void addName(String alias) {
if (names.contains(alias)) {
Expand All @@ -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<String> generated, Map<String, String> aliases) {
if (!generated.isEmpty()) {
out.println(generated.size() + " alias(es) have been successfully generated: " + generated);
}
List<String> 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<String, String> aliases, List<String> generated) {
for (Map.Entry<String, String> entry : aliases.entrySet()) {
if (entry.getValue() == null) {
if (Boolean.parseBoolean(generate)) {
Expand All @@ -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<String> created = new ArrayList<>(aliases.keySet());
created.removeAll(generated);
if (!created.isEmpty()) {
out.println(created.size() + " alias(es) have been successfully created: " + created);
}
}

private Map<String, String> toMap() {
Expand All @@ -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<String, Map<String, String>> 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<String, Map<String, String>> aliasesMapEntry : aliasMap.entrySet()) {
List<String> 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<String> aliases = as.getAliasesForCluster(cluster);
for (String alias : aliases) {
out.println(alias);
}
out.println("\n" + aliases.size() + " items.");
}

private void toMap(String cluster) {
Map<String, String> 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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down

0 comments on commit 32acf00

Please sign in to comment.