mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-03-25 09:28:27 +00:00
Modify permissions dialog for plugins
This commit modifies the handling of plugins that require special permissions to cover a case that was not previously covered. Relates #23742
This commit is contained in:
parent
fc8cb417e7
commit
defd0452e7
@ -39,6 +39,9 @@ class PluginPropertiesExtension {
|
||||
@Input
|
||||
String classname
|
||||
|
||||
@Input
|
||||
boolean hasNativeController = false
|
||||
|
||||
/** Indicates whether the plugin jar should be made available for the transport client. */
|
||||
@Input
|
||||
boolean hasClientJar = false
|
||||
|
@ -79,7 +79,8 @@ class PluginPropertiesTask extends Copy {
|
||||
'version': stringSnap(extension.version),
|
||||
'elasticsearchVersion': stringSnap(VersionProperties.elasticsearch),
|
||||
'javaVersion': project.targetCompatibility as String,
|
||||
'classname': extension.classname
|
||||
'classname': extension.classname,
|
||||
'hasNativeController': extension.hasNativeController
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -605,7 +605,6 @@
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]bootstrap[/\\]JavaVersion.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]bootstrap[/\\]Natives.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]bootstrap[/\\]Security.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]bootstrap[/\\]Spawner.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]bootstrap[/\\]StartupException.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]bootstrap[/\\]SystemCallFilter.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]cli[/\\]Command.java" checks="LineLength" />
|
||||
@ -1565,14 +1564,12 @@
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]plugins[/\\]AnalysisPlugin.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]plugins[/\\]ClusterPlugin.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]plugins[/\\]DiscoveryPlugin.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]plugins[/\\]DummyPluginInfo.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]plugins[/\\]IngestPlugin.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]plugins[/\\]InstallPluginCommand.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]plugins[/\\]ListPluginsCommand.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]plugins[/\\]MetaDataUpgrader.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]plugins[/\\]NetworkPlugin.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]plugins[/\\]Plugin.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]plugins[/\\]PluginInfo.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]plugins[/\\]PluginSecurity.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]plugins[/\\]PluginsService.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]plugins[/\\]ProgressInputStream.java" checks="LineLength" />
|
||||
@ -2388,7 +2385,6 @@
|
||||
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]bootstrap[/\\]JNANativesTests.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]bootstrap[/\\]JarHellTests.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]bootstrap[/\\]MaxMapCountCheckTests.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]bootstrap[/\\]SpawnerTests.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]broadcast[/\\]BroadcastActionsIT.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]bwcompat[/\\]OldIndexBackwardsCompatibilityIT.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]bwcompat[/\\]RecoveryWithUnsupportedIndicesIT.java" checks="LineLength" />
|
||||
@ -3009,7 +3005,6 @@
|
||||
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]nodesinfo[/\\]NodeInfoStreamingTests.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]nodesinfo[/\\]SimpleNodesInfoIT.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]operateAllIndices[/\\]DestructiveOperationsIT.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]plugins[/\\]PluginInfoTests.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]plugins[/\\]PluginsServiceTests.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]recovery[/\\]FullRollingRestartIT.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]recovery[/\\]RecoveriesCollectionTests.java" checks="LineLength" />
|
||||
@ -3949,11 +3944,8 @@
|
||||
<suppress files="qa[/\\]evil-tests[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]common[/\\]logging[/\\]EvilLoggerTests.java" checks="LineLength" />
|
||||
<suppress files="qa[/\\]evil-tests[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]env[/\\]NodeEnvironmentEvilTests.java" checks="LineLength" />
|
||||
<suppress files="qa[/\\]evil-tests[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]plugins[/\\]InstallPluginCommandTests.java" checks="LineLength" />
|
||||
<suppress files="qa[/\\]evil-tests[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]plugins[/\\]ListPluginsCommandTests.java" checks="LineLength" />
|
||||
<suppress files="qa[/\\]evil-tests[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]plugins[/\\]PluginSecurityTests.java" checks="LineLength" />
|
||||
<suppress files="qa[/\\]evil-tests[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]plugins[/\\]RemovePluginCommandTests.java" checks="LineLength" />
|
||||
<suppress files="qa[/\\]evil-tests[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]tribe[/\\]TribeUnitTests.java" checks="LineLength" />
|
||||
<suppress files="qa[/\\]no-bootstrap-tests[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]bootstrap[/\\]SpawnerNoBootstrapTests.java" checks="LineLength" />
|
||||
<suppress files="qa[/\\]smoke-test-client[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]smoketest[/\\]ESSmokeClientTestCase.java" checks="LineLength" />
|
||||
<suppress files="qa[/\\]smoke-test-client[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]smoketest[/\\]SmokeTestClientIT.java" checks="LineLength" />
|
||||
<suppress files="qa[/\\]smoke-test-http[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]http[/\\]ContextAndHeaderTransportIT.java" checks="LineLength" />
|
||||
|
@ -30,11 +30,15 @@ name=${name}
|
||||
# 'classname': the name of the class to load, fully-qualified.
|
||||
classname=${classname}
|
||||
#
|
||||
# 'java.version' version of java the code is built against
|
||||
# 'java.version': version of java the code is built against
|
||||
# use the system property java.specification.version
|
||||
# version string must be a sequence of nonnegative decimal integers
|
||||
# separated by "."'s and may have leading zeros
|
||||
java.version=${javaVersion}
|
||||
#
|
||||
# 'elasticsearch.version' version of elasticsearch compiled against
|
||||
# 'elasticsearch.version': version of elasticsearch compiled against
|
||||
elasticsearch.version=${elasticsearchVersion}
|
||||
### optional elements for plugins:
|
||||
#
|
||||
# 'has.native.controller': whether or not the plugin has a native controller
|
||||
has.native.controller=${hasNativeController}
|
||||
|
@ -19,9 +19,10 @@
|
||||
|
||||
package org.elasticsearch.bootstrap;
|
||||
|
||||
import org.apache.lucene.util.Constants;
|
||||
import org.apache.lucene.util.IOUtils;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.plugins.PluginInfo;
|
||||
import org.elasticsearch.plugins.Platforms;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
@ -32,97 +33,89 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* Spawns native plugin controller processes if present. Will only work prior to a system call filter being installed.
|
||||
* Spawns native plugin controller processes if present. Will only work prior to a system call
|
||||
* filter being installed.
|
||||
*/
|
||||
final class Spawner implements Closeable {
|
||||
|
||||
private static final String PROGRAM_NAME = Constants.WINDOWS ? "controller.exe" : "controller";
|
||||
private static final String PLATFORM_NAME = makePlatformName(Constants.OS_NAME, Constants.OS_ARCH);
|
||||
private static final String TMP_ENVVAR = "TMPDIR";
|
||||
|
||||
/**
|
||||
/*
|
||||
* References to the processes that have been spawned, so that we can destroy them.
|
||||
*/
|
||||
private final List<Process> processes = new ArrayList<>();
|
||||
private AtomicBoolean spawned = new AtomicBoolean();
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
IOUtils.close(() -> processes.stream().map(s -> (Closeable)s::destroy).iterator());
|
||||
} finally {
|
||||
processes.clear();
|
||||
}
|
||||
IOUtils.close(() -> processes.stream().map(s -> (Closeable) s::destroy).iterator());
|
||||
}
|
||||
|
||||
/**
|
||||
* For each plugin, attempt to spawn the controller daemon. Silently ignore any plugins
|
||||
* that don't include a controller for the correct platform.
|
||||
* Spawns the native controllers for each plugin
|
||||
*
|
||||
* @param environment the node environment
|
||||
* @throws IOException if an I/O error occurs reading the plugins or spawning a native process
|
||||
*/
|
||||
void spawnNativePluginControllers(Environment environment) throws IOException {
|
||||
if (Files.exists(environment.pluginsFile())) {
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(environment.pluginsFile())) {
|
||||
for (Path plugin : stream) {
|
||||
Path spawnPath = makeSpawnPath(plugin);
|
||||
if (Files.isRegularFile(spawnPath)) {
|
||||
spawnNativePluginController(spawnPath, environment.tmpFile());
|
||||
}
|
||||
void spawnNativePluginControllers(final Environment environment) throws IOException {
|
||||
if (!spawned.compareAndSet(false, true)) {
|
||||
throw new IllegalStateException("native controllers already spawned");
|
||||
}
|
||||
final Path pluginsFile = environment.pluginsFile();
|
||||
if (!Files.exists(pluginsFile)) {
|
||||
throw new IllegalStateException("plugins directory [" + pluginsFile + "] not found");
|
||||
}
|
||||
/*
|
||||
* For each plugin, attempt to spawn the controller daemon. Silently ignore any plugin that
|
||||
* don't include a controller for the correct platform.
|
||||
*/
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(pluginsFile)) {
|
||||
for (final Path plugin : stream) {
|
||||
final PluginInfo info = PluginInfo.readFromProperties(plugin);
|
||||
final Path spawnPath = Platforms.nativeControllerPath(plugin);
|
||||
if (!Files.isRegularFile(spawnPath)) {
|
||||
continue;
|
||||
}
|
||||
if (!info.hasNativeController()) {
|
||||
final String message = String.format(
|
||||
Locale.ROOT,
|
||||
"plugin [%s] does not have permission to fork native controller",
|
||||
plugin.getFileName());
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
final Process process =
|
||||
spawnNativePluginController(spawnPath, environment.tmpFile());
|
||||
processes.add(process);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to spawn the controller daemon for a given plugin. The spawned process
|
||||
* will remain connected to this JVM via its stdin, stdout and stderr, but the
|
||||
* references to these streams are not available to code outside this package.
|
||||
* Attempt to spawn the controller daemon for a given plugin. The spawned process will remain
|
||||
* connected to this JVM via its stdin, stdout, and stderr streams, but the references to these
|
||||
* streams are not available to code outside this package.
|
||||
*/
|
||||
private void spawnNativePluginController(Path spawnPath, Path tmpPath) throws IOException {
|
||||
ProcessBuilder pb = new ProcessBuilder(spawnPath.toString());
|
||||
private Process spawnNativePluginController(
|
||||
final Path spawnPath,
|
||||
final Path tmpPath) throws IOException {
|
||||
final ProcessBuilder pb = new ProcessBuilder(spawnPath.toString());
|
||||
|
||||
// The only environment variable passes on the path to the temporary directory
|
||||
// the only environment variable passes on the path to the temporary directory
|
||||
pb.environment().clear();
|
||||
pb.environment().put(TMP_ENVVAR, tmpPath.toString());
|
||||
pb.environment().put("TMPDIR", tmpPath.toString());
|
||||
|
||||
// The output stream of the Process object corresponds to the daemon's stdin
|
||||
processes.add(pb.start());
|
||||
// the output stream of the process object corresponds to the daemon's stdin
|
||||
return pb.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* The collection of processes representing spawned native controllers.
|
||||
*
|
||||
* @return the processes
|
||||
*/
|
||||
List<Process> getProcesses() {
|
||||
return Collections.unmodifiableList(processes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the full path to the program to be spawned.
|
||||
*/
|
||||
static Path makeSpawnPath(Path plugin) {
|
||||
return plugin.resolve("platform").resolve(PLATFORM_NAME).resolve("bin").resolve(PROGRAM_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the platform name in the format used in Kibana downloads, for example:
|
||||
* - darwin-x86_64
|
||||
* - linux-x86-64
|
||||
* - windows-x86_64
|
||||
* For *nix platforms this is more-or-less `uname -s`-`uname -m` converted to lower case.
|
||||
* However, for consistency between different operating systems on the same architecture
|
||||
* "amd64" is replaced with "x86_64" and "i386" with "x86".
|
||||
* For Windows it's "windows-" followed by either "x86" or "x86_64".
|
||||
*/
|
||||
static String makePlatformName(String osName, String osArch) {
|
||||
String os = osName.toLowerCase(Locale.ROOT);
|
||||
if (os.startsWith("windows")) {
|
||||
os = "windows";
|
||||
} else if (os.equals("mac os x")) {
|
||||
os = "darwin";
|
||||
}
|
||||
String cpu = osArch.toLowerCase(Locale.ROOT);
|
||||
if (cpu.equals("amd64")) {
|
||||
cpu = "x86_64";
|
||||
} else if (cpu.equals("i386")) {
|
||||
cpu = "x86";
|
||||
}
|
||||
return os + "-" + cpu;
|
||||
}
|
||||
}
|
||||
|
@ -21,9 +21,13 @@ package org.elasticsearch.plugins;
|
||||
public class DummyPluginInfo extends PluginInfo {
|
||||
|
||||
private DummyPluginInfo(String name, String description, String version, String classname) {
|
||||
super(name, description, version, classname);
|
||||
super(name, description, version, classname, false);
|
||||
}
|
||||
|
||||
public static final DummyPluginInfo INSTANCE = new DummyPluginInfo(
|
||||
"dummy_plugin_name", "dummy plugin description", "dummy_plugin_version", "DummyPluginName");
|
||||
public static final DummyPluginInfo INSTANCE =
|
||||
new DummyPluginInfo(
|
||||
"dummy_plugin_name",
|
||||
"dummy plugin description",
|
||||
"dummy_plugin_version",
|
||||
"DummyPluginName");
|
||||
}
|
||||
|
@ -458,7 +458,7 @@ class InstallPluginCommand extends EnvironmentAwareCommand {
|
||||
// if it exists, confirm or warn the user
|
||||
Path policy = pluginRoot.resolve(PluginInfo.ES_PLUGIN_POLICY);
|
||||
if (Files.exists(policy)) {
|
||||
PluginSecurity.readPolicy(policy, terminal, env, isBatch);
|
||||
PluginSecurity.readPolicy(info, policy, terminal, env::tmpFile, isBatch);
|
||||
}
|
||||
|
||||
return info;
|
||||
|
@ -61,7 +61,7 @@ class ListPluginsCommand extends EnvironmentAwareCommand {
|
||||
PluginInfo info = PluginInfo.readFromProperties(env.pluginsFile().resolve(plugin.toAbsolutePath()));
|
||||
terminal.println(Terminal.Verbosity.VERBOSE, info.toString());
|
||||
} catch (IllegalArgumentException e) {
|
||||
if (e.getMessage().contains("incompatible with Elasticsearch")) {
|
||||
if (e.getMessage().contains("incompatible with version")) {
|
||||
terminal.println("WARNING: " + e.getMessage());
|
||||
} else {
|
||||
throw e;
|
||||
|
83
core/src/main/java/org/elasticsearch/plugins/Platforms.java
Normal file
83
core/src/main/java/org/elasticsearch/plugins/Platforms.java
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch 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.elasticsearch.plugins;
|
||||
|
||||
import org.apache.lucene.util.Constants;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Encapsulates platform-dependent methods for handling native components of plugins.
|
||||
*/
|
||||
public class Platforms {
|
||||
|
||||
private static final String PROGRAM_NAME = Constants.WINDOWS ? "controller.exe" : "controller";
|
||||
private static final String PLATFORM_NAME =
|
||||
Platforms.platformName(Constants.OS_NAME, Constants.OS_ARCH);
|
||||
|
||||
private Platforms() {}
|
||||
|
||||
/**
|
||||
* The path to the native controller for a plugin with native components.
|
||||
*/
|
||||
public static Path nativeControllerPath(Path plugin) {
|
||||
return plugin
|
||||
.resolve("platform")
|
||||
.resolve(PLATFORM_NAME)
|
||||
.resolve("bin")
|
||||
.resolve(PROGRAM_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the platform name based on the OS name and
|
||||
* - darwin-x86_64
|
||||
* - linux-x86-64
|
||||
* - windows-x86_64
|
||||
* For *nix platforms this is more-or-less `uname -s`-`uname -m` converted to lower case.
|
||||
* However, for consistency between different operating systems on the same architecture
|
||||
* "amd64" is replaced with "x86_64" and "i386" with "x86".
|
||||
* For Windows it's "windows-" followed by either "x86" or "x86_64".
|
||||
*/
|
||||
public static String platformName(final String osName, final String osArch) {
|
||||
final String lowerCaseOs = osName.toLowerCase(Locale.ROOT);
|
||||
final String normalizedOs;
|
||||
if (lowerCaseOs.startsWith("windows")) {
|
||||
normalizedOs = "windows";
|
||||
} else if (lowerCaseOs.equals("mac os x")) {
|
||||
normalizedOs = "darwin";
|
||||
} else {
|
||||
normalizedOs = lowerCaseOs;
|
||||
}
|
||||
|
||||
final String lowerCaseArch = osArch.toLowerCase(Locale.ROOT);
|
||||
final String normalizedArch;
|
||||
if (lowerCaseArch.equals("amd64")) {
|
||||
normalizedArch = "x86_64";
|
||||
} else if (lowerCaseArch.equals("i386")) {
|
||||
normalizedArch = "x86";
|
||||
} else {
|
||||
normalizedArch = lowerCaseArch;
|
||||
}
|
||||
|
||||
return normalizedOs + "-" + normalizedArch;
|
||||
}
|
||||
|
||||
}
|
@ -16,6 +16,7 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.plugins;
|
||||
|
||||
import org.elasticsearch.Version;
|
||||
@ -30,133 +31,215 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* An in-memory representation of the plugin descriptor.
|
||||
*/
|
||||
public class PluginInfo implements Writeable, ToXContent {
|
||||
|
||||
public static final String ES_PLUGIN_PROPERTIES = "plugin-descriptor.properties";
|
||||
public static final String ES_PLUGIN_POLICY = "plugin-security.policy";
|
||||
|
||||
static final class Fields {
|
||||
static final String NAME = "name";
|
||||
static final String DESCRIPTION = "description";
|
||||
static final String URL = "url";
|
||||
static final String VERSION = "version";
|
||||
static final String CLASSNAME = "classname";
|
||||
}
|
||||
|
||||
private final String name;
|
||||
private final String description;
|
||||
private final String version;
|
||||
private final String classname;
|
||||
private final boolean hasNativeController;
|
||||
|
||||
/**
|
||||
* Information about plugins
|
||||
* Construct plugin info.
|
||||
*
|
||||
* @param name Its name
|
||||
* @param description Its description
|
||||
* @param version Version number
|
||||
* @param name the name of the plugin
|
||||
* @param description a description of the plugin
|
||||
* @param version the version of Elasticsearch the plugin is built for
|
||||
* @param classname the entry point to the plugin
|
||||
* @param hasNativeController whether or not the plugin has a native controller
|
||||
*/
|
||||
public PluginInfo(String name, String description, String version, String classname) {
|
||||
public PluginInfo(
|
||||
final String name,
|
||||
final String description,
|
||||
final String version,
|
||||
final String classname,
|
||||
final boolean hasNativeController) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.version = version;
|
||||
this.classname = classname;
|
||||
this.hasNativeController = hasNativeController;
|
||||
}
|
||||
|
||||
public PluginInfo(StreamInput in) throws IOException {
|
||||
/**
|
||||
* Construct plugin info from a stream.
|
||||
*
|
||||
* @param in the stream
|
||||
* @throws IOException if an I/O exception occurred reading the plugin info from the stream
|
||||
*/
|
||||
public PluginInfo(final StreamInput in) throws IOException {
|
||||
this.name = in.readString();
|
||||
this.description = in.readString();
|
||||
this.version = in.readString();
|
||||
this.classname = in.readString();
|
||||
if (in.getVersion().after(Version.V_5_4_0_UNRELEASED)) {
|
||||
hasNativeController = in.readBoolean();
|
||||
} else {
|
||||
hasNativeController = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
public void writeTo(final StreamOutput out) throws IOException {
|
||||
out.writeString(name);
|
||||
out.writeString(description);
|
||||
out.writeString(version);
|
||||
out.writeString(classname);
|
||||
if (out.getVersion().after(Version.V_5_4_0_UNRELEASED)) {
|
||||
out.writeBoolean(hasNativeController);
|
||||
}
|
||||
}
|
||||
|
||||
/** reads (and validates) plugin metadata descriptor file */
|
||||
public static PluginInfo readFromProperties(Path dir) throws IOException {
|
||||
Path descriptor = dir.resolve(ES_PLUGIN_PROPERTIES);
|
||||
Properties props = new Properties();
|
||||
|
||||
/**
|
||||
* Reads and validates the plugin descriptor file.
|
||||
*
|
||||
* @param path the path to the root directory for the plugin
|
||||
* @return the plugin info
|
||||
* @throws IOException if an I/O exception occurred reading the plugin descriptor
|
||||
*/
|
||||
public static PluginInfo readFromProperties(final Path path) throws IOException {
|
||||
final Path descriptor = path.resolve(ES_PLUGIN_PROPERTIES);
|
||||
final Properties props = new Properties();
|
||||
try (InputStream stream = Files.newInputStream(descriptor)) {
|
||||
props.load(stream);
|
||||
}
|
||||
String name = props.getProperty("name");
|
||||
final String name = props.getProperty("name");
|
||||
if (name == null || name.isEmpty()) {
|
||||
throw new IllegalArgumentException("Property [name] is missing in [" + descriptor + "]");
|
||||
throw new IllegalArgumentException(
|
||||
"property [name] is missing in [" + descriptor + "]");
|
||||
}
|
||||
String description = props.getProperty("description");
|
||||
final String description = props.getProperty("description");
|
||||
if (description == null) {
|
||||
throw new IllegalArgumentException("Property [description] is missing for plugin [" + name + "]");
|
||||
throw new IllegalArgumentException(
|
||||
"property [description] is missing for plugin [" + name + "]");
|
||||
}
|
||||
String version = props.getProperty("version");
|
||||
final String version = props.getProperty("version");
|
||||
if (version == null) {
|
||||
throw new IllegalArgumentException("Property [version] is missing for plugin [" + name + "]");
|
||||
throw new IllegalArgumentException(
|
||||
"property [version] is missing for plugin [" + name + "]");
|
||||
}
|
||||
|
||||
String esVersionString = props.getProperty("elasticsearch.version");
|
||||
final String esVersionString = props.getProperty("elasticsearch.version");
|
||||
if (esVersionString == null) {
|
||||
throw new IllegalArgumentException("Property [elasticsearch.version] is missing for plugin [" + name + "]");
|
||||
throw new IllegalArgumentException(
|
||||
"property [elasticsearch.version] is missing for plugin [" + name + "]");
|
||||
}
|
||||
Version esVersion = Version.fromString(esVersionString);
|
||||
final Version esVersion = Version.fromString(esVersionString);
|
||||
if (esVersion.equals(Version.CURRENT) == false) {
|
||||
throw new IllegalArgumentException("Plugin [" + name + "] is incompatible with Elasticsearch [" + Version.CURRENT.toString() +
|
||||
"]. Was designed for version [" + esVersionString + "]");
|
||||
final String message = String.format(
|
||||
Locale.ROOT,
|
||||
"plugin [%s] is incompatible with version [%s]; was designed for version [%s]",
|
||||
name,
|
||||
Version.CURRENT.toString(),
|
||||
esVersionString);
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
String javaVersionString = props.getProperty("java.version");
|
||||
final String javaVersionString = props.getProperty("java.version");
|
||||
if (javaVersionString == null) {
|
||||
throw new IllegalArgumentException("Property [java.version] is missing for plugin [" + name + "]");
|
||||
throw new IllegalArgumentException(
|
||||
"property [java.version] is missing for plugin [" + name + "]");
|
||||
}
|
||||
JarHell.checkVersionFormat(javaVersionString);
|
||||
JarHell.checkJavaVersion(name, javaVersionString);
|
||||
String classname = props.getProperty("classname");
|
||||
final String classname = props.getProperty("classname");
|
||||
if (classname == null) {
|
||||
throw new IllegalArgumentException("Property [classname] is missing for plugin [" + name + "]");
|
||||
throw new IllegalArgumentException(
|
||||
"property [classname] is missing for plugin [" + name + "]");
|
||||
}
|
||||
|
||||
return new PluginInfo(name, description, version, classname);
|
||||
final String hasNativeControllerValue = props.getProperty("has.native.controller");
|
||||
final boolean hasNativeController;
|
||||
if (hasNativeControllerValue == null) {
|
||||
hasNativeController = false;
|
||||
} else {
|
||||
switch (hasNativeControllerValue) {
|
||||
case "true":
|
||||
hasNativeController = true;
|
||||
break;
|
||||
case "false":
|
||||
hasNativeController = false;
|
||||
break;
|
||||
default:
|
||||
final String message = String.format(
|
||||
Locale.ROOT,
|
||||
"property [%s] must be [%s], [%s], or unspecified but was [%s]",
|
||||
"has_native_controller",
|
||||
"true",
|
||||
"false",
|
||||
hasNativeControllerValue);
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
}
|
||||
|
||||
return new PluginInfo(name, description, version, classname, hasNativeController);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Plugin's name
|
||||
* The name of the plugin.
|
||||
*
|
||||
* @return the plugin name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Plugin's description if any
|
||||
* The description of the plugin.
|
||||
*
|
||||
* @return the plugin description
|
||||
*/
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return plugin's classname
|
||||
* The entry point to the plugin.
|
||||
*
|
||||
* @return the entry point to the plugin
|
||||
*/
|
||||
public String getClassname() {
|
||||
return classname;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Version number for the plugin
|
||||
* The version of Elasticsearch the plugin was built for.
|
||||
*
|
||||
* @return the version
|
||||
*/
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the plugin has a native controller.
|
||||
*
|
||||
* @return {@code true} if the plugin has a native controller
|
||||
*/
|
||||
public boolean hasNativeController() {
|
||||
return hasNativeController;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field(Fields.NAME, name);
|
||||
builder.field(Fields.VERSION, version);
|
||||
builder.field(Fields.DESCRIPTION, description);
|
||||
builder.field(Fields.CLASSNAME, classname);
|
||||
{
|
||||
builder.field("name", name);
|
||||
builder.field("version", version);
|
||||
builder.field("description", description);
|
||||
builder.field("classname", classname);
|
||||
builder.field("has_native_controller", hasNativeController);
|
||||
}
|
||||
builder.endObject();
|
||||
|
||||
return builder;
|
||||
@ -187,8 +270,9 @@ public class PluginInfo implements Writeable, ToXContent {
|
||||
.append("Name: ").append(name).append("\n")
|
||||
.append("Description: ").append(description).append("\n")
|
||||
.append("Version: ").append(version).append("\n")
|
||||
.append("Native Controller: ").append(hasNativeController).append("\n")
|
||||
.append(" * Classname: ").append(classname);
|
||||
|
||||
return information.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -37,60 +37,74 @@ import java.security.UnresolvedPermission;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
class PluginSecurity {
|
||||
|
||||
/**
|
||||
* Reads plugin policy, prints/confirms exceptions
|
||||
*/
|
||||
static void readPolicy(Path file, Terminal terminal, Environment environment, boolean batch) throws IOException {
|
||||
PermissionCollection permissions = parsePermissions(terminal, file, environment.tmpFile());
|
||||
static void readPolicy(PluginInfo info, Path file, Terminal terminal, Supplier<Path> tmpFile, boolean batch) throws IOException {
|
||||
PermissionCollection permissions = parsePermissions(terminal, file, tmpFile.get());
|
||||
List<Permission> requested = Collections.list(permissions.elements());
|
||||
if (requested.isEmpty()) {
|
||||
terminal.println(Verbosity.VERBOSE, "plugin has a policy file with no additional permissions");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
|
||||
// sort permissions in a reasonable order
|
||||
Collections.sort(requested, new Comparator<Permission>() {
|
||||
@Override
|
||||
public int compare(Permission o1, Permission o2) {
|
||||
int cmp = o1.getClass().getName().compareTo(o2.getClass().getName());
|
||||
if (cmp == 0) {
|
||||
String name1 = o1.getName();
|
||||
String name2 = o2.getName();
|
||||
if (name1 == null) {
|
||||
name1 = "";
|
||||
}
|
||||
if (name2 == null) {
|
||||
name2 = "";
|
||||
}
|
||||
cmp = name1.compareTo(name2);
|
||||
// sort permissions in a reasonable order
|
||||
Collections.sort(requested, new Comparator<Permission>() {
|
||||
@Override
|
||||
public int compare(Permission o1, Permission o2) {
|
||||
int cmp = o1.getClass().getName().compareTo(o2.getClass().getName());
|
||||
if (cmp == 0) {
|
||||
String actions1 = o1.getActions();
|
||||
String actions2 = o2.getActions();
|
||||
if (actions1 == null) {
|
||||
actions1 = "";
|
||||
String name1 = o1.getName();
|
||||
String name2 = o2.getName();
|
||||
if (name1 == null) {
|
||||
name1 = "";
|
||||
}
|
||||
if (actions2 == null) {
|
||||
actions2 = "";
|
||||
if (name2 == null) {
|
||||
name2 = "";
|
||||
}
|
||||
cmp = name1.compareTo(name2);
|
||||
if (cmp == 0) {
|
||||
String actions1 = o1.getActions();
|
||||
String actions2 = o2.getActions();
|
||||
if (actions1 == null) {
|
||||
actions1 = "";
|
||||
}
|
||||
if (actions2 == null) {
|
||||
actions2 = "";
|
||||
}
|
||||
cmp = actions1.compareTo(actions2);
|
||||
}
|
||||
cmp = actions1.compareTo(actions2);
|
||||
}
|
||||
return cmp;
|
||||
}
|
||||
return cmp;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
terminal.println(Verbosity.NORMAL, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
|
||||
terminal.println(Verbosity.NORMAL, "@ WARNING: plugin requires additional permissions @");
|
||||
terminal.println(Verbosity.NORMAL, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
|
||||
// print all permissions:
|
||||
for (Permission permission : requested) {
|
||||
terminal.println(Verbosity.NORMAL, "* " + formatPermission(permission));
|
||||
terminal.println(Verbosity.NORMAL, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
|
||||
terminal.println(Verbosity.NORMAL, "@ WARNING: plugin requires additional permissions @");
|
||||
terminal.println(Verbosity.NORMAL, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
|
||||
// print all permissions:
|
||||
for (Permission permission : requested) {
|
||||
terminal.println(Verbosity.NORMAL, "* " + formatPermission(permission));
|
||||
}
|
||||
terminal.println(Verbosity.NORMAL, "See http://docs.oracle.com/javase/8/docs/technotes/guides/security/permissions.html");
|
||||
terminal.println(Verbosity.NORMAL, "for descriptions of what these permissions allow and the associated risks.");
|
||||
prompt(terminal, batch);
|
||||
}
|
||||
terminal.println(Verbosity.NORMAL, "See http://docs.oracle.com/javase/8/docs/technotes/guides/security/permissions.html");
|
||||
terminal.println(Verbosity.NORMAL, "for descriptions of what these permissions allow and the associated risks.");
|
||||
|
||||
if (info.hasNativeController()) {
|
||||
terminal.println(Verbosity.NORMAL, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
|
||||
terminal.println(Verbosity.NORMAL, "@ WARNING: plugin forks a native controller @");
|
||||
terminal.println(Verbosity.NORMAL, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
|
||||
terminal.println(Verbosity.NORMAL, "This plugin launches a native controller that is not subject to the Java");
|
||||
terminal.println(Verbosity.NORMAL, "security manager nor to system call filters.");
|
||||
prompt(terminal, batch);
|
||||
}
|
||||
}
|
||||
|
||||
private static void prompt(final Terminal terminal, final boolean batch) {
|
||||
if (!batch) {
|
||||
terminal.println(Verbosity.NORMAL, "");
|
||||
String text = terminal.readText("Continue with installation? [y/N]");
|
||||
|
@ -101,7 +101,7 @@ public class PluginsService extends AbstractComponent {
|
||||
// first we load plugins that are on the classpath. this is for tests and transport clients
|
||||
for (Class<? extends Plugin> pluginClass : classpathPlugins) {
|
||||
Plugin plugin = loadPlugin(pluginClass, settings);
|
||||
PluginInfo pluginInfo = new PluginInfo(pluginClass.getName(), "classpath plugin", "NA", pluginClass.getName());
|
||||
PluginInfo pluginInfo = new PluginInfo(pluginClass.getName(), "classpath plugin", "NA", pluginClass.getName(), false);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("plugin loaded from classpath [{}]", pluginInfo);
|
||||
}
|
||||
|
@ -143,13 +143,13 @@ public class NodeInfoStreamingTests extends ESTestCase {
|
||||
List<PluginInfo> plugins = new ArrayList<>();
|
||||
for (int i = 0; i < numPlugins; i++) {
|
||||
plugins.add(new PluginInfo(randomAsciiOfLengthBetween(3, 10), randomAsciiOfLengthBetween(3, 10),
|
||||
randomAsciiOfLengthBetween(3, 10), randomAsciiOfLengthBetween(3, 10)));
|
||||
randomAsciiOfLengthBetween(3, 10), randomAsciiOfLengthBetween(3, 10), randomBoolean()));
|
||||
}
|
||||
int numModules = randomIntBetween(0, 5);
|
||||
List<PluginInfo> modules = new ArrayList<>();
|
||||
for (int i = 0; i < numModules; i++) {
|
||||
modules.add(new PluginInfo(randomAsciiOfLengthBetween(3, 10), randomAsciiOfLengthBetween(3, 10),
|
||||
randomAsciiOfLengthBetween(3, 10), randomAsciiOfLengthBetween(3, 10)));
|
||||
randomAsciiOfLengthBetween(3, 10), randomAsciiOfLengthBetween(3, 10), randomBoolean()));
|
||||
}
|
||||
pluginsAndModules = new PluginsAndModules(plugins, modules);
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ public class PluginInfoTests extends ESTestCase {
|
||||
PluginInfo.readFromProperties(pluginDir);
|
||||
fail("expected missing name exception");
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertTrue(e.getMessage().contains("Property [name] is missing in"));
|
||||
assertTrue(e.getMessage().contains("property [name] is missing in"));
|
||||
}
|
||||
|
||||
PluginTestUtil.writeProperties(pluginDir, "name", "");
|
||||
@ -64,7 +64,7 @@ public class PluginInfoTests extends ESTestCase {
|
||||
PluginInfo.readFromProperties(pluginDir);
|
||||
fail("expected missing name exception");
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertTrue(e.getMessage().contains("Property [name] is missing in"));
|
||||
assertTrue(e.getMessage().contains("property [name] is missing in"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,7 +81,8 @@ public class PluginInfoTests extends ESTestCase {
|
||||
|
||||
public void testReadFromPropertiesVersionMissing() throws Exception {
|
||||
Path pluginDir = createTempDir().resolve("fake-plugin");
|
||||
PluginTestUtil.writeProperties(pluginDir, "description", "fake desc", "name", "fake-plugin");
|
||||
PluginTestUtil.writeProperties(
|
||||
pluginDir, "description", "fake desc", "name", "fake-plugin");
|
||||
try {
|
||||
PluginInfo.readFromProperties(pluginDir);
|
||||
fail("expected missing version exception");
|
||||
@ -151,7 +152,11 @@ public class PluginInfoTests extends ESTestCase {
|
||||
PluginInfo.readFromProperties(pluginDir);
|
||||
fail("expected bad java version format exception");
|
||||
} catch (IllegalStateException e) {
|
||||
assertTrue(e.getMessage(), e.getMessage().equals("version string must be a sequence of nonnegative decimal integers separated by \".\"'s and may have leading zeros but was 1.7.0_80"));
|
||||
assertTrue(
|
||||
e.getMessage(),
|
||||
e.getMessage().equals("version string must be a sequence of nonnegative "
|
||||
+ "decimal integers separated by \".\"'s and may have leading zeros "
|
||||
+ "but was 1.7.0_80"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -166,7 +171,8 @@ public class PluginInfoTests extends ESTestCase {
|
||||
PluginInfo.readFromProperties(pluginDir);
|
||||
fail("expected bogus elasticsearch version exception");
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertTrue(e.getMessage().contains("version needs to contain major, minor, and revision"));
|
||||
assertTrue(e.getMessage().contains(
|
||||
"version needs to contain major, minor, and revision"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -181,7 +187,7 @@ public class PluginInfoTests extends ESTestCase {
|
||||
PluginInfo.readFromProperties(pluginDir);
|
||||
fail("expected old elasticsearch version exception");
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertTrue(e.getMessage().contains("Was designed for version [2.0.0]"));
|
||||
assertTrue(e.getMessage().contains("was designed for version [2.0.0]"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -197,17 +203,17 @@ public class PluginInfoTests extends ESTestCase {
|
||||
PluginInfo.readFromProperties(pluginDir);
|
||||
fail("expected old elasticsearch version exception");
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertTrue(e.getMessage().contains("Property [classname] is missing"));
|
||||
assertTrue(e.getMessage().contains("property [classname] is missing"));
|
||||
}
|
||||
}
|
||||
|
||||
public void testPluginListSorted() {
|
||||
List<PluginInfo> plugins = new ArrayList<>();
|
||||
plugins.add(new PluginInfo("c", "foo", "dummy", "dummyclass"));
|
||||
plugins.add(new PluginInfo("b", "foo", "dummy", "dummyclass"));
|
||||
plugins.add(new PluginInfo("e", "foo", "dummy", "dummyclass"));
|
||||
plugins.add(new PluginInfo("a", "foo", "dummy", "dummyclass"));
|
||||
plugins.add(new PluginInfo("d", "foo", "dummy", "dummyclass"));
|
||||
plugins.add(new PluginInfo("c", "foo", "dummy", "dummyclass", randomBoolean()));
|
||||
plugins.add(new PluginInfo("b", "foo", "dummy", "dummyclass", randomBoolean()));
|
||||
plugins.add(new PluginInfo("e", "foo", "dummy", "dummyclass", randomBoolean()));
|
||||
plugins.add(new PluginInfo("a", "foo", "dummy", "dummyclass", randomBoolean()));
|
||||
plugins.add(new PluginInfo("d", "foo", "dummy", "dummyclass", randomBoolean()));
|
||||
PluginsAndModules pluginsInfo = new PluginsAndModules(plugins, Collections.emptyList());
|
||||
|
||||
|
||||
@ -215,4 +221,5 @@ public class PluginInfoTests extends ESTestCase {
|
||||
List<String> names = infos.stream().map(PluginInfo::getName).collect(Collectors.toList());
|
||||
assertThat(names, contains("a", "b", "c", "d", "e"));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,20 +17,17 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.bootstrap;
|
||||
package org.elasticsearch.plugins;
|
||||
|
||||
import org.apache.lucene.util.Constants;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Doesn't actually test spawning a process, as a system call filter is installed before tests run and forbids it.
|
||||
*/
|
||||
public class SpawnerTests extends ESTestCase {
|
||||
public class PluginsTests extends ESTestCase {
|
||||
|
||||
public void testMakePlatformName() {
|
||||
String platformName = Spawner.makePlatformName(Constants.OS_NAME, Constants.OS_ARCH);
|
||||
final String platformName = Platforms.platformName(Constants.OS_NAME, Constants.OS_ARCH);
|
||||
|
||||
assertFalse(platformName, platformName.isEmpty());
|
||||
assertTrue(platformName, platformName.equals(platformName.toLowerCase(Locale.ROOT)));
|
||||
@ -40,13 +37,13 @@ public class SpawnerTests extends ESTestCase {
|
||||
}
|
||||
|
||||
public void testMakeSpecificPlatformNames() {
|
||||
assertEquals("darwin-x86_64", Spawner.makePlatformName("Mac OS X", "x86_64"));
|
||||
assertEquals("linux-x86_64", Spawner.makePlatformName("Linux", "amd64"));
|
||||
assertEquals("linux-x86", Spawner.makePlatformName("Linux", "i386"));
|
||||
assertEquals("windows-x86_64", Spawner.makePlatformName("Windows Server 2008 R2", "amd64"));
|
||||
assertEquals("windows-x86", Spawner.makePlatformName("Windows Server 2008", "x86"));
|
||||
assertEquals("windows-x86_64", Spawner.makePlatformName("Windows 8.1", "amd64"));
|
||||
assertEquals("sunos-x86_64", Spawner.makePlatformName("SunOS", "amd64"));
|
||||
assertEquals("darwin-x86_64", Platforms.platformName("Mac OS X", "x86_64"));
|
||||
assertEquals("linux-x86_64", Platforms.platformName("Linux", "amd64"));
|
||||
assertEquals("linux-x86", Platforms.platformName("Linux", "i386"));
|
||||
assertEquals("windows-x86_64", Platforms.platformName("Windows Server 2008 R2", "amd64"));
|
||||
assertEquals("windows-x86", Platforms.platformName("Windows Server 2008", "x86"));
|
||||
assertEquals("windows-x86_64", Platforms.platformName("Windows 8.1", "amd64"));
|
||||
assertEquals("sunos-x86_64", Platforms.platformName("SunOS", "amd64"));
|
||||
}
|
||||
|
||||
}
|
@ -25,6 +25,7 @@ import java.nio.file.Files;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
@ -72,25 +73,39 @@ public class ListPluginsCommandTests extends ESTestCase {
|
||||
return terminal;
|
||||
}
|
||||
|
||||
static String buildMultiline(String... args){
|
||||
return Arrays.asList(args).stream().collect(Collectors.joining("\n", "", "\n"));
|
||||
private static String buildMultiline(String... args){
|
||||
return Arrays.stream(args).collect(Collectors.joining("\n", "", "\n"));
|
||||
}
|
||||
|
||||
static void buildFakePlugin(Environment env, String description, String name, String classname) throws IOException {
|
||||
PluginTestUtil.writeProperties(env.pluginsFile().resolve(name),
|
||||
private static void buildFakePlugin(
|
||||
final Environment env,
|
||||
final String description,
|
||||
final String name,
|
||||
final String classname) throws IOException {
|
||||
buildFakePlugin(env, description, name, classname, false);
|
||||
}
|
||||
|
||||
private static void buildFakePlugin(
|
||||
final Environment env,
|
||||
final String description,
|
||||
final String name,
|
||||
final String classname,
|
||||
final boolean hasNativeController) throws IOException {
|
||||
PluginTestUtil.writeProperties(
|
||||
env.pluginsFile().resolve(name),
|
||||
"description", description,
|
||||
"name", name,
|
||||
"version", "1.0",
|
||||
"elasticsearch.version", Version.CURRENT.toString(),
|
||||
"java.version", System.getProperty("java.specification.version"),
|
||||
"classname", classname);
|
||||
"classname", classname,
|
||||
"has.native.controller", Boolean.toString(hasNativeController));
|
||||
}
|
||||
|
||||
|
||||
public void testPluginsDirMissing() throws Exception {
|
||||
Files.delete(env.pluginsFile());
|
||||
IOException e = expectThrows(IOException.class, () -> listPlugins(home));
|
||||
assertEquals(e.getMessage(), "Plugins directory missing: " + env.pluginsFile());
|
||||
assertEquals("Plugins directory missing: " + env.pluginsFile(), e.getMessage());
|
||||
}
|
||||
|
||||
public void testNoPlugins() throws Exception {
|
||||
@ -101,22 +116,48 @@ public class ListPluginsCommandTests extends ESTestCase {
|
||||
public void testOnePlugin() throws Exception {
|
||||
buildFakePlugin(env, "fake desc", "fake", "org.fake");
|
||||
MockTerminal terminal = listPlugins(home);
|
||||
assertEquals(terminal.getOutput(), buildMultiline("fake"));
|
||||
assertEquals(buildMultiline("fake"), terminal.getOutput());
|
||||
}
|
||||
|
||||
public void testTwoPlugins() throws Exception {
|
||||
buildFakePlugin(env, "fake desc", "fake1", "org.fake");
|
||||
buildFakePlugin(env, "fake desc 2", "fake2", "org.fake");
|
||||
MockTerminal terminal = listPlugins(home);
|
||||
assertEquals(terminal.getOutput(), buildMultiline("fake1", "fake2"));
|
||||
assertEquals(buildMultiline("fake1", "fake2"), terminal.getOutput());
|
||||
}
|
||||
|
||||
public void testPluginWithVerbose() throws Exception {
|
||||
buildFakePlugin(env, "fake desc", "fake_plugin", "org.fake");
|
||||
String[] params = { "-v" };
|
||||
MockTerminal terminal = listPlugins(home, params);
|
||||
assertEquals(terminal.getOutput(), buildMultiline("Plugins directory: " + env.pluginsFile(), "fake_plugin",
|
||||
"- Plugin information:", "Name: fake_plugin", "Description: fake desc", "Version: 1.0", " * Classname: org.fake"));
|
||||
assertEquals(
|
||||
buildMultiline(
|
||||
"Plugins directory: " + env.pluginsFile(),
|
||||
"fake_plugin",
|
||||
"- Plugin information:",
|
||||
"Name: fake_plugin",
|
||||
"Description: fake desc",
|
||||
"Version: 1.0",
|
||||
"Native Controller: false",
|
||||
" * Classname: org.fake"),
|
||||
terminal.getOutput());
|
||||
}
|
||||
|
||||
public void testPluginWithNativeController() throws Exception {
|
||||
buildFakePlugin(env, "fake desc 1", "fake_plugin1", "org.fake", true);
|
||||
String[] params = { "-v" };
|
||||
MockTerminal terminal = listPlugins(home, params);
|
||||
assertEquals(
|
||||
buildMultiline(
|
||||
"Plugins directory: " + env.pluginsFile(),
|
||||
"fake_plugin1",
|
||||
"- Plugin information:",
|
||||
"Name: fake_plugin1",
|
||||
"Description: fake desc 1",
|
||||
"Version: 1.0",
|
||||
"Native Controller: true",
|
||||
" * Classname: org.fake"),
|
||||
terminal.getOutput());
|
||||
}
|
||||
|
||||
public void testPluginWithVerboseMultiplePlugins() throws Exception {
|
||||
@ -124,10 +165,24 @@ public class ListPluginsCommandTests extends ESTestCase {
|
||||
buildFakePlugin(env, "fake desc 2", "fake_plugin2", "org.fake2");
|
||||
String[] params = { "-v" };
|
||||
MockTerminal terminal = listPlugins(home, params);
|
||||
assertEquals(terminal.getOutput(), buildMultiline("Plugins directory: " + env.pluginsFile(),
|
||||
"fake_plugin1", "- Plugin information:", "Name: fake_plugin1", "Description: fake desc 1", "Version: 1.0",
|
||||
" * Classname: org.fake", "fake_plugin2", "- Plugin information:", "Name: fake_plugin2",
|
||||
"Description: fake desc 2", "Version: 1.0", " * Classname: org.fake2"));
|
||||
assertEquals(
|
||||
buildMultiline(
|
||||
"Plugins directory: " + env.pluginsFile(),
|
||||
"fake_plugin1",
|
||||
"- Plugin information:",
|
||||
"Name: fake_plugin1",
|
||||
"Description: fake desc 1",
|
||||
"Version: 1.0",
|
||||
"Native Controller: false",
|
||||
" * Classname: org.fake",
|
||||
"fake_plugin2",
|
||||
"- Plugin information:",
|
||||
"Name: fake_plugin2",
|
||||
"Description: fake desc 2",
|
||||
"Version: 1.0",
|
||||
"Native Controller: false",
|
||||
" * Classname: org.fake2"),
|
||||
terminal.getOutput());
|
||||
}
|
||||
|
||||
public void testPluginWithoutVerboseMultiplePlugins() throws Exception {
|
||||
@ -135,21 +190,26 @@ public class ListPluginsCommandTests extends ESTestCase {
|
||||
buildFakePlugin(env, "fake desc 2", "fake_plugin2", "org.fake2");
|
||||
MockTerminal terminal = listPlugins(home, new String[0]);
|
||||
String output = terminal.getOutput();
|
||||
assertEquals(output, buildMultiline("fake_plugin1", "fake_plugin2"));
|
||||
assertEquals(buildMultiline("fake_plugin1", "fake_plugin2"), output);
|
||||
}
|
||||
|
||||
public void testPluginWithoutDescriptorFile() throws Exception{
|
||||
Files.createDirectories(env.pluginsFile().resolve("fake1"));
|
||||
final Path pluginDir = env.pluginsFile().resolve("fake1");
|
||||
Files.createDirectories(pluginDir);
|
||||
NoSuchFileException e = expectThrows(NoSuchFileException.class, () -> listPlugins(home));
|
||||
assertEquals(e.getFile(), env.pluginsFile().resolve("fake1").resolve(PluginInfo.ES_PLUGIN_PROPERTIES).toString());
|
||||
assertEquals(pluginDir.resolve(PluginInfo.ES_PLUGIN_PROPERTIES).toString(), e.getFile());
|
||||
}
|
||||
|
||||
public void testPluginWithWrongDescriptorFile() throws Exception{
|
||||
PluginTestUtil.writeProperties(env.pluginsFile().resolve("fake1"),
|
||||
"description", "fake desc");
|
||||
IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> listPlugins(home));
|
||||
assertEquals(e.getMessage(), "Property [name] is missing in [" +
|
||||
env.pluginsFile().resolve("fake1").resolve(PluginInfo.ES_PLUGIN_PROPERTIES).toString() + "]");
|
||||
final Path pluginDir = env.pluginsFile().resolve("fake1");
|
||||
PluginTestUtil.writeProperties(pluginDir, "description", "fake desc");
|
||||
IllegalArgumentException e = expectThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> listPlugins(home));
|
||||
final Path descriptorPath = pluginDir.resolve(PluginInfo.ES_PLUGIN_PROPERTIES);
|
||||
assertEquals(
|
||||
"property [name] is missing in [" + descriptorPath.toString() + "]",
|
||||
e.getMessage());
|
||||
}
|
||||
|
||||
public void testExistingIncompatiblePlugin() throws Exception {
|
||||
@ -163,11 +223,14 @@ public class ListPluginsCommandTests extends ESTestCase {
|
||||
buildFakePlugin(env, "fake desc 2", "fake_plugin2", "org.fake2");
|
||||
|
||||
MockTerminal terminal = listPlugins(home);
|
||||
assertEquals("fake_plugin1\n" +
|
||||
"WARNING: Plugin [fake_plugin1] is incompatible with Elasticsearch [" +
|
||||
Version.CURRENT.toString() + "]. Was designed for version [1.0.0]\n" +
|
||||
"fake_plugin2\n",
|
||||
terminal.getOutput());
|
||||
final String message = String.format(Locale.ROOT,
|
||||
"plugin [%s] is incompatible with version [%s]; was designed for version [%s]",
|
||||
"fake_plugin1",
|
||||
Version.CURRENT.toString(),
|
||||
"1.0.0");
|
||||
assertEquals(
|
||||
"fake_plugin1\n" + "WARNING: " + message + "\n" + "fake_plugin2\n",
|
||||
terminal.getOutput());
|
||||
|
||||
String[] params = {"-s"};
|
||||
terminal = listPlugins(home, params);
|
||||
|
@ -19,60 +19,132 @@
|
||||
|
||||
package org.elasticsearch.plugins;
|
||||
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.cli.MockTerminal;
|
||||
import org.elasticsearch.cli.Terminal;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.security.Permission;
|
||||
import java.security.PermissionCollection;
|
||||
import java.security.Permissions;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.hasToString;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
|
||||
/** Tests plugin manager security check */
|
||||
public class PluginSecurityTests extends ESTestCase {
|
||||
|
||||
private final Supplier<Path> tmpFile = LuceneTestCase::createTempDir;
|
||||
|
||||
public void testHasNativeController() throws IOException {
|
||||
assumeTrue(
|
||||
"test cannot run with security manager enabled",
|
||||
System.getSecurityManager() == null);
|
||||
final PluginInfo info =
|
||||
new PluginInfo("fake", "fake", Version.CURRENT.toString(), "Fake", true);
|
||||
final MockTerminal terminal = new MockTerminal();
|
||||
terminal.addTextInput("y");
|
||||
terminal.addTextInput("y");
|
||||
final Path policyFile = this.getDataPath("security/simple-plugin-security.policy");
|
||||
PluginSecurity.readPolicy(info, policyFile, terminal, tmpFile, false);
|
||||
final String output = terminal.getOutput();
|
||||
assertThat(output, containsString("plugin forks a native controller"));
|
||||
}
|
||||
|
||||
public void testDeclineNativeController() throws IOException {
|
||||
assumeTrue(
|
||||
"test cannot run with security manager enabled",
|
||||
System.getSecurityManager() == null);
|
||||
final PluginInfo info =
|
||||
new PluginInfo("fake", "fake", Version.CURRENT.toString(), "Fake", true);
|
||||
final MockTerminal terminal = new MockTerminal();
|
||||
terminal.addTextInput("y");
|
||||
terminal.addTextInput("n");
|
||||
final Path policyFile = this.getDataPath("security/simple-plugin-security.policy");
|
||||
RuntimeException e = expectThrows(
|
||||
RuntimeException.class,
|
||||
() -> PluginSecurity.readPolicy(info, policyFile, terminal, tmpFile, false));
|
||||
assertThat(e, hasToString(containsString("installation aborted by user")));
|
||||
}
|
||||
|
||||
public void testDoesNotHaveNativeController() throws IOException {
|
||||
assumeTrue(
|
||||
"test cannot run with security manager enabled",
|
||||
System.getSecurityManager() == null);
|
||||
final PluginInfo info =
|
||||
new PluginInfo("fake", "fake", Version.CURRENT.toString(), "Fake", false);
|
||||
final MockTerminal terminal = new MockTerminal();
|
||||
terminal.addTextInput("y");
|
||||
final Path policyFile = this.getDataPath("security/simple-plugin-security.policy");
|
||||
PluginSecurity.readPolicy(info, policyFile, terminal, tmpFile, false);
|
||||
final String output = terminal.getOutput();
|
||||
assertThat(output, not(containsString("plugin forks a native controller")));
|
||||
}
|
||||
|
||||
/** Test that we can parse the set of permissions correctly for a simple policy */
|
||||
public void testParsePermissions() throws Exception {
|
||||
assumeTrue("test cannot run with security manager enabled", System.getSecurityManager() == null);
|
||||
assumeTrue(
|
||||
"test cannot run with security manager enabled",
|
||||
System.getSecurityManager() == null);
|
||||
Path scratch = createTempDir();
|
||||
Path testFile = this.getDataPath("security/simple-plugin-security.policy");
|
||||
Permissions expected = new Permissions();
|
||||
expected.add(new RuntimePermission("queuePrintJob"));
|
||||
PermissionCollection actual = PluginSecurity.parsePermissions(Terminal.DEFAULT, testFile, scratch);
|
||||
PermissionCollection actual =
|
||||
PluginSecurity.parsePermissions(Terminal.DEFAULT, testFile, scratch);
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
/** Test that we can parse the set of permissions correctly for a complex policy */
|
||||
public void testParseTwoPermissions() throws Exception {
|
||||
assumeTrue("test cannot run with security manager enabled", System.getSecurityManager() == null);
|
||||
assumeTrue(
|
||||
"test cannot run with security manager enabled",
|
||||
System.getSecurityManager() == null);
|
||||
Path scratch = createTempDir();
|
||||
Path testFile = this.getDataPath("security/complex-plugin-security.policy");
|
||||
Permissions expected = new Permissions();
|
||||
expected.add(new RuntimePermission("getClassLoader"));
|
||||
expected.add(new RuntimePermission("closeClassLoader"));
|
||||
PermissionCollection actual = PluginSecurity.parsePermissions(Terminal.DEFAULT, testFile, scratch);
|
||||
PermissionCollection actual =
|
||||
PluginSecurity.parsePermissions(Terminal.DEFAULT, testFile, scratch);
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
/** Test that we can format some simple permissions properly */
|
||||
public void testFormatSimplePermission() throws Exception {
|
||||
assertEquals("java.lang.RuntimePermission queuePrintJob", PluginSecurity.formatPermission(new RuntimePermission("queuePrintJob")));
|
||||
assertEquals(
|
||||
"java.lang.RuntimePermission queuePrintJob",
|
||||
PluginSecurity.formatPermission(new RuntimePermission("queuePrintJob")));
|
||||
}
|
||||
|
||||
/** Test that we can format an unresolved permission properly */
|
||||
public void testFormatUnresolvedPermission() throws Exception {
|
||||
assumeTrue("test cannot run with security manager enabled", System.getSecurityManager() == null);
|
||||
assumeTrue(
|
||||
"test cannot run with security manager enabled",
|
||||
System.getSecurityManager() == null);
|
||||
Path scratch = createTempDir();
|
||||
Path testFile = this.getDataPath("security/unresolved-plugin-security.policy");
|
||||
PermissionCollection actual = PluginSecurity.parsePermissions(Terminal.DEFAULT, testFile, scratch);
|
||||
PermissionCollection actual =
|
||||
PluginSecurity.parsePermissions(Terminal.DEFAULT, testFile, scratch);
|
||||
List<Permission> permissions = Collections.list(actual.elements());
|
||||
assertEquals(1, permissions.size());
|
||||
assertEquals("org.fake.FakePermission fakeName", PluginSecurity.formatPermission(permissions.get(0)));
|
||||
assertEquals(
|
||||
"org.fake.FakePermission fakeName",
|
||||
PluginSecurity.formatPermission(permissions.get(0)));
|
||||
}
|
||||
|
||||
/** no guaranteed equals on these classes, we assert they contain the same set */
|
||||
private void assertEquals(PermissionCollection expected, PermissionCollection actual) {
|
||||
assertEquals(asSet(Collections.list(expected.elements())), asSet(Collections.list(actual.elements())));
|
||||
assertEquals(
|
||||
asSet(Collections.list(expected.elements())),
|
||||
asSet(Collections.list(actual.elements())));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -21,8 +21,11 @@ package org.elasticsearch.bootstrap;
|
||||
|
||||
import org.apache.lucene.util.Constants;
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.plugins.PluginTestUtil;
|
||||
import org.elasticsearch.plugins.Platforms;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
@ -36,11 +39,15 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
|
||||
/**
|
||||
* Create a simple "daemon controller", put it in the right place and check that it runs.
|
||||
*
|
||||
* Extends LuceneTestCase rather than ESTestCase as ESTestCase installs a system call filter, and that prevents the Spawner class doing its
|
||||
* job. Also needs to run in a separate JVM to other tests that extend ESTestCase for the same reason.
|
||||
* Extends LuceneTestCase rather than ESTestCase as ESTestCase installs a system call filter, and
|
||||
* that prevents the Spawner class from doing its job. Also needs to run in a separate JVM to other
|
||||
* tests that extend ESTestCase for the same reason.
|
||||
*/
|
||||
public class SpawnerNoBootstrapTests extends LuceneTestCase {
|
||||
|
||||
@ -64,10 +71,19 @@ public class SpawnerNoBootstrapTests extends LuceneTestCase {
|
||||
// This plugin will NOT have a controller daemon
|
||||
Path plugin = environment.pluginsFile().resolve("a_plugin");
|
||||
Files.createDirectories(plugin);
|
||||
PluginTestUtil.writeProperties(
|
||||
plugin,
|
||||
"description", "a_plugin",
|
||||
"version", Version.CURRENT.toString(),
|
||||
"elasticsearch.version", Version.CURRENT.toString(),
|
||||
"name", "a_plugin",
|
||||
"java.version", "1.8",
|
||||
"classname", "APlugin",
|
||||
"has.native.controller", "false");
|
||||
|
||||
try (Spawner spawner = new Spawner()) {
|
||||
spawner.spawnNativePluginControllers(environment);
|
||||
assertTrue(spawner.getProcesses().isEmpty());
|
||||
assertThat(spawner.getProcesses(), hasSize(0));
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,10 +91,10 @@ public class SpawnerNoBootstrapTests extends LuceneTestCase {
|
||||
* Two plugins - one with a controller daemon and one without.
|
||||
*/
|
||||
public void testControllerSpawn() throws IOException, InterruptedException {
|
||||
// On Windows you cannot directly run a batch file - you have to run cmd.exe with the batch file
|
||||
// as an argument and that's out of the remit of the controller daemon process spawner. If
|
||||
// you need to build on Windows, just don't run this test. The process spawner itself will work
|
||||
// with native processes.
|
||||
/*
|
||||
* On Windows you can not directly run a batch file - you have to run cmd.exe with the batch
|
||||
* file as an argument and that's out of the remit of the controller daemon process spawner.
|
||||
*/
|
||||
assumeFalse("This test does not work on Windows", Constants.WINDOWS);
|
||||
|
||||
Path esHome = createTempDir().resolve("esHome");
|
||||
@ -88,32 +104,90 @@ public class SpawnerNoBootstrapTests extends LuceneTestCase {
|
||||
|
||||
Environment environment = new Environment(settings);
|
||||
|
||||
// This plugin WILL have a controller daemon
|
||||
// this plugin will have a controller daemon
|
||||
Path plugin = environment.pluginsFile().resolve("test_plugin");
|
||||
Files.createDirectories(plugin);
|
||||
Path controllerProgram = Spawner.makeSpawnPath(plugin);
|
||||
PluginTestUtil.writeProperties(
|
||||
plugin,
|
||||
"description", "test_plugin",
|
||||
"version", Version.CURRENT.toString(),
|
||||
"elasticsearch.version", Version.CURRENT.toString(),
|
||||
"name", "test_plugin",
|
||||
"java.version", "1.8",
|
||||
"classname", "TestPlugin",
|
||||
"has.native.controller", "true");
|
||||
Path controllerProgram = Platforms.nativeControllerPath(plugin);
|
||||
createControllerProgram(controllerProgram);
|
||||
|
||||
// This plugin will NOT have a controller daemon
|
||||
// this plugin will not have a controller daemon
|
||||
Path otherPlugin = environment.pluginsFile().resolve("other_plugin");
|
||||
Files.createDirectories(otherPlugin);
|
||||
PluginTestUtil.writeProperties(
|
||||
otherPlugin,
|
||||
"description", "other_plugin",
|
||||
"version", Version.CURRENT.toString(),
|
||||
"elasticsearch.version", Version.CURRENT.toString(),
|
||||
"name", "other_plugin",
|
||||
"java.version", "1.8",
|
||||
"classname", "OtherPlugin",
|
||||
"has.native.controller", "false");
|
||||
|
||||
Spawner spawner = new Spawner();
|
||||
spawner.spawnNativePluginControllers(environment);
|
||||
|
||||
List<Process> processes = spawner.getProcesses();
|
||||
// 1 because there should only be a reference in the list for the plugin that had the controller daemon, not the other plugin
|
||||
assertEquals(1, processes.size());
|
||||
/*
|
||||
* As there should only be a reference in the list for the plugin that had the controller
|
||||
* daemon, we expect one here.
|
||||
*/
|
||||
assertThat(processes, hasSize(1));
|
||||
Process process = processes.get(0);
|
||||
try (BufferedReader stdoutReader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
|
||||
final InputStreamReader in =
|
||||
new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8);
|
||||
try (BufferedReader stdoutReader = new BufferedReader(in)) {
|
||||
String line = stdoutReader.readLine();
|
||||
assertEquals("I am alive", line);
|
||||
spawner.close();
|
||||
// Fail if the process doesn't die within 1 second - usually it will be even quicker but it depends on OS scheduling
|
||||
/*
|
||||
* Fail if the process does not die within one second; usually it will be even quicker
|
||||
* but it depends on OS scheduling.
|
||||
*/
|
||||
assertTrue(process.waitFor(1, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
||||
|
||||
public void testControllerSpawnWithIncorrectDescriptor() throws IOException {
|
||||
// this plugin will have a controller daemon
|
||||
Path esHome = createTempDir().resolve("esHome");
|
||||
Settings.Builder settingsBuilder = Settings.builder();
|
||||
settingsBuilder.put(Environment.PATH_HOME_SETTING.getKey(), esHome.toString());
|
||||
Settings settings = settingsBuilder.build();
|
||||
|
||||
Environment environment = new Environment(settings);
|
||||
|
||||
Path plugin = environment.pluginsFile().resolve("test_plugin");
|
||||
Files.createDirectories(plugin);
|
||||
PluginTestUtil.writeProperties(
|
||||
plugin,
|
||||
"description", "test_plugin",
|
||||
"version", Version.CURRENT.toString(),
|
||||
"elasticsearch.version", Version.CURRENT.toString(),
|
||||
"name", "test_plugin",
|
||||
"java.version", "1.8",
|
||||
"classname", "TestPlugin",
|
||||
"has.native.controller", "false");
|
||||
Path controllerProgram = Platforms.nativeControllerPath(plugin);
|
||||
createControllerProgram(controllerProgram);
|
||||
|
||||
Spawner spawner = new Spawner();
|
||||
IllegalArgumentException e = expectThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> spawner.spawnNativePluginControllers(environment));
|
||||
assertThat(
|
||||
e.getMessage(),
|
||||
equalTo("plugin [test_plugin] does not have permission to fork native controller"));
|
||||
}
|
||||
|
||||
private void createControllerProgram(Path outputFile) throws IOException {
|
||||
Path outputDir = outputFile.getParent();
|
||||
Files.createDirectories(outputDir);
|
||||
@ -128,4 +202,5 @@ public class SpawnerNoBootstrapTests extends LuceneTestCase {
|
||||
perms.add(PosixFilePermission.OTHERS_EXECUTE);
|
||||
Files.setPosixFilePermissions(outputFile, perms);
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user