You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pulsar.apache.org by ni...@apache.org on 2022/09/17 09:28:06 UTC
[pulsar] branch master updated: [improve][cli] Pulsar shell: add command to set/get property of a config (#17651)
This is an automated email from the ASF dual-hosted git repository.
nicoloboschi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar.git
The following commit(s) were added to refs/heads/master by this push:
new 4ec1009b42c [improve][cli] Pulsar shell: add command to set/get property of a config (#17651)
4ec1009b42c is described below
commit 4ec1009b42c226d6f9b9eb52b03e50bb4cdbb3cd
Author: Nicolò Boschi <bo...@gmail.com>
AuthorDate: Sat Sep 17 11:27:59 2022 +0200
[improve][cli] Pulsar shell: add command to set/get property of a config (#17651)
---
.../java/org/apache/pulsar/shell/ConfigShell.java | 100 ++++++++++++++++++--
.../java/org/apache/pulsar/shell/PulsarShell.java | 12 +--
.../apache/pulsar/shell/config/ConfigStore.java | 72 +++++++++++++++
.../pulsar/shell/config/FileConfigStore.java | 20 +---
.../org/apache/pulsar/shell/ConfigShellTest.java | 102 ++++++++++++++++++---
site2/docs/reference-cli-tools.md | 30 ++++++
6 files changed, 290 insertions(+), 46 deletions(-)
diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/ConfigShell.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/ConfigShell.java
index 7aa87f07055..72710498017 100644
--- a/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/ConfigShell.java
+++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/ConfigShell.java
@@ -42,6 +42,7 @@ import java.util.stream.Collectors;
import lombok.Getter;
import lombok.SneakyThrows;
import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
import org.apache.pulsar.shell.config.ConfigStore;
/**
@@ -79,11 +80,12 @@ public class ConfigShell implements ShellCommandsProvider {
private final ConfigStore configStore;
private final ObjectMapper writer = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
@Getter
- private String currentConfig = DEFAULT_CONFIG;
+ private String currentConfig;
- public ConfigShell(PulsarShell pulsarShell) {
+ public ConfigShell(PulsarShell pulsarShell, String currentConfig) {
this.configStore = pulsarShell.getConfigStore();
this.pulsarShell = pulsarShell;
+ this.currentConfig = currentConfig;
}
@Override
@@ -114,6 +116,8 @@ public class ConfigShell implements ShellCommandsProvider {
commands.put("delete", new CmdConfigDelete());
commands.put("use", new CmdConfigUse());
commands.put("view", new CmdConfigView());
+ commands.put("set-property", new CmdConfigSetProperty());
+ commands.put("get-property", new CmdConfigGetProperty());
commands.forEach((k, v) -> jcommander.addCommand(k, v));
}
@@ -336,18 +340,98 @@ public class ConfigShell implements ShellCommandsProvider {
return false;
}
- configStore.putConfig(new ConfigStore.ConfigEntry(name, value));
- if (currentConfig.equals(name)) {
- final Properties properties = new Properties();
- properties.load(new StringReader(value));
- pulsarShell.reload(properties);
- }
+ final ConfigStore.ConfigEntry entry = new ConfigStore.ConfigEntry(name, value);
+ configStore.putConfig(entry);
+ reloadIfCurrent(entry);
return true;
}
+
+
abstract boolean verifyCondition();
}
+ private void reloadIfCurrent(ConfigStore.ConfigEntry entry) throws Exception {
+ if (currentConfig.equals(entry.getName())) {
+ final Properties properties = new Properties();
+ properties.load(new StringReader(entry.getValue()));
+ pulsarShell.reload(properties);
+ }
+ }
+
+
+ @Parameters(commandDescription = "Set a configuration property by name")
+ private class CmdConfigSetProperty implements RunnableWithResult {
+
+ @Parameter(description = "Name of the config", required = true)
+ @JCommanderCompleter.ParameterCompleter(type = JCommanderCompleter.ParameterCompleter.Type.CONFIGS)
+ private String name;
+
+ @Parameter(names = {"-p", "--property"}, required = true, description = "Name of the property to update")
+ protected String propertyName;
+
+ @Parameter(names = {"-v", "--value"}, description = "New value for the property")
+ protected String propertyValue;
+
+ @Override
+ @SneakyThrows
+ public boolean run() {
+ if (StringUtils.isBlank(propertyName)) {
+ print("-p parameter is required");
+ return false;
+ }
+
+ if (propertyValue == null) {
+ print("-v parameter is required. you can pass an empty value to empty the property. (-v= )");
+ return false;
+ }
+
+
+ final ConfigStore.ConfigEntry config = configStore.getConfig(this.name);
+ if (config == null) {
+ print("Config " + name + " not found");
+ return false;
+ }
+ ConfigStore.setProperty(config, propertyName, propertyValue);
+ print("Property " + propertyName + " set for config " + name);
+ configStore.putConfig(config);
+ reloadIfCurrent(config);
+ return true;
+ }
+ }
+
+ @Parameters(commandDescription = "Get a configuration property by name")
+ private class CmdConfigGetProperty implements RunnableWithResult {
+
+ @Parameter(description = "Name of the config", required = true)
+ @JCommanderCompleter.ParameterCompleter(type = JCommanderCompleter.ParameterCompleter.Type.CONFIGS)
+ private String name;
+
+ @Parameter(names = {"-p", "--property"}, required = true, description = "Name of the property")
+ protected String propertyName;
+
+ @Override
+ @SneakyThrows
+ public boolean run() {
+ if (StringUtils.isBlank(propertyName)) {
+ print("-p parameter is required");
+ return false;
+ }
+
+ final ConfigStore.ConfigEntry config = configStore.getConfig(this.name);
+ if (config == null) {
+ print("Config " + name + " not found");
+ return false;
+ }
+ final String value = ConfigStore.getProperty(config, propertyName);
+ if (!StringUtils.isBlank(value)) {
+ print(value);
+ }
+ return true;
+ }
+ }
+
+
<T> void print(List<T> items) {
for (T item : items) {
diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/PulsarShell.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/PulsarShell.java
index cbc043fe0c7..24a222e0e7f 100644
--- a/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/PulsarShell.java
+++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/PulsarShell.java
@@ -129,10 +129,9 @@ public class PulsarShell {
private final JCommander mainCommander;
private final MainOptions mainOptions;
private JCommander shellCommander;
- private final String[] args;
private Function<Map<String, ShellCommandsProvider>, InteractiveLineReader> readerBuilder;
private InteractiveLineReader reader;
- private ConfigShell configShell;
+ private final ConfigShell configShell;
public PulsarShell(String args[]) throws IOException {
this(args, new Properties());
@@ -175,12 +174,14 @@ public class PulsarShell {
defaultConfig);
final ConfigStore.ConfigEntry lastUsed = configStore.getLastUsed();
+ String configName = ConfigStore.DEFAULT_CONFIG;
if (lastUsed != null) {
properties.load(new StringReader(lastUsed.getValue()));
+ configName = lastUsed.getName();
} else if (defaultConfig != null) {
properties.load(new StringReader(defaultConfig.getValue()));
}
- this.args = args;
+ configShell = new ConfigShell(this, configName);
}
private static File computePulsarShellFile() {
@@ -266,7 +267,7 @@ public class PulsarShell {
new AttributedStringBuilder().style(AttributedStyle.BOLD).append("quit").toAnsi());
output(welcomeMessage, terminal);
String promptMessage;
- if (configShell != null && configShell.getCurrentConfig() != null) {
+ if (configShell.getCurrentConfig() != null) {
promptMessage = String.format("%s(%s)",
configShell.getCurrentConfig(), getHostFromUrl(serviceUrl));
} else {
@@ -567,9 +568,6 @@ public class PulsarShell {
final Map<String, ShellCommandsProvider> providerMap = new HashMap<>();
registerProvider(createAdminShell(properties), commander, providerMap);
registerProvider(createClientShell(properties), commander, providerMap);
- if (configShell == null) {
- configShell = new ConfigShell(this);
- }
registerProvider(configShell, commander, providerMap);
return providerMap;
}
diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/config/ConfigStore.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/config/ConfigStore.java
index cd994cc5354..4dc6bbb2c51 100644
--- a/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/config/ConfigStore.java
+++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/config/ConfigStore.java
@@ -19,7 +19,10 @@
package org.apache.pulsar.shell.config;
import java.io.IOException;
+import java.util.HashSet;
import java.util.List;
+import java.util.Scanner;
+import java.util.Set;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -50,4 +53,73 @@ public interface ConfigStore {
void setLastUsed(String name) throws IOException;
ConfigEntry getLastUsed() throws IOException;
+
+ static void cleanupValue(ConfigEntry entry) {
+ StringBuilder builder = new StringBuilder();
+ try (Scanner scanner = new Scanner(entry.getValue());) {
+ while (scanner.hasNextLine()) {
+ String line = scanner.nextLine().trim();
+ if (line.isBlank() || line.startsWith("#")) {
+ continue;
+ }
+ builder.append(line);
+ builder.append(System.lineSeparator());
+ }
+ }
+ entry.setValue(builder.toString());
+ }
+
+ static void setProperty(ConfigEntry entry, String propertyName, String propertyValue) {
+ Set<String> keys = new HashSet<>();
+ StringBuilder builder = new StringBuilder();
+ try (Scanner scanner = new Scanner(entry.getValue());) {
+ while (scanner.hasNextLine()) {
+ String line = scanner.nextLine().trim();
+ if (line.isBlank() || line.startsWith("#")) {
+ continue;
+ }
+ final String[] split = line.split("=", 2);
+ if (split.length > 0) {
+ final String property = split[0];
+ if (!keys.add(property)) {
+ continue;
+ }
+ if (property.equals(propertyName)) {
+ line = property + "=" + propertyValue;
+ }
+ }
+ builder.append(line);
+ builder.append(System.lineSeparator());
+ }
+ if (!keys.contains(propertyName)) {
+ builder.append(propertyName + "=" + propertyValue);
+ builder.append(System.lineSeparator());
+ }
+ }
+ entry.setValue(builder.toString());
+ }
+
+ static String getProperty(ConfigEntry entry, String propertyName) {
+ try (Scanner scanner = new Scanner(entry.getValue());) {
+ while (scanner.hasNextLine()) {
+ String line = scanner.nextLine().trim();
+ if (line.isBlank() || line.startsWith("#")) {
+ continue;
+ }
+ final String[] split = line.split("=", 2);
+ if (split.length > 0) {
+ final String property = split[0];
+ if (property.equals(propertyName)) {
+ if (split.length > 1) {
+ return split[1];
+ }
+ return null;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+
}
diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/config/FileConfigStore.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/config/FileConfigStore.java
index f7d558dba98..0d50eb0240c 100644
--- a/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/config/FileConfigStore.java
+++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/shell/config/FileConfigStore.java
@@ -29,7 +29,6 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
-import java.util.Scanner;
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -61,7 +60,7 @@ public class FileConfigStore implements ConfigStore {
}
if (defaultConfig != null) {
this.defaultConfig = new ConfigEntry(defaultConfig.getName(), defaultConfig.getValue());
- cleanupValue(this.defaultConfig);
+ ConfigStore.cleanupValue(this.defaultConfig);
} else {
this.defaultConfig = null;
}
@@ -88,26 +87,11 @@ public class FileConfigStore implements ConfigStore {
if (DEFAULT_CONFIG.equals(entry.getName())) {
throw new IllegalArgumentException("'" + DEFAULT_CONFIG + "' can't be modified.");
}
- cleanupValue(entry);
+ ConfigStore.cleanupValue(entry);
fileConfig.configs.put(entry.getName(), entry);
write();
}
- private static void cleanupValue(ConfigEntry entry) {
- StringBuilder builder = new StringBuilder();
- try (Scanner scanner = new Scanner(entry.getValue());) {
- while (scanner.hasNextLine()) {
- String line = scanner.nextLine().trim();
- if (line.startsWith("#")) {
- continue;
- }
- builder.append(line);
- builder.append(System.lineSeparator());
- }
- }
- entry.setValue(builder.toString());
- }
-
@Override
public ConfigEntry getConfig(String name) {
if (DEFAULT_CONFIG.equals(name)) {
diff --git a/pulsar-client-tools/src/test/java/org/apache/pulsar/shell/ConfigShellTest.java b/pulsar-client-tools/src/test/java/org/apache/pulsar/shell/ConfigShellTest.java
index f5b22af299f..db186ff4a85 100644
--- a/pulsar-client-tools/src/test/java/org/apache/pulsar/shell/ConfigShellTest.java
+++ b/pulsar-client-tools/src/test/java/org/apache/pulsar/shell/ConfigShellTest.java
@@ -46,7 +46,7 @@ public class ConfigShellTest {
private PulsarShell pulsarShell;
private ConfigShell configShell;
- private final List<String> output = new ArrayList<>();
+ private List<String> output;
@BeforeMethod(alwaysRun = true)
public void before() throws Exception {
@@ -58,9 +58,13 @@ public class ConfigShellTest {
when(pulsarShell.getConfigStore()).thenReturn(
new FileConfigStore(tempJson.toFile(),
new ConfigStore.ConfigEntry(ConfigStore.DEFAULT_CONFIG, "#comment\ndefault-config=true")));
- configShell = new ConfigShell(pulsarShell);
+ configShell = new ConfigShell(pulsarShell, ConfigStore.DEFAULT_CONFIG);
configShell.setupState(new Properties());
+ output = new ArrayList<>();
+ setConsole();
+ }
+ private void setConsole() {
configShell.getJCommander().setConsole(new Console() {
@Override
public void print(String msg) {
@@ -79,30 +83,29 @@ public class ConfigShellTest {
return new char[0];
}
});
-
}
@Test
public void testDefault() throws Exception {
- assertTrue(configShell.runCommand(new String[]{"list"}));
+ assertTrue(runCommand(new String[]{"list"}));
assertEquals(output, Arrays.asList("default (*)"));
output.clear();
- assertTrue(configShell.runCommand(new String[]{"view", "default"}));
+ assertTrue(runCommand(new String[]{"view", "default"}));
assertEquals(output.get(0), "default-config=true\n");
output.clear();
final Path newClientConf = Files.createTempFile("client", ".conf");
- assertFalse(configShell.runCommand(new String[]{"create", "default",
+ assertFalse(runCommand(new String[]{"create", "default",
"--file", newClientConf.toFile().getAbsolutePath()}));
assertEquals(output, Arrays.asList("Config 'default' already exists."));
output.clear();
- assertFalse(configShell.runCommand(new String[]{"update", "default",
+ assertFalse(runCommand(new String[]{"update", "default",
"--file", newClientConf.toFile().getAbsolutePath()}));
assertEquals(output, Arrays.asList("'default' can't be updated."));
output.clear();
- assertFalse(configShell.runCommand(new String[]{"delete", "default"}));
+ assertFalse(runCommand(new String[]{"delete", "default"}));
assertEquals(output, Arrays.asList("'default' can't be deleted."));
}
@@ -113,14 +116,14 @@ public class ConfigShellTest {
final byte[] content = ("webServiceUrl=http://localhost:8081/\n" +
"brokerServiceUrl=pulsar://localhost:6651/\n").getBytes(StandardCharsets.UTF_8);
Files.write(newClientConf, content);
- assertTrue(configShell.runCommand(new String[]{"create", "myclient",
+ assertTrue(runCommand(new String[]{"create", "myclient",
"--file", newClientConf.toFile().getAbsolutePath()}));
assertTrue(output.isEmpty());
output.clear();
assertNull(pulsarShell.getConfigStore().getLastUsed());
- assertTrue(configShell.runCommand(new String[]{"use", "myclient"}));
+ assertTrue(runCommand(new String[]{"use", "myclient"}));
assertTrue(output.isEmpty());
output.clear();
assertEquals(pulsarShell.getConfigStore().getLastUsed(), pulsarShell.getConfigStore()
@@ -128,17 +131,90 @@ public class ConfigShellTest {
verify(pulsarShell).reload(any());
- assertTrue(configShell.runCommand(new String[]{"list"}));
+ assertTrue(runCommand(new String[]{"list"}));
assertEquals(output, Arrays.asList("default", "myclient (*)"));
output.clear();
- assertFalse(configShell.runCommand(new String[]{"delete", "myclient"}));
+ assertFalse(runCommand(new String[]{"delete", "myclient"}));
assertEquals(output, Arrays.asList("'myclient' is currently used and it can't be deleted."));
output.clear();
- assertTrue(configShell.runCommand(new String[]{"update", "myclient",
+ assertTrue(runCommand(new String[]{"update", "myclient",
"--file", newClientConf.toFile().getAbsolutePath()}));
assertTrue(output.isEmpty());
verify(pulsarShell, times(2)).reload(any());
}
+
+ @Test
+ public void testSetGetProperty() throws Exception {
+ final Path newClientConf = Files.createTempFile("client", ".conf");
+
+ final byte[] content = ("webServiceUrl=http://localhost:8081/\n" +
+ "brokerServiceUrl=pulsar://localhost:6651/\n").getBytes(StandardCharsets.UTF_8);
+ Files.write(newClientConf, content);
+ assertTrue(runCommand(new String[]{"create", "myclient",
+ "--file", newClientConf.toFile().getAbsolutePath()}));
+ assertTrue(output.isEmpty());
+ output.clear();
+
+ assertTrue(runCommand(new String[]{"use", "myclient"}));
+ assertTrue(output.isEmpty());
+ output.clear();
+
+ assertTrue(runCommand(new String[]{"get-property", "-p", "webServiceUrl", "myclient"}));
+ assertEquals(output.get(0), "http://localhost:8081/");
+ output.clear();
+
+ assertTrue(runCommand(new String[]{"set-property", "-p", "newConf",
+ "-v", "myValue", "myclient"}));
+ verify(pulsarShell, times(2)).reload(any());
+ output.clear();
+
+ assertTrue(runCommand(new String[]{"get-property", "-p", "newConf", "myclient"}));
+ assertEquals(output.get(0), "myValue");
+ output.clear();
+
+ assertTrue(runCommand(new String[]{"view", "myclient"}));
+ assertEquals(output.get(0), "webServiceUrl=http://localhost:8081/\nbrokerServiceUrl" +
+ "=pulsar://localhost:6651/\nnewConf=myValue\n");
+ output.clear();
+
+ assertTrue(runCommand(new String[]{"set-property", "-p", "newConf",
+ "-v", "myValue2", "myclient"}));
+ verify(pulsarShell, times(3)).reload(any());
+ output.clear();
+
+ assertTrue(runCommand(new String[]{"get-property", "-p", "newConf", "myclient"}));
+ assertEquals(output.get(0), "myValue2");
+ output.clear();
+
+
+ assertTrue(runCommand(new String[]{"view", "myclient"}));
+ assertEquals(output.get(0), "webServiceUrl=http://localhost:8081/\nbrokerServiceUrl" +
+ "=pulsar://localhost:6651/\nnewConf=myValue2\n");
+ output.clear();
+
+ assertTrue(runCommand(new String[]{"set-property", "-p", "newConf",
+ "-v", "", "myclient"}));
+ verify(pulsarShell, times(4)).reload(any());
+ output.clear();
+ assertTrue(runCommand(new String[]{"view", "myclient"}));
+ assertEquals(output.get(0), "webServiceUrl=http://localhost:8081/\nbrokerServiceUrl" +
+ "=pulsar://localhost:6651/\nnewConf=\n");
+ output.clear();
+
+ assertTrue(runCommand(new String[]{"get-property", "-p", "newConf", "myclient"}));
+ assertTrue(output.isEmpty());
+ output.clear();
+
+ }
+
+ private boolean runCommand(String[] x) throws Exception {
+ try {
+ return configShell.runCommand(x);
+ } finally {
+ configShell.setupState(null);
+ setConsole();
+ }
+ }
}
\ No newline at end of file
diff --git a/site2/docs/reference-cli-tools.md b/site2/docs/reference-cli-tools.md
index dd094782b42..8d172c8a3b4 100644
--- a/site2/docs/reference-cli-tools.md
+++ b/site2/docs/reference-cli-tools.md
@@ -1014,6 +1014,36 @@ Options
| `--url` | URL of the config. | |
| `--value` | Inline value of the config. Base64-encoded value is supported with the prefix `base64:`. | |
+#### `set-property`
+
+Set a value for a specified configuration property.
+
+```bash
+default(localhost)> config set-property -p webServiceUrl -v http://<cluster-hostname> mycluster
+```
+
+Options
+
+| Flag | Description | Default |
+|--------------------|-----------------------------|-----------------|
+| `-p`, `--property` | Property name to update. | |
+| `-v`, `--value` | New value for the property. | |
+
+
+#### `get-property`
+
+Get the value for a specified configuration property.
+
+```bash
+default(localhost)> config get-property -p webServiceUrl mycluster
+```
+
+Options
+
+| Flag | Description | Default |
+|--------------------|-----------------------------|-----------------|
+| `-p`, `--property` | Property name to update. | |
+
#### `view`