blob: d50f19953edd600c4492d0cda7c75a95f19bb551 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.pulsar.admin.cli;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import java.io.FileInputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Supplier;
import org.apache.commons.lang3.StringUtils;
import org.apache.pulsar.PulsarVersion;
import org.apache.pulsar.client.admin.PulsarAdmin;
import org.apache.pulsar.client.admin.PulsarAdminBuilder;
import org.apache.pulsar.client.admin.internal.PulsarAdminImpl;
public class PulsarAdminTool {
private static boolean allowSystemExit = true;
private static int lastExitCode = Integer.MIN_VALUE;
protected final Map<String, Class<?>> commandMap;
private final JCommander jcommander;
protected final PulsarAdminBuilder adminBuilder;
@Parameter(names = { "--admin-url" }, description = "Admin Service URL to which to connect.")
String serviceUrl = null;
@Parameter(names = { "--auth-plugin" }, description = "Authentication plugin class name.")
String authPluginClassName = null;
@Parameter(names = { "--request-timeout" }, description = "Request time out in seconds for "
+ "the pulsar admin client for any request")
int requestTimeout = PulsarAdminImpl.DEFAULT_REQUEST_TIMEOUT_SECONDS;
@Parameter(
names = { "--auth-params" },
description = "Authentication parameters, whose format is determined by the implementation " +
"of method `configure` in authentication plugin class, for example \"key1:val1,key2:val2\" " +
"or \"{\"key1\":\"val1\",\"key2\":\"val2\"}.")
String authParams = null;
@Parameter(names = { "--tls-allow-insecure" }, description = "Allow TLS insecure connection")
Boolean tlsAllowInsecureConnection;
@Parameter(names = { "--tls-trust-cert-path" }, description = "Allow TLS trust cert file path")
String tlsTrustCertsFilePath;
@Parameter(names = { "--tls-enable-hostname-verification" }, description = "Enable TLS common name verification")
Boolean tlsEnableHostnameVerification;
@Parameter(names = { "-v", "--version" }, description = "Get version of pulsar admin client")
boolean version;
@Parameter(names = { "-h", "--help", }, help = true, description = "Show this help.")
boolean help;
// for tls with keystore type config
boolean useKeyStoreTls = false;
String tlsTrustStoreType = "JKS";
String tlsTrustStorePath = null;
String tlsTrustStorePassword = null;
PulsarAdminTool(Properties properties) throws Exception {
// fallback to previous-version serviceUrl property to maintain backward-compatibility
serviceUrl = StringUtils.isNotBlank(properties.getProperty("webServiceUrl"))
? properties.getProperty("webServiceUrl")
: properties.getProperty("serviceUrl");
authPluginClassName = properties.getProperty("authPlugin");
authParams = properties.getProperty("authParams");
boolean tlsAllowInsecureConnection = this.tlsAllowInsecureConnection != null ? this.tlsAllowInsecureConnection
: Boolean.parseBoolean(properties.getProperty("tlsAllowInsecureConnection", "false"));
boolean tlsEnableHostnameVerification = this.tlsEnableHostnameVerification != null
? this.tlsEnableHostnameVerification
: Boolean.parseBoolean(properties.getProperty("tlsEnableHostnameVerification", "false"));
final String tlsTrustCertsFilePath = StringUtils.isNotBlank(this.tlsTrustCertsFilePath)
? this.tlsTrustCertsFilePath
: properties.getProperty("tlsTrustCertsFilePath");
this.useKeyStoreTls = Boolean
.parseBoolean(properties.getProperty("useKeyStoreTls", "false"));
this.tlsTrustStoreType = properties.getProperty("tlsTrustStoreType", "JKS");
this.tlsTrustStorePath = properties.getProperty("tlsTrustStorePath");
this.tlsTrustStorePassword = properties.getProperty("tlsTrustStorePassword");
adminBuilder = PulsarAdmin.builder().allowTlsInsecureConnection(tlsAllowInsecureConnection)
.enableTlsHostnameVerification(tlsEnableHostnameVerification)
.tlsTrustCertsFilePath(tlsTrustCertsFilePath)
.useKeyStoreTls(useKeyStoreTls)
.tlsTrustStoreType(tlsTrustStoreType)
.tlsTrustStorePath(tlsTrustStorePath)
.tlsTrustStorePassword(tlsTrustStorePassword);
jcommander = new JCommander();
jcommander.setProgramName("pulsar-admin");
jcommander.addObject(this);
commandMap = new HashMap<>();
commandMap.put("clusters", CmdClusters.class);
commandMap.put("ns-isolation-policy", CmdNamespaceIsolationPolicy.class);
commandMap.put("brokers", CmdBrokers.class);
commandMap.put("broker-stats", CmdBrokerStats.class);
commandMap.put("tenants", CmdTenants.class);
commandMap.put("properties", CmdTenants.CmdProperties.class); // deprecated, doesn't show in usage()
commandMap.put("namespaces", CmdNamespaces.class);
commandMap.put("topics", CmdTopics.class);
commandMap.put("schemas", CmdSchemas.class);
commandMap.put("bookies", CmdBookies.class);
// Hidden deprecated "persistent" and "non-persistent" subcommands
commandMap.put("persistent", CmdPersistentTopics.class);
commandMap.put("non-persistent", CmdNonPersistentTopics.class);
commandMap.put("resource-quotas", CmdResourceQuotas.class);
// pulsar-proxy cli
commandMap.put("proxy-stats", CmdProxyStats.class);
commandMap.put("functions", CmdFunctions.class);
commandMap.put("functions-worker", CmdFunctionWorker.class);
commandMap.put("sources", CmdSources.class);
commandMap.put("sinks", CmdSinks.class);
// Automatically generate documents for pulsar-admin
commandMap.put("documents", CmdGenerateDocument.class);
// To remain backwards compatibility for "source" and "sink" commands
// TODO eventually remove this
commandMap.put("source", CmdSources.class);
commandMap.put("sink", CmdSinks.class);
commandMap.put("packages", CmdPackages.class);
}
private static class PulsarAdminSupplier implements Supplier<PulsarAdmin> {
private final PulsarAdminBuilder pulsarAdminBuilder;
private final Function<PulsarAdminBuilder, ? extends PulsarAdmin> adminFactory;
private PulsarAdmin admin;
private PulsarAdminSupplier(PulsarAdminBuilder pulsarAdminBuilder, Function<PulsarAdminBuilder, ? extends PulsarAdmin> adminFactory) {
this.pulsarAdminBuilder = pulsarAdminBuilder;
this.adminFactory = adminFactory;
}
@Override
public PulsarAdmin get() {
if (admin == null) {
admin = adminFactory.apply(pulsarAdminBuilder);
}
return admin;
}
}
private void setupCommands(Function<PulsarAdminBuilder, ? extends PulsarAdmin> adminFactory) {
try {
adminBuilder.serviceHttpUrl(serviceUrl);
adminBuilder.authentication(authPluginClassName, authParams);
adminBuilder.requestTimeout(requestTimeout, TimeUnit.SECONDS);
Supplier<PulsarAdmin> admin = new PulsarAdminSupplier(adminBuilder, adminFactory);
for (Map.Entry<String, Class<?>> c : commandMap.entrySet()) {
addCommand(c, admin);
}
} catch (Exception e) {
Throwable cause;
if (e instanceof InvocationTargetException && null != e.getCause()) {
cause = e.getCause();
} else {
cause = e;
}
System.err.println(cause.getClass() + ": " + cause.getMessage());
System.exit(1);
}
}
private void addCommand(Map.Entry<String, Class<?>> c, Supplier<PulsarAdmin> admin) throws Exception {
// To remain backwards compatibility for "source" and "sink" commands
// TODO eventually remove this
if (c.getKey().equals("sources") || c.getKey().equals("source")) {
jcommander.addCommand("sources", c.getValue().getConstructor(Supplier.class).newInstance(admin), "source");
} else if (c.getKey().equals("sinks") || c.getKey().equals("sink")) {
jcommander.addCommand("sinks", c.getValue().getConstructor(Supplier.class).newInstance(admin), "sink");
} else if (c.getKey().equals("functions")) {
jcommander.addCommand(c.getKey(), c.getValue().getConstructor(Supplier.class).newInstance(admin));
} else {
// Other mode, all components are initialized.
jcommander.addCommand(c.getKey(), c.getValue().getConstructor(Supplier.class).newInstance(admin));
}
}
boolean run(String[] args) {
return run(args, adminBuilder -> {
try {
return adminBuilder.build();
} catch (Exception ex) {
System.err.println(ex.getClass() + ": " + ex.getMessage());
System.exit(1);
return null;
}
});
}
boolean run(String[] args, Function<PulsarAdminBuilder, ? extends PulsarAdmin> adminFactory) {
if (args.length == 0) {
setupCommands(adminFactory);
jcommander.usage();
return false;
}
int cmdPos;
for (cmdPos = 0; cmdPos < args.length; cmdPos++) {
if (commandMap.containsKey(args[cmdPos])) {
break;
}
}
try {
jcommander.parse(Arrays.copyOfRange(args, 0, Math.min(cmdPos, args.length)));
} catch (Exception e) {
System.err.println(e.getMessage());
System.err.println();
setupCommands(adminFactory);
jcommander.usage();
return false;
}
if (version) {
System.out.println("Current version of pulsar admin client is: " + PulsarVersion.getVersion());
return true;
}
if (help) {
setupCommands(adminFactory);
jcommander.usage();
return true;
}
if (cmdPos == args.length) {
setupCommands(adminFactory);
jcommander.usage();
return false;
} else {
setupCommands(adminFactory);
String cmd = args[cmdPos];
// To remain backwards compatibility for "source" and "sink" commands
// TODO eventually remove this
if (cmd.equals("source")) {
cmd = "sources";
} else if (cmd.equals("sink")) {
cmd = "sinks";
}
JCommander obj = jcommander.getCommands().get(cmd);
CmdBase cmdObj = (CmdBase) obj.getObjects().get(0);
return cmdObj.run(Arrays.copyOfRange(args, cmdPos + 1, args.length));
}
}
public static void main(String[] args) throws Exception {
lastExitCode = 0;
String configFile = null;
if (args.length > 0) {
configFile = args[0];
args = Arrays.copyOfRange(args, 1, args.length);
}
Properties properties = new Properties();
if (configFile != null) {
try (FileInputStream fis = new FileInputStream(configFile)) {
properties.load(fis);
}
}
PulsarAdminTool tool = new PulsarAdminTool(properties);
int cmdPos;
for (cmdPos = 1; cmdPos < args.length; cmdPos++) {
if (tool.commandMap.containsKey(args[cmdPos])) {
break;
}
}
++cmdPos;
boolean isLocalRun = cmdPos < args.length && "localrun".equals(args[cmdPos].toLowerCase());
Function<PulsarAdminBuilder, ? extends PulsarAdmin> adminFactory;
if (isLocalRun) {
// bypass constructing admin client
adminFactory = (adminBuilder) -> null;
} else {
adminFactory = (adminBuilder) -> {
try {
return adminBuilder.build();
} catch (Exception ex) {
System.err.println(ex.getClass() + ": " + ex.getMessage());
exit(1);
return null;
}
};
}
if (tool.run(args, adminFactory)) {
exit(0);
} else {
exit(1);
}
}
private static void exit(int code) {
lastExitCode = lastExitCode;
if (allowSystemExit) {
// we are using halt and not System.exit, we do not mind about shutdown hooks
// they are only slowing down the tool
Runtime.getRuntime().halt(code);
} else {
System.out.println("Exit code is " + code+" (System.exit not called, as we are in test mode)");
}
}
static void setAllowSystemExit(boolean allowSystemExit) {
PulsarAdminTool.allowSystemExit = allowSystemExit;
}
static int getLastExitCode() {
return lastExitCode;
}
}