Skip to content

Commit f73e66e

Browse files
authored
Allowing custom folder name for plugin installation (#848) (#1116)
Signed-off-by: Vacha Shah <[email protected]>
1 parent e153629 commit f73e66e

File tree

15 files changed

+270
-76
lines changed

15 files changed

+270
-76
lines changed

buildSrc/src/main/groovy/org/opensearch/gradle/plugin/PluginBuildPlugin.groovy

+1
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ class PluginBuildPlugin implements Plugin<Project> {
105105
'opensearchVersion' : Version.fromString(VersionProperties.getOpenSearch()).toString(),
106106
'javaVersion' : project.targetCompatibility as String,
107107
'classname' : extension1.classname,
108+
'customFolderName' : extension1.customFolderName,
108109
'extendedPlugins' : extension1.extendedPlugins.join(','),
109110
'hasNativeController' : extension1.hasNativeController,
110111
'requiresKeystore' : extension1.requiresKeystore

buildSrc/src/main/java/org/opensearch/gradle/plugin/PluginPropertiesExtension.java

+10
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ public class PluginPropertiesExtension {
5151

5252
private String classname;
5353

54+
private String customFolderName = "";
55+
5456
/** Other plugins this plugin extends through SPI */
5557
private List<String> extendedPlugins = new ArrayList<>();
5658

@@ -76,6 +78,14 @@ public PluginPropertiesExtension(Project project) {
7678
this.project = project;
7779
}
7880

81+
public String getCustomFolderName() {
82+
return customFolderName;
83+
}
84+
85+
public void setCustomFolderName(String customFolderName) {
86+
this.customFolderName = customFolderName;
87+
}
88+
7989
public String getName() {
8090
return name == null ? project.getName() : name;
8191
}

buildSrc/src/main/resources/plugin-descriptor.properties

+3
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ java.version=${javaVersion}
4949
opensearch.version=${opensearchVersion}
5050
### optional elements for plugins:
5151
#
52+
# 'custom.foldername': the custom name of the folder in which the plugin is installed.
53+
custom.foldername=${customFolderName}
54+
#
5255
# 'extended.plugins': other plugins this plugin extends through SPI
5356
extended.plugins=${extendedPlugins}
5457
#

distribution/tools/plugin-cli/src/main/java/org/opensearch/plugins/InstallPluginCommand.java

+8-5
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ void execute(Terminal terminal, List<String> pluginIds, boolean isBatch, Environ
261261
final Path extractedZip = unzip(pluginZip, env.pluginsFile());
262262
deleteOnFailure.add(extractedZip);
263263
final PluginInfo pluginInfo = installPlugin(terminal, isBatch, extractedZip, env, deleteOnFailure);
264-
terminal.println("-> Installed " + pluginInfo.getName());
264+
terminal.println("-> Installed " + pluginInfo.getName() + " with folder name " + pluginInfo.getTargetFolderName());
265265
// swap the entry by plugin id for one with the installed plugin name, it gives a cleaner error message for URL installs
266266
deleteOnFailures.remove(pluginId);
267267
deleteOnFailures.put(pluginInfo.getName(), deleteOnFailure);
@@ -780,7 +780,9 @@ private void verifyPluginName(Path pluginPath, String pluginName) throws UserExc
780780
throw new UserException(ExitCodes.USAGE, "plugin '" + pluginName + "' cannot be installed as a plugin, it is a system module");
781781
}
782782

783-
final Path destination = pluginPath.resolve(pluginName);
783+
// scan all the installed plugins to see if the plugin being installed already exists
784+
// either with the plugin name or a custom folder name
785+
Path destination = PluginHelper.verifyIfPluginExists(pluginPath, pluginName);
784786
if (Files.exists(destination)) {
785787
final String message = String.format(
786788
Locale.ROOT,
@@ -864,14 +866,15 @@ private PluginInfo installPlugin(Terminal terminal, boolean isBatch, Path tmpRoo
864866
}
865867
PluginSecurity.confirmPolicyExceptions(terminal, permissions, isBatch);
866868

867-
final Path destination = env.pluginsFile().resolve(info.getName());
869+
String targetFolderName = info.getTargetFolderName();
870+
final Path destination = env.pluginsFile().resolve(targetFolderName);
868871
deleteOnFailure.add(destination);
869872

870873
installPluginSupportFiles(
871874
info,
872875
tmpRoot,
873-
env.binFile().resolve(info.getName()),
874-
env.configFile().resolve(info.getName()),
876+
env.binFile().resolve(targetFolderName),
877+
env.configFile().resolve(targetFolderName),
875878
deleteOnFailure
876879
);
877880
movePlugin(tmpRoot, destination);

distribution/tools/plugin-cli/src/main/java/org/opensearch/plugins/ListPluginsCommand.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th
7575
}
7676

7777
private void printPlugin(Environment env, Terminal terminal, Path plugin, String prefix) throws IOException {
78-
terminal.println(Terminal.Verbosity.SILENT, prefix + plugin.getFileName().toString());
7978
PluginInfo info = PluginInfo.readFromProperties(env.pluginsFile().resolve(plugin));
79+
terminal.println(Terminal.Verbosity.SILENT, prefix + info.getName());
8080
terminal.println(Terminal.Verbosity.VERBOSE, info.toString(prefix));
8181
if (info.getOpenSearchVersion().equals(Version.CURRENT) == false) {
8282
terminal.errorPrintln(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* The OpenSearch Contributors require contributions made to
5+
* this file be licensed under the Apache-2.0 license or a
6+
* compatible open source license.
7+
*/
8+
9+
package org.opensearch.plugins;
10+
11+
import java.io.IOException;
12+
import java.nio.file.Files;
13+
import java.nio.file.Path;
14+
import java.util.List;
15+
import java.util.stream.Collectors;
16+
17+
/**
18+
* A helper class for the plugin-cli tasks.
19+
*/
20+
public class PluginHelper {
21+
22+
/**
23+
* Verify if a plugin exists with any folder name.
24+
* @param pluginPath the path for the plugins directory.
25+
* @param pluginName the name of the concerned plugin.
26+
* @return the path of the folder if the plugin exists.
27+
* @throws IOException if any I/O exception occurs while performing a file operation
28+
*/
29+
public static Path verifyIfPluginExists(Path pluginPath, String pluginName) throws IOException {
30+
List<Path> pluginSubFolders = Files.walk(pluginPath, 1).filter(Files::isDirectory).collect(Collectors.toList());
31+
for (Path customPluginFolderPath : pluginSubFolders) {
32+
if (customPluginFolderPath != pluginPath
33+
&& !((customPluginFolderPath.getFileName().toString()).contains(".installing"))
34+
&& !((customPluginFolderPath.getFileName().toString()).contains(".removing"))) {
35+
final PluginInfo info = PluginInfo.readFromProperties(customPluginFolderPath);
36+
if (info.getName().equals(pluginName)) {
37+
return customPluginFolderPath;
38+
}
39+
}
40+
}
41+
return pluginPath.resolve(pluginName);
42+
}
43+
}

distribution/tools/plugin-cli/src/main/java/org/opensearch/plugins/RemovePluginCommand.java

+14-3
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,20 @@ void execute(Terminal terminal, Environment env, String pluginName, boolean purg
114114
);
115115
}
116116

117-
final Path pluginDir = env.pluginsFile().resolve(pluginName);
118-
final Path pluginConfigDir = env.configFile().resolve(pluginName);
119-
final Path removing = env.pluginsFile().resolve(".removing-" + pluginName);
117+
Path pluginDir = env.pluginsFile().resolve(pluginName);
118+
Path pluginConfigDir = env.configFile().resolve(pluginName);
119+
Path removing = env.pluginsFile().resolve(".removing-" + pluginName);
120+
121+
/*
122+
* If the plugin directory is not found with the plugin name, scan the list of all installed plugins
123+
* to find if the concerned plugin is installed with a custom folder name.
124+
*/
125+
if (!Files.exists(pluginDir)) {
126+
terminal.println("searching in other folders to find if plugin exists with custom folder name");
127+
pluginDir = PluginHelper.verifyIfPluginExists(env.pluginsFile(), pluginName);
128+
pluginConfigDir = env.configFile().resolve(pluginDir.getFileName());
129+
removing = env.pluginsFile().resolve(".removing-" + pluginDir.getFileName());
130+
}
120131

121132
terminal.println("-> removing [" + pluginName + "]...");
122133
/*

distribution/tools/plugin-cli/src/test/java/org/opensearch/plugins/InstallPluginCommandTests.java

+19
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,17 @@ public void testExistingPlugin() throws Exception {
576576
assertInstallCleaned(env.v2());
577577
}
578578

579+
public void testExistingPluginWithCustomFolderName() throws Exception {
580+
Tuple<Path, Environment> env = createEnv(fs, temp);
581+
Path pluginDir = createPluginDir(temp);
582+
String pluginZip = createPluginUrl("fake", pluginDir, "custom.foldername", "fake-folder");
583+
installPlugin(pluginZip, env.v1());
584+
assertPlugin("fake-folder", pluginDir, env.v2());
585+
UserException e = expectThrows(UserException.class, () -> installPlugin(pluginZip, env.v1()));
586+
assertTrue(e.getMessage(), e.getMessage().contains("already exists"));
587+
assertInstallCleaned(env.v2());
588+
}
589+
579590
public void testBin() throws Exception {
580591
Tuple<Path, Environment> env = createEnv(fs, temp);
581592
Path pluginDir = createPluginDir(temp);
@@ -873,6 +884,14 @@ public void testPluginAlreadyInstalled() throws Exception {
873884
);
874885
}
875886

887+
public void testPluginInstallationWithCustomFolderName() throws Exception {
888+
Tuple<Path, Environment> env = createEnv(fs, temp);
889+
Path pluginDir = createPluginDir(temp);
890+
String pluginZip = createPluginUrl("fake", pluginDir, "custom.foldername", "fake-folder");
891+
installPlugin(pluginZip, env.v1());
892+
assertPlugin("fake-folder", pluginDir, env.v2());
893+
}
894+
876895
private void installPlugin(MockTerminal terminal, boolean isBatch) throws Exception {
877896
Tuple<Path, Environment> env = createEnv(fs, temp);
878897
Path pluginDir = createPluginDir(temp);

distribution/tools/plugin-cli/src/test/java/org/opensearch/plugins/ListPluginsCommandTests.java

+9-3
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ private static void buildFakePlugin(
124124
"1.8",
125125
"classname",
126126
classname,
127+
"custom.foldername",
128+
"custom-folder",
127129
"has.native.controller",
128130
Boolean.toString(hasNativeController)
129131
);
@@ -169,7 +171,8 @@ public void testPluginWithVerbose() throws Exception {
169171
"Java Version: 1.8",
170172
"Native Controller: false",
171173
"Extended Plugins: []",
172-
" * Classname: org.fake"
174+
" * Classname: org.fake",
175+
"Folder name: custom-folder"
173176
),
174177
terminal.getOutput()
175178
);
@@ -191,7 +194,8 @@ public void testPluginWithNativeController() throws Exception {
191194
"Java Version: 1.8",
192195
"Native Controller: true",
193196
"Extended Plugins: []",
194-
" * Classname: org.fake"
197+
" * Classname: org.fake",
198+
"Folder name: custom-folder"
195199
),
196200
terminal.getOutput()
197201
);
@@ -215,6 +219,7 @@ public void testPluginWithVerboseMultiplePlugins() throws Exception {
215219
"Native Controller: false",
216220
"Extended Plugins: []",
217221
" * Classname: org.fake",
222+
"Folder name: custom-folder",
218223
"fake_plugin2",
219224
"- Plugin information:",
220225
"Name: fake_plugin2",
@@ -224,7 +229,8 @@ public void testPluginWithVerboseMultiplePlugins() throws Exception {
224229
"Java Version: 1.8",
225230
"Native Controller: false",
226231
"Extended Plugins: []",
227-
" * Classname: org.fake2"
232+
" * Classname: org.fake2",
233+
"Folder name: custom-folder"
228234
),
229235
terminal.getOutput()
230236
);

distribution/tools/plugin-cli/src/test/java/org/opensearch/plugins/RemovePluginCommandTests.java

+36-22
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@
5050
import java.nio.file.DirectoryStream;
5151
import java.nio.file.Files;
5252
import java.nio.file.Path;
53+
import java.util.Arrays;
5354
import java.util.Map;
55+
import java.util.stream.Stream;
5456

5557
import static org.hamcrest.CoreMatchers.containsString;
5658
import static org.hamcrest.CoreMatchers.not;
@@ -90,34 +92,34 @@ public void setUp() throws Exception {
9092
env = TestEnvironment.newEnvironment(settings);
9193
}
9294

93-
void createPlugin(String name) throws IOException {
94-
createPlugin(env.pluginsFile(), name);
95+
void createPlugin(String name, String... additionalProps) throws IOException {
96+
createPlugin(env.pluginsFile(), name, Version.CURRENT, additionalProps);
9597
}
9698

9799
void createPlugin(String name, Version version) throws IOException {
98100
createPlugin(env.pluginsFile(), name, version);
99101
}
100102

101-
void createPlugin(Path path, String name) throws IOException {
102-
createPlugin(path, name, Version.CURRENT);
103-
}
104-
105-
void createPlugin(Path path, String name, Version version) throws IOException {
106-
PluginTestUtil.writePluginProperties(
107-
path.resolve(name),
108-
"description",
109-
"dummy",
110-
"name",
111-
name,
112-
"version",
113-
"1.0",
114-
"opensearch.version",
115-
version.toString(),
116-
"java.version",
117-
System.getProperty("java.specification.version"),
118-
"classname",
119-
"SomeClass"
120-
);
103+
void createPlugin(Path path, String name, Version version, String... additionalProps) throws IOException {
104+
String[] properties = Stream.concat(
105+
Stream.of(
106+
"description",
107+
"dummy",
108+
"name",
109+
name,
110+
"version",
111+
"1.0",
112+
"opensearch.version",
113+
version.toString(),
114+
"java.version",
115+
System.getProperty("java.specification.version"),
116+
"classname",
117+
"SomeClass"
118+
),
119+
Arrays.stream(additionalProps)
120+
).toArray(String[]::new);
121+
String pluginFolderName = additionalProps.length == 0 ? name : additionalProps[1];
122+
PluginTestUtil.writePluginProperties(path.resolve(pluginFolderName), properties);
121123
}
122124

123125
static MockTerminal removePlugin(String name, Path home, boolean purge) throws Exception {
@@ -154,6 +156,17 @@ public void testBasic() throws Exception {
154156
assertRemoveCleaned(env);
155157
}
156158

159+
public void testRemovePluginWithCustomFolderName() throws Exception {
160+
createPlugin("fake", "custom.foldername", "custom-folder");
161+
Files.createFile(env.pluginsFile().resolve("custom-folder").resolve("plugin.jar"));
162+
Files.createDirectory(env.pluginsFile().resolve("custom-folder").resolve("subdir"));
163+
createPlugin("other");
164+
removePlugin("fake", home, randomBoolean());
165+
assertFalse(Files.exists(env.pluginsFile().resolve("custom-folder")));
166+
assertTrue(Files.exists(env.pluginsFile().resolve("other")));
167+
assertRemoveCleaned(env);
168+
}
169+
157170
public void testRemoveOldVersion() throws Exception {
158171
createPlugin(
159172
"fake",
@@ -261,6 +274,7 @@ protected boolean addShutdownHook() {
261274
BufferedReader reader = new BufferedReader(new StringReader(terminal.getOutput()));
262275
BufferedReader errorReader = new BufferedReader(new StringReader(terminal.getErrorOutput()))
263276
) {
277+
assertEquals("searching in other folders to find if plugin exists with custom folder name", reader.readLine());
264278
assertEquals("-> removing [fake]...", reader.readLine());
265279
assertEquals(
266280
"ERROR: plugin [fake] not found; run 'opensearch-plugin list' to get list of installed plugins",

0 commit comments

Comments
 (0)