ソースを参照

Merge pull request ESQL-1078 from elastic/main

🤖 ESQL: Merge upstream
elasticsearchmachine 2 年 前
コミット
ebdd5ddf47

+ 1 - 0
server/src/main/java/module-info.java

@@ -296,6 +296,7 @@ module org.elasticsearch.server {
     exports org.elasticsearch.plugins;
     exports org.elasticsearch.plugins.interceptor to org.elasticsearch.security;
     exports org.elasticsearch.plugins.spi;
+    exports org.elasticsearch.plugins.internal to org.elasticsearch.settings.secure;
     exports org.elasticsearch.repositories;
     exports org.elasticsearch.repositories.blobstore;
     exports org.elasticsearch.repositories.fs;

+ 22 - 0
server/src/main/java/org/elasticsearch/node/Node.java

@@ -168,12 +168,14 @@ import org.elasticsearch.plugins.PersistentTaskPlugin;
 import org.elasticsearch.plugins.Plugin;
 import org.elasticsearch.plugins.PluginsService;
 import org.elasticsearch.plugins.RecoveryPlannerPlugin;
+import org.elasticsearch.plugins.ReloadablePlugin;
 import org.elasticsearch.plugins.RepositoryPlugin;
 import org.elasticsearch.plugins.ScriptPlugin;
 import org.elasticsearch.plugins.SearchPlugin;
 import org.elasticsearch.plugins.ShutdownAwarePlugin;
 import org.elasticsearch.plugins.SystemIndexPlugin;
 import org.elasticsearch.plugins.TracerPlugin;
+import org.elasticsearch.plugins.internal.ReloadAwarePlugin;
 import org.elasticsearch.readiness.ReadinessService;
 import org.elasticsearch.repositories.RepositoriesModule;
 import org.elasticsearch.repositories.RepositoriesService;
@@ -1014,6 +1016,9 @@ public class Node implements Closeable {
             HealthInfoCache nodeHealthOverview = HealthInfoCache.create(clusterService);
             HealthApiStats healthApiStats = new HealthApiStats();
 
+            List<ReloadablePlugin> reloadablePlugins = pluginsService.filterPlugins(ReloadablePlugin.class);
+            pluginsService.filterPlugins(ReloadAwarePlugin.class).forEach(p -> p.setReloadCallback(wrapPlugins(reloadablePlugins)));
+
             modules.add(b -> {
                 b.bind(Node.class).toInstance(this);
                 b.bind(NodeService.class).toInstance(nodeService);
@@ -1205,6 +1210,23 @@ public class Node implements Closeable {
         }
     }
 
+    /**
+     * Wrap a group of reloadable plugins into a single reloadable plugin interface
+     * @param reloadablePlugins A list of reloadable plugins
+     * @return A single ReloadablePlugin that, upon reload, reloads the plugins it wraps
+     */
+    private static ReloadablePlugin wrapPlugins(List<ReloadablePlugin> reloadablePlugins) {
+        return settings -> {
+            for (ReloadablePlugin plugin : reloadablePlugins) {
+                try {
+                    plugin.reload(settings);
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        };
+    }
+
     private Tracer getTracer(PluginsService pluginsService, Settings settings) {
         final List<TracerPlugin> tracerPlugins = pluginsService.filterPlugins(TracerPlugin.class);
 

+ 31 - 0
server/src/main/java/org/elasticsearch/plugins/internal/ReloadAwarePlugin.java

@@ -0,0 +1,31 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+package org.elasticsearch.plugins.internal;
+
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.plugins.ReloadablePlugin;
+
+/**
+ * A plugin that may receive a {@link ReloadablePlugin} in order to
+ * call its {@link ReloadablePlugin#reload(Settings)} method.
+ */
+public interface ReloadAwarePlugin {
+
+    /**
+     * Provide a callback for reloading plugins
+     *
+     * <p>This callback is in the form of an implementation of {@link ReloadablePlugin},
+     * but the implementation does not need to be a {@link org.elasticsearch.plugins.Plugin},
+     * or be registered with {@link org.elasticsearch.plugins.PluginsService}.
+     *
+     * @param reloadablePlugin A plugin that this plugin may be able to reload
+     */
+    void setReloadCallback(ReloadablePlugin reloadablePlugin);
+
+}

+ 77 - 0
server/src/test/java/org/elasticsearch/plugins/internal/ReloadAwarePluginTests.java

@@ -0,0 +1,77 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+package org.elasticsearch.plugins.internal;
+
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.node.MockNode;
+import org.elasticsearch.plugins.Plugin;
+import org.elasticsearch.plugins.PluginsService;
+import org.elasticsearch.plugins.ReloadablePlugin;
+import org.elasticsearch.test.ESTestCase;
+import org.elasticsearch.transport.netty4.Netty4Plugin;
+
+import java.util.List;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.instanceOf;
+
+@ESTestCase.WithoutSecurityManager
+public class ReloadAwarePluginTests extends ESTestCase {
+
+    public void testNodeInitializesReloadingPlugin() throws Exception {
+        final Settings settings = Settings.builder().put("path.home", createTempDir()).build();
+        try (
+            MockNode node = new MockNode(settings, List.of(TestReloadablePlugin.class, TestReloadAwarePlugin.class, Netty4Plugin.class));
+        ) {
+            PluginsService pluginsService = node.injector().getInstance(PluginsService.class);
+
+            var reloadAwarePlugins = pluginsService.filterPlugins(ReloadAwarePlugin.class).stream().toList();
+            var reloadablePlugins = pluginsService.filterPlugins(ReloadablePlugin.class).stream().toList();
+
+            assertThat(reloadAwarePlugins.size(), equalTo(1));
+            assertThat(reloadAwarePlugins.get(0), instanceOf(TestReloadAwarePlugin.class));
+            TestReloadAwarePlugin reloadAwarePlugin = (TestReloadAwarePlugin) reloadAwarePlugins.get(0);
+
+            assertThat(reloadablePlugins.size(), equalTo(1));
+            assertThat(reloadablePlugins.get(0), instanceOf(TestReloadablePlugin.class));
+            TestReloadablePlugin reloadablePlugin = (TestReloadablePlugin) reloadablePlugins.get(0);
+
+            assertFalse("Plugin has been reloaded", reloadablePlugin.isReloaded());
+            reloadAwarePlugin.invokeReloadOperation();
+            assertTrue("Plugin has been reloaded", reloadablePlugin.isReloaded());
+        }
+    }
+
+    public static class TestReloadAwarePlugin extends Plugin implements ReloadAwarePlugin {
+        private ReloadablePlugin reloadablePlugin;
+
+        @Override
+        public void setReloadCallback(ReloadablePlugin reloadablePlugin) {
+            this.reloadablePlugin = reloadablePlugin;
+        }
+
+        public void invokeReloadOperation() throws Exception {
+            reloadablePlugin.reload(Settings.EMPTY);
+        }
+    }
+
+    public static class TestReloadablePlugin extends Plugin implements ReloadablePlugin {
+
+        private boolean reloaded = false;
+
+        @Override
+        public void reload(Settings settings) throws Exception {
+            reloaded = true;
+        }
+
+        public boolean isReloaded() {
+            return reloaded;
+        }
+    }
+}