From e943e279541c8b9fd628a6b0b6535bdb126d794b Mon Sep 17 00:00:00 2001 From: David Roberts Date: Wed, 25 Sep 2019 12:04:48 +0100 Subject: [PATCH] Spawn controller processes from a different directory on macOS (#47013) This is the Java side of https://github.com/elastic/ml-cpp/pull/593 with a fallback so that ml-cpp bundles with either the new or old directory structure work for the time being. A few days after merging the C++ changes a followup to this change will be made that removes the fallback. --- distribution/build.gradle | 3 +- .../plugins/InstallPluginCommand.java | 6 ++- .../bootstrap/SpawnerNoBootstrapTests.java | 2 +- .../org/elasticsearch/bootstrap/Spawner.java | 8 +++- .../org/elasticsearch/plugins/Platforms.java | 30 +++++++++++- .../elasticsearch/plugins/PlatformsTests.java | 47 +++++++++++++++++++ 6 files changed, 90 insertions(+), 6 deletions(-) create mode 100644 server/src/test/java/org/elasticsearch/plugins/PlatformsTests.java diff --git a/distribution/build.gradle b/distribution/build.gradle index 8026f596bfa..f83c38e5955 100644 --- a/distribution/build.gradle +++ b/distribution/build.gradle @@ -277,8 +277,9 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { modulesFiles = { oss, platform -> copySpec { eachFile { - if (it.relativePath.segments[-2] == 'bin') { + if (it.relativePath.segments[-2] == 'bin' || (platform == 'darwin' && it.relativePath.segments[-2] == 'MacOS')) { // bin files, wherever they are within modules (eg platform specific) should be executable + // and MacOS is an alternative to bin on macOS it.mode = 0755 } else { it.mode = 0644 diff --git a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java index bf478965592..9b23328e500 100644 --- a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java +++ b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java @@ -23,6 +23,7 @@ import joptsimple.OptionSet; import joptsimple.OptionSpec; import org.apache.lucene.search.spell.LevenshteinDistance; import org.apache.lucene.util.CollectionUtil; +import org.apache.lucene.util.Constants; import org.bouncycastle.bcpg.ArmoredInputStream; import org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider; import org.bouncycastle.openpgp.PGPException; @@ -842,7 +843,10 @@ class InstallPluginCommand extends EnvironmentAwareCommand { Files.walkFileTree(destination, new SimpleFileVisitor() { @Override public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException { - if ("bin".equals(file.getParent().getFileName().toString())) { + final String parentDirName = file.getParent().getFileName().toString(); + if ("bin".equals(parentDirName) + // "MacOS" is an alternative to "bin" on macOS + || (Constants.MAC_OS_X && "MacOS".equals(parentDirName))) { setFileAttributes(file, BIN_FILES_PERMS); } else { setFileAttributes(file, PLUGIN_FILES_PERMS); diff --git a/qa/no-bootstrap-tests/src/test/java/org/elasticsearch/bootstrap/SpawnerNoBootstrapTests.java b/qa/no-bootstrap-tests/src/test/java/org/elasticsearch/bootstrap/SpawnerNoBootstrapTests.java index edc10bdec3a..00ad1b7e9b2 100644 --- a/qa/no-bootstrap-tests/src/test/java/org/elasticsearch/bootstrap/SpawnerNoBootstrapTests.java +++ b/qa/no-bootstrap-tests/src/test/java/org/elasticsearch/bootstrap/SpawnerNoBootstrapTests.java @@ -68,7 +68,7 @@ public class SpawnerNoBootstrapTests extends LuceneTestCase { /** * Simplest case: a module with no controller daemon. */ - public void testNoControllerSpawn() throws IOException, InterruptedException { + public void testNoControllerSpawn() throws IOException { Path esHome = createTempDir().resolve("esHome"); Settings.Builder settingsBuilder = Settings.builder(); settingsBuilder.put(Environment.PATH_HOME_SETTING.getKey(), esHome.toString()); diff --git a/server/src/main/java/org/elasticsearch/bootstrap/Spawner.java b/server/src/main/java/org/elasticsearch/bootstrap/Spawner.java index f1c6c36dc5c..8337500f73a 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/Spawner.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/Spawner.java @@ -72,9 +72,13 @@ final class Spawner implements Closeable { List paths = PluginsService.findPluginDirs(environment.modulesFile()); for (final Path modules : paths) { final PluginInfo info = PluginInfo.readFromProperties(modules); - final Path spawnPath = Platforms.nativeControllerPath(modules); + Path spawnPath = Platforms.nativeControllerPath(modules); if (!Files.isRegularFile(spawnPath)) { - continue; + // TODO: remove before release and just continue if the controller is not in the standard place + spawnPath = Platforms.fallbackNativeControllerPath(modules); + if (spawnPath == null || Files.isRegularFile(spawnPath) == false) { + continue; + } } if (!info.hasNativeController()) { final String message = String.format( diff --git a/server/src/main/java/org/elasticsearch/plugins/Platforms.java b/server/src/main/java/org/elasticsearch/plugins/Platforms.java index 91af58ebec4..8a374701b6e 100644 --- a/server/src/main/java/org/elasticsearch/plugins/Platforms.java +++ b/server/src/main/java/org/elasticsearch/plugins/Platforms.java @@ -38,6 +38,15 @@ public class Platforms { * The path to the native controller for a plugin with native components. */ public static Path nativeControllerPath(Path plugin) { + if (Constants.MAC_OS_X) { + return plugin + .resolve("platform") + .resolve(PLATFORM_NAME) + .resolve(PROGRAM_NAME + ".app") + .resolve("Contents") + .resolve("MacOS") + .resolve(PROGRAM_NAME); + } return plugin .resolve("platform") .resolve(PLATFORM_NAME) @@ -46,7 +55,26 @@ public class Platforms { } /** - * Return the platform name based on the OS name and + * The fallback path to the native controller for a plugin with native + * components to be used if no program is found using the standard path. + * This is a temporary measure to allow developers not working on this + * functionality to continue to work with C++ bundles from before or + * after the change. This code should never be in a supported release. + * TODO: remove this method before release + */ + public static Path fallbackNativeControllerPath(Path plugin) { + if (Constants.MAC_OS_X) { + return plugin + .resolve("platform") + .resolve(PLATFORM_NAME) + .resolve("bin") + .resolve(PROGRAM_NAME); + } + return null; + } + + /** + * Return the platform name based on the OS name and architecture, for example: * - darwin-x86_64 * - linux-x86-64 * - windows-x86_64 diff --git a/server/src/test/java/org/elasticsearch/plugins/PlatformsTests.java b/server/src/test/java/org/elasticsearch/plugins/PlatformsTests.java new file mode 100644 index 00000000000..b23475a8c4c --- /dev/null +++ b/server/src/test/java/org/elasticsearch/plugins/PlatformsTests.java @@ -0,0 +1,47 @@ +/* + * 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 org.elasticsearch.test.ESTestCase; + +import java.nio.file.Path; + +public class PlatformsTests extends ESTestCase { + + public void testNativeControllerPath() { + + final Path nativeControllerPath = Platforms.nativeControllerPath(createTempDir()); + + // The directory structure on macOS must match Apple's .app + // structure or Gatekeeper may refuse to run the program + if (Constants.MAC_OS_X) { + String programName = nativeControllerPath.getFileName().toString(); + Path binDirectory = nativeControllerPath.getParent(); + assertEquals("MacOS", binDirectory.getFileName().toString()); + Path contentsDirectory = binDirectory.getParent(); + assertEquals("Contents", contentsDirectory.getFileName().toString()); + Path appDirectory = contentsDirectory.getParent(); + assertEquals(programName + ".app", appDirectory.getFileName().toString()); + } else { + Path binDirectory = nativeControllerPath.getParent(); + assertEquals("bin", binDirectory.getFileName().toString()); + } + } +}