Skip to content

Commit 998f5b6

Browse files
committed
HDDS-11838. Top-level shell to allow access to admin/debug/sh commands
1 parent f1122aa commit 998f5b6

4 files changed

Lines changed: 102 additions & 1 deletion

File tree

hadoop-hdds/cli-common/src/main/java/org/apache/hadoop/hdds/cli/GenericCli.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,12 @@ public void run(String[] argv) {
7878
int exitCode = execute(argv);
7979

8080
if (exitCode != ExitCode.OK) {
81-
ExitUtils.terminate(exitCode, null, null);
81+
if (cmd.getOut() != null) {
82+
cmd.getOut().println("Command executed with exit code: " + exitCode);
83+
}
84+
if (System.getProperty("ozone.interactive.shell") == null) {
85+
ExitUtils.terminate(exitCode, null, null);
86+
}
8287
}
8388
}
8489

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.hadoop.ozone.shell;
19+
20+
import org.apache.hadoop.hdds.cli.GenericCli;
21+
import picocli.CommandLine;
22+
import picocli.CommandLine.Command;
23+
import picocli.shell.jline3.PicocliCommands.PicocliCommandsFactory;
24+
25+
/**
26+
* Interactive Shell for all Ozone commands.
27+
*/
28+
public final class OzoneInteractiveShell {
29+
30+
private OzoneInteractiveShell() {
31+
}
32+
33+
public static void main(String[] argv) throws Exception {
34+
PicocliCommandsFactory factory = new PicocliCommandsFactory();
35+
CommandLine topCmd = new CommandLine(new TopCommand(), factory);
36+
37+
// Add known subcommands statically if they are in the same module.
38+
topCmd.addSubcommand("sh", new OzoneShell().getCmd());
39+
topCmd.addSubcommand("tenant", new org.apache.hadoop.ozone.shell.tenant.TenantShell().getCmd());
40+
topCmd.addSubcommand("s3", new org.apache.hadoop.ozone.shell.s3.S3Shell().getCmd());
41+
42+
// Dynamically add subcommands from other modules to avoid circular dependencies.
43+
addDynamicSubcommand(topCmd, "admin", "org.apache.hadoop.ozone.admin.OzoneAdmin");
44+
addDynamicSubcommand(topCmd, "debug", "org.apache.hadoop.ozone.debug.OzoneDebug");
45+
addDynamicSubcommand(topCmd, "fs", "org.apache.hadoop.fs.ozone.OzoneFsShell");
46+
addDynamicSubcommand(topCmd, "repair", "org.apache.hadoop.ozone.repair.OzoneRepair");
47+
48+
Shell dummyShell = new Shell() {
49+
@Override
50+
public String name() {
51+
return "ozone";
52+
}
53+
54+
@Override
55+
public String prompt() {
56+
return "ozone";
57+
}
58+
};
59+
60+
new REPL(dummyShell, topCmd, factory, null);
61+
}
62+
63+
private static void addDynamicSubcommand(CommandLine topCmd, String name, String className) {
64+
try {
65+
Class<?> clazz = Class.forName(className);
66+
GenericCli instance = (GenericCli) clazz.getDeclaredConstructor().newInstance();
67+
topCmd.addSubcommand(name, instance.getCmd());
68+
} catch (Exception e) {
69+
// Ignore if the class is not present in the classpath
70+
}
71+
}
72+
73+
@Command(name = "ozone", description = "Interactive Shell for all Ozone commands", mixinStandardHelpOptions = true)
74+
private static class TopCommand implements Runnable {
75+
@Override
76+
public void run() {
77+
// Top level command doesn't do anything by itself in REPL
78+
}
79+
}
80+
}

hadoop-ozone/cli-shell/src/main/java/org/apache/hadoop/ozone/shell/REPL.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class REPL {
7171
}
7272

7373
String prompt = shell.prompt() + "> ";
74+
System.setProperty("ozone.interactive.shell", "true");
7475

7576
final int batchSize = lines == null ? 0 : lines.size();
7677
if (batchSize > 0) {

hadoop-ozone/dist/src/shell/ozone/ozone

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,17 @@ function ozonecmd_case
221221
OZONE_CLASSNAME=org.apache.hadoop.security.token.DtUtilShell
222222
OZONE_RUN_ARTIFACT_NAME="ozone-tools"
223223
;;
224+
interactive | --interactive)
225+
OZONE_CLASSNAME=org.apache.hadoop.ozone.shell.OzoneInteractiveShell
226+
OZONE_RUN_ARTIFACT_NAME="ozone-cli-shell"
227+
OZONE_OPTS="${OZONE_OPTS} ${RATIS_OPTS} ${OZONE_MODULE_ACCESS_ARGS}"
228+
# Add all CLI classpaths to support all subcommands dynamically
229+
for cp_file in "ozone-cli-admin" "ozone-cli-debug" "ozone-cli-repair" "ozone-tools"; do
230+
if [[ -f "${OZONE_HOME}/share/ozone/classpath/${cp_file}.classpath" ]]; then
231+
ozone_add_classpath_from_file "${OZONE_HOME}/share/ozone/classpath/${cp_file}.classpath"
232+
fi
233+
done
234+
;;
224235
admin)
225236
OZONE_CLASSNAME=org.apache.hadoop.ozone.admin.OzoneAdmin
226237
OZONE_ADMIN_OPTS="${OZONE_ADMIN_OPTS} ${RATIS_OPTS} ${OZONE_MODULE_ACCESS_ARGS}"
@@ -318,6 +329,10 @@ fi
318329

319330
OZONE_SUBCMD=$1
320331

332+
if [[ "$OZONE_SUBCMD" == "--interactive" ]]; then
333+
OZONE_SUBCMD="interactive"
334+
fi
335+
321336
if [[ "$OZONE_SUBCMD" == "auditparser" ]] || [[ "$OZONE_SUBCMD" == "checknative" ]]; then
322337
echo "warning: 'ozone $OZONE_SUBCMD' is deprecated, use 'ozone debug $OZONE_SUBCMD' instead."
323338
OZONE_SUBCMD="debug"

0 commit comments

Comments
 (0)