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/11/28 08:13:27 UTC

[pulsar] branch branch-2.11 updated (fc6830471ad -> 247e05739fe)

This is an automated email from the ASF dual-hosted git repository.

nicoloboschi pushed a change to branch branch-2.11
in repository https://gitbox.apache.org/repos/asf/pulsar.git


    from fc6830471ad [fix][offload] Fix numerical overflow bug while reading data from tiered storage (#18595)
     new 4d0646b3cdf [improve][cli] Pulsar shell: add command to set/get property of a config (#17651)
     new 60a79050538 [fix][cli] Exclude windows script from the docker image (#17404)
     new 62978ae7f32 [improve][cli] Pulsar shell: allow cloning an existing config (#18608)
     new 247e05739fe [fix][cli] Pulsar shell: do not exit on user interrupt during commands (#18615)

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 distribution/shell/src/assemble/shell.xml          |  10 ++
 docker/pulsar/Dockerfile                           |   1 +
 .../java/org/apache/pulsar/shell/ConfigShell.java  | 153 +++++++++++++++++++--
 .../java/org/apache/pulsar/shell/PulsarShell.java  |  34 +++--
 .../apache/pulsar/shell/config/ConfigStore.java    |  72 ++++++++++
 .../pulsar/shell/config/FileConfigStore.java       |  20 +--
 .../org/apache/pulsar/shell/ConfigShellTest.java   | 112 +++++++++++++--
 site2/docs/reference-cli-tools.md                  |  30 ++++
 8 files changed, 381 insertions(+), 51 deletions(-)


[pulsar] 01/04: [improve][cli] Pulsar shell: add command to set/get property of a config (#17651)

Posted by ni...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nicoloboschi pushed a commit to branch branch-2.11
in repository https://gitbox.apache.org/repos/asf/pulsar.git

commit 4d0646b3cdff4fe7c85d94c7448a8e69f9395b6d
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)
    
    (cherry picked from commit 4ec1009b42c226d6f9b9eb52b03e50bb4cdbb3cd)
---
 .../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 a6397fd6f84..aa385d0f7f6 100644
--- a/site2/docs/reference-cli-tools.md
+++ b/site2/docs/reference-cli-tools.md
@@ -1115,6 +1115,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`
 


[pulsar] 02/04: [fix][cli] Exclude windows script from the docker image (#17404)

Posted by ni...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nicoloboschi pushed a commit to branch branch-2.11
in repository https://gitbox.apache.org/repos/asf/pulsar.git

commit 60a79050538e5d7747dce2b080449155ad9801aa
Author: Nicolò Boschi <bo...@gmail.com>
AuthorDate: Fri Sep 2 11:29:48 2022 +0200

    [fix][cli] Exclude windows script from the docker image (#17404)
    
    (cherry picked from commit 3da319cb0898734c6c747b4cd27260fdb861cfd9)
---
 distribution/shell/src/assemble/shell.xml | 10 ++++++++++
 docker/pulsar/Dockerfile                  |  1 +
 2 files changed, 11 insertions(+)

diff --git a/distribution/shell/src/assemble/shell.xml b/distribution/shell/src/assemble/shell.xml
index af1f229c7f7..567402f504a 100644
--- a/distribution/shell/src/assemble/shell.xml
+++ b/distribution/shell/src/assemble/shell.xml
@@ -50,6 +50,16 @@
       <source>${basedir}/../../bin/pulsar-shell</source>
       <fileMode>755</fileMode>
     </file>
+    <file>
+      <outputDirectory>bin</outputDirectory>
+      <source>${basedir}/../../bin/pulsar-admin-common.cmd</source>
+      <fileMode>755</fileMode>
+    </file>
+    <file>
+      <outputDirectory>bin</outputDirectory>
+      <source>${basedir}/../../bin/pulsar-shell.cmd</source>
+      <fileMode>755</fileMode>
+    </file>
     <file>
       <outputDirectory>conf</outputDirectory>
       <source>${basedir}/../../conf/client.conf</source>
diff --git a/docker/pulsar/Dockerfile b/docker/pulsar/Dockerfile
index 27fef14fb76..6450668ccd2 100644
--- a/docker/pulsar/Dockerfile
+++ b/docker/pulsar/Dockerfile
@@ -24,6 +24,7 @@ ARG PULSAR_TARBALL
 
 ADD ${PULSAR_TARBALL} /
 RUN mv /apache-pulsar-* /pulsar
+RUN rm -rf /pulsar/bin/*.cmd
 
 COPY scripts/apply-config-from-env.py /pulsar/bin
 COPY scripts/apply-config-from-env-with-prefix.py /pulsar/bin


[pulsar] 03/04: [improve][cli] Pulsar shell: allow cloning an existing config (#18608)

Posted by ni...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nicoloboschi pushed a commit to branch branch-2.11
in repository https://gitbox.apache.org/repos/asf/pulsar.git

commit 62978ae7f323c54e262dd23d62244b07fc2667cf
Author: Nicolò Boschi <bo...@gmail.com>
AuthorDate: Fri Nov 25 09:46:46 2022 +0100

    [improve][cli] Pulsar shell: allow cloning an existing config (#18608)
    
    (cherry picked from commit 3d643437026ecbb566041e94ab6a7bfc9426136c)
---
 .../java/org/apache/pulsar/shell/ConfigShell.java  | 57 +++++++++++++++++++---
 .../org/apache/pulsar/shell/ConfigShellTest.java   | 10 ++++
 2 files changed, 61 insertions(+), 6 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 72710498017..c04918dec0b 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
@@ -112,6 +112,7 @@ public class ConfigShell implements ShellCommandsProvider {
 
         commands.put("list", new CmdConfigList());
         commands.put("create", new CmdConfigCreate());
+        commands.put("clone", new CmdConfigClone());
         commands.put("update", new CmdConfigUpdate());
         commands.put("delete", new CmdConfigDelete());
         commands.put("use", new CmdConfigUse());
@@ -256,6 +257,9 @@ public class ConfigShell implements ShellCommandsProvider {
     @Parameters(commandDescription = "Create a new configuration.")
     private class CmdConfigCreate extends CmdConfigPut {
 
+        @Parameter(description = "Configuration name", required = true)
+        protected String name;
+
         @Override
         @SneakyThrows
         boolean verifyCondition() {
@@ -266,11 +270,20 @@ public class ConfigShell implements ShellCommandsProvider {
             }
             return true;
         }
+
+        @Override
+        String name() {
+            return name;
+        }
     }
 
     @Parameters(commandDescription = "Update an existing configuration.")
     private class CmdConfigUpdate extends CmdConfigPut {
 
+        @Parameter(description = "Configuration name", required = true)
+        @JCommanderCompleter.ParameterCompleter(type = JCommanderCompleter.ParameterCompleter.Type.CONFIGS)
+        protected String name;
+
         @Override
         @SneakyThrows
         boolean verifyCondition() {
@@ -285,14 +298,15 @@ public class ConfigShell implements ShellCommandsProvider {
             }
             return true;
         }
+
+        @Override
+        String name() {
+            return name;
+        }
     }
 
     private abstract class CmdConfigPut implements RunnableWithResult {
 
-        @Parameter(description = "Configuration name", required = true)
-        @JCommanderCompleter.ParameterCompleter(type = JCommanderCompleter.ParameterCompleter.Type.CONFIGS)
-        protected String name;
-
         @Parameter(names = {"--url"}, description = "URL of the config")
         protected String url;
 
@@ -309,6 +323,7 @@ public class ConfigShell implements ShellCommandsProvider {
             if (!verifyCondition()) {
                 return false;
             }
+            final String name = name();
             final String value;
             if (inlineValue != null) {
                 if (inlineValue.startsWith("base64:")) {
@@ -346,11 +361,41 @@ public class ConfigShell implements ShellCommandsProvider {
             return true;
         }
 
-
+        abstract String name();
 
         abstract boolean verifyCondition();
     }
 
+
+    private class CmdConfigClone implements RunnableWithResult {
+
+        @Parameter(description = "Configuration to clone", required = true)
+        @JCommanderCompleter.ParameterCompleter(type = JCommanderCompleter.ParameterCompleter.Type.CONFIGS)
+        protected String cloneFrom;
+
+        @Parameter(names = {"--name"}, description = "Name of the new config", required = true)
+        protected String newName;
+
+        @Override
+        @SneakyThrows
+        public boolean run() {
+            if (DEFAULT_CONFIG.equals(newName) || configStore.getConfig(newName) != null) {
+                print("'" + newName + "' already exists.");
+                return false;
+            }
+            final ConfigStore.ConfigEntry config = configStore.getConfig(cloneFrom);
+            if (config == null) {
+                print("Config '" + config + "' does not exist.");
+                return false;
+            }
+
+            final ConfigStore.ConfigEntry entry = new ConfigStore.ConfigEntry(newName, config.getValue());
+            configStore.putConfig(entry);
+            reloadIfCurrent(entry);
+            return true;
+        }
+    }
+
     private void reloadIfCurrent(ConfigStore.ConfigEntry entry) throws Exception {
         if (currentConfig.equals(entry.getName())) {
             final Properties properties = new Properties();
@@ -382,7 +427,7 @@ public class ConfigShell implements ShellCommandsProvider {
             }
 
             if (propertyValue == null) {
-                print("-v parameter is required. you can pass an empty value to empty the property. (-v= )");
+                print("-v parameter is required. You can pass an empty value to empty the property. (-v= )");
                 return false;
             }
 
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 db186ff4a85..3a5a94ca5ca 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
@@ -143,6 +143,16 @@ public class ConfigShellTest {
                 "--file", newClientConf.toFile().getAbsolutePath()}));
         assertTrue(output.isEmpty());
         verify(pulsarShell, times(2)).reload(any());
+
+        assertTrue(runCommand(new String[]{"clone", "myclient",
+                "--name", "myclient-copied"}));
+        assertTrue(output.isEmpty());
+        verify(pulsarShell, times(2)).reload(any());
+
+        assertTrue(runCommand(new String[]{"view", "myclient-copied"}));
+        assertEquals(output.get(0), "webServiceUrl=http://localhost:8081/\nbrokerServiceUrl" +
+                "=pulsar://localhost:6651/\n");
+        output.clear();
     }
 
     @Test


[pulsar] 04/04: [fix][cli] Pulsar shell: do not exit on user interrupt during commands (#18615)

Posted by ni...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nicoloboschi pushed a commit to branch branch-2.11
in repository https://gitbox.apache.org/repos/asf/pulsar.git

commit 247e05739fe56dd94ff13342512981ed71f48c02
Author: Nicolò Boschi <bo...@gmail.com>
AuthorDate: Fri Nov 25 14:30:29 2022 +0100

    [fix][cli] Pulsar shell: do not exit on user interrupt during commands (#18615)
    
    (cherry picked from commit 43da8b90ff7ca475b7335166a4b1378c8554edc4)
---
 .../java/org/apache/pulsar/shell/PulsarShell.java  | 22 +++++++++++++++++++++-
 1 file changed, 21 insertions(+), 1 deletion(-)

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 24a222e0e7f..3d31fce6cee 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
@@ -122,6 +122,10 @@ public class PulsarShell {
         boolean noProgress;
     }
 
+    enum ExecState {
+        IDLE,
+        RUNNING
+    }
     private Properties properties;
     @Getter
     private final ConfigStore configStore;
@@ -132,6 +136,7 @@ public class PulsarShell {
     private Function<Map<String, ShellCommandsProvider>, InteractiveLineReader> readerBuilder;
     private InteractiveLineReader reader;
     private final ConfigShell configShell;
+    private ExecState execState = ExecState.IDLE;
 
     public PulsarShell(String args[]) throws IOException {
         this(args, new Properties());
@@ -217,7 +222,18 @@ public class PulsarShell {
     }
 
     public void run() throws Exception {
-        final Terminal terminal = TerminalBuilder.builder().build();
+        final Terminal terminal = TerminalBuilder.builder()
+                .nativeSignals(true)
+                .signalHandler(signal -> {
+                    if (signal == Terminal.Signal.INT || signal == Terminal.Signal.QUIT) {
+                        if (execState == ExecState.RUNNING) {
+                            throw new InterruptShellException();
+                        } else {
+                            exit(0);
+                        }
+                    }
+                })
+                .build();
         run((providersMap) -> {
             List<Completer> completers = new ArrayList<>();
             String serviceUrl = "";
@@ -404,6 +420,7 @@ public class PulsarShell {
 
         Runtime.getRuntime().addShutdownHook(new Thread(() -> quit(terminal)));
         while (true) {
+            execState = ExecState.IDLE;
             final List<String> words;
             try {
                 words = commandReader.readCommand();
@@ -411,6 +428,7 @@ public class PulsarShell {
                 exit(0);
                 return;
             }
+            execState = ExecState.RUNNING;
             final String line = words.stream().collect(Collectors.joining(" "));
             if (StringUtils.isBlank(line)) {
                 continue;
@@ -434,6 +452,8 @@ public class PulsarShell {
             try {
                 printExecutingCommands(terminal, commandsInfo, false);
                 commandOk = pulsarShellCommandsProvider.runCommand(argv);
+            } catch (InterruptShellException t) {
+                // no-op
             } catch (Throwable t) {
                 t.printStackTrace(terminal.writer());
             } finally {