Skip to content

Commit cacba6b

Browse files
authored
Allow plugins to upgrade templates and index metadata on startup (#24379)
The UpgraderPlugin adds two additional extension points called during cluster upgrade and when old indices are introduced into the cluster state during initial recovery, restore from a snapshot or as a dangling index. One extension point allows plugin to update old templates and another extension points allows to update/check index metadata.
1 parent 855b64b commit cacba6b

File tree

8 files changed

+273
-40
lines changed

8 files changed

+273
-40
lines changed

core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexUpgradeService.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,14 @@
3535
import org.elasticsearch.index.mapper.MapperService;
3636
import org.elasticsearch.index.similarity.SimilarityService;
3737
import org.elasticsearch.indices.mapper.MapperRegistry;
38+
import org.elasticsearch.plugins.Plugin;
3839

3940
import java.util.AbstractMap;
41+
import java.util.Collection;
4042
import java.util.Collections;
4143
import java.util.Map;
4244
import java.util.Set;
45+
import java.util.function.UnaryOperator;
4346

4447
/**
4548
* This service is responsible for upgrading legacy index metadata to the current version
@@ -54,14 +57,23 @@ public class MetaDataIndexUpgradeService extends AbstractComponent {
5457
private final NamedXContentRegistry xContentRegistry;
5558
private final MapperRegistry mapperRegistry;
5659
private final IndexScopedSettings indexScopedSettings;
60+
private final UnaryOperator<IndexMetaData> upgraders;
5761

5862
@Inject
5963
public MetaDataIndexUpgradeService(Settings settings, NamedXContentRegistry xContentRegistry, MapperRegistry mapperRegistry,
60-
IndexScopedSettings indexScopedSettings) {
64+
IndexScopedSettings indexScopedSettings,
65+
Collection<UnaryOperator<IndexMetaData>> indexMetaDataUpgraders) {
6166
super(settings);
6267
this.xContentRegistry = xContentRegistry;
6368
this.mapperRegistry = mapperRegistry;
6469
this.indexScopedSettings = indexScopedSettings;
70+
this.upgraders = indexMetaData -> {
71+
IndexMetaData newIndexMetaData = indexMetaData;
72+
for (UnaryOperator<IndexMetaData> upgrader : indexMetaDataUpgraders) {
73+
newIndexMetaData = upgrader.apply(newIndexMetaData);
74+
}
75+
return newIndexMetaData;
76+
};
6577
}
6678

6779
/**
@@ -84,6 +96,8 @@ public IndexMetaData upgradeIndexMetaData(IndexMetaData indexMetaData, Version m
8496
newMetaData = archiveBrokenIndexSettings(newMetaData);
8597
// only run the check with the upgraded settings!!
8698
checkMappingsCompatibility(newMetaData);
99+
// apply plugin checks
100+
newMetaData = upgraders.apply(newMetaData);
87101
return markAsUpgraded(newMetaData);
88102
}
89103

core/src/main/java/org/elasticsearch/gateway/GatewayMetaState.java

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.elasticsearch.cluster.routing.RoutingNode;
3232
import org.elasticsearch.cluster.routing.ShardRouting;
3333
import org.elasticsearch.common.Nullable;
34+
import org.elasticsearch.common.collect.ImmutableOpenMap;
3435
import org.elasticsearch.common.component.AbstractComponent;
3536
import org.elasticsearch.common.inject.Inject;
3637
import org.elasticsearch.common.settings.Settings;
@@ -50,6 +51,9 @@
5051
import java.util.List;
5152
import java.util.Map;
5253
import java.util.Set;
54+
import java.util.function.BiConsumer;
55+
import java.util.function.Consumer;
56+
import java.util.function.UnaryOperator;
5357

5458
import static java.util.Collections.emptySet;
5559
import static java.util.Collections.unmodifiableSet;
@@ -219,8 +223,8 @@ private void ensureNoPre019State() throws Exception {
219223
final String name = stateFile.getFileName().toString();
220224
if (name.startsWith("metadata-")) {
221225
throw new IllegalStateException("Detected pre 0.19 metadata file please upgrade to a version before "
222-
+ Version.CURRENT.minimumCompatibilityVersion()
223-
+ " first to upgrade state structures - metadata found: [" + stateFile.getParent().toAbsolutePath());
226+
+ Version.CURRENT.minimumCompatibilityVersion()
227+
+ " first to upgrade state structures - metadata found: [" + stateFile.getParent().toAbsolutePath());
224228
}
225229
}
226230
}
@@ -247,23 +251,41 @@ static MetaData upgradeMetaData(MetaData metaData,
247251
changed |= indexMetaData != newMetaData;
248252
upgradedMetaData.put(newMetaData, false);
249253
}
250-
// collect current customs
251-
Map<String, MetaData.Custom> existingCustoms = new HashMap<>();
252-
for (ObjectObjectCursor<String, MetaData.Custom> customCursor : metaData.customs()) {
253-
existingCustoms.put(customCursor.key, customCursor.value);
254-
}
255254
// upgrade global custom meta data
256-
Map<String, MetaData.Custom> upgradedCustoms = metaDataUpgrader.customMetaDataUpgraders.apply(existingCustoms);
257-
if (upgradedCustoms.equals(existingCustoms) == false) {
258-
existingCustoms.keySet().forEach(upgradedMetaData::removeCustom);
259-
for (Map.Entry<String, MetaData.Custom> upgradedCustomEntry : upgradedCustoms.entrySet()) {
260-
upgradedMetaData.putCustom(upgradedCustomEntry.getKey(), upgradedCustomEntry.getValue());
261-
}
255+
if (applyPluginUpgraders(metaData.getCustoms(), metaDataUpgrader.customMetaDataUpgraders,
256+
upgradedMetaData::removeCustom,upgradedMetaData::putCustom)) {
257+
changed = true;
258+
}
259+
// upgrade current templates
260+
if (applyPluginUpgraders(metaData.getTemplates(), metaDataUpgrader.indexTemplateMetaDataUpgraders,
261+
upgradedMetaData::removeTemplate, (s, indexTemplateMetaData) -> upgradedMetaData.put(indexTemplateMetaData))) {
262262
changed = true;
263263
}
264264
return changed ? upgradedMetaData.build() : metaData;
265265
}
266266

267+
private static <Data> boolean applyPluginUpgraders(ImmutableOpenMap<String, Data> existingData,
268+
UnaryOperator<Map<String, Data>> upgrader,
269+
Consumer<String> removeData,
270+
BiConsumer<String, Data> putData) {
271+
// collect current data
272+
Map<String, Data> existingMap = new HashMap<>();
273+
for (ObjectObjectCursor<String, Data> customCursor : existingData) {
274+
existingMap.put(customCursor.key, customCursor.value);
275+
}
276+
// upgrade global custom meta data
277+
Map<String, Data> upgradedCustoms = upgrader.apply(existingMap);
278+
if (upgradedCustoms.equals(existingMap) == false) {
279+
// remove all data first so a plugin can remove custom metadata or templates if needed
280+
existingMap.keySet().forEach(removeData);
281+
for (Map.Entry<String, Data> upgradedCustomEntry : upgradedCustoms.entrySet()) {
282+
putData.accept(upgradedCustomEntry.getKey(), upgradedCustomEntry.getValue());
283+
}
284+
return true;
285+
}
286+
return false;
287+
}
288+
267289
// shard state BWC
268290
private void ensureNoPre019ShardState(NodeEnvironment nodeEnv) throws Exception {
269291
for (Path dataLocation : nodeEnv.nodeDataPaths()) {

core/src/main/java/org/elasticsearch/node/Node.java

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343
import org.elasticsearch.cluster.InternalClusterInfoService;
4444
import org.elasticsearch.cluster.NodeConnectionsService;
4545
import org.elasticsearch.cluster.action.index.MappingUpdatedAction;
46+
import org.elasticsearch.cluster.metadata.IndexMetaData;
47+
import org.elasticsearch.cluster.metadata.IndexTemplateMetaData;
4648
import org.elasticsearch.cluster.metadata.MetaData;
4749
import org.elasticsearch.cluster.metadata.MetaDataIndexUpgradeService;
4850
import org.elasticsearch.cluster.node.DiscoveryNode;
@@ -400,14 +402,20 @@ protected Node(final Environment environment, Collection<Class<? extends Plugin>
400402
.flatMap(p -> p.createComponents(client, clusterService, threadPool, resourceWatcherService,
401403
scriptModule.getScriptService(), xContentRegistry).stream())
402404
.collect(Collectors.toList());
403-
Collection<UnaryOperator<Map<String, MetaData.Custom>>> customMetaDataUpgraders =
404-
pluginsService.filterPlugins(Plugin.class).stream()
405-
.map(Plugin::getCustomMetaDataUpgrader)
406-
.collect(Collectors.toList());
407405
final RestController restController = actionModule.getRestController();
408406
final NetworkModule networkModule = new NetworkModule(settings, false, pluginsService.filterPlugins(NetworkPlugin.class),
409407
threadPool, bigArrays, circuitBreakerService, namedWriteableRegistry, xContentRegistry, networkService, restController);
410-
final MetaDataUpgrader metaDataUpgrader = new MetaDataUpgrader(customMetaDataUpgraders);
408+
Collection<UnaryOperator<Map<String, MetaData.Custom>>> customMetaDataUpgraders =
409+
pluginsService.filterPlugins(Plugin.class).stream()
410+
.map(Plugin::getCustomMetaDataUpgrader)
411+
.collect(Collectors.toList());
412+
Collection<UnaryOperator<Map<String, IndexTemplateMetaData>>> indexTemplateMetaDataUpgraders =
413+
pluginsService.filterPlugins(Plugin.class).stream()
414+
.map(Plugin::getIndexTemplateMetaDataUpgrader)
415+
.collect(Collectors.toList());
416+
Collection<UnaryOperator<IndexMetaData>> indexMetaDataUpgraders = pluginsService.filterPlugins(Plugin.class).stream()
417+
.map(Plugin::getIndexMetaDataUpgrader).collect(Collectors.toList());
418+
final MetaDataUpgrader metaDataUpgrader = new MetaDataUpgrader(customMetaDataUpgraders, indexTemplateMetaDataUpgraders);
411419
final Transport transport = networkModule.getTransportSupplier().get();
412420
final TransportService transportService = newTransportService(settings, transport, threadPool,
413421
networkModule.getTransportInterceptor(), localNodeFactory, settingsModule.getClusterSettings());
@@ -462,8 +470,8 @@ protected Node(final Environment environment, Collection<Class<? extends Plugin>
462470
b.bind(TransportService.class).toInstance(transportService);
463471
b.bind(NetworkService.class).toInstance(networkService);
464472
b.bind(UpdateHelper.class).toInstance(new UpdateHelper(settings, scriptModule.getScriptService()));
465-
b.bind(MetaDataIndexUpgradeService.class).toInstance(new MetaDataIndexUpgradeService(settings,
466-
xContentRegistry, indicesModule.getMapperRegistry(), settingsModule.getIndexScopedSettings()));
473+
b.bind(MetaDataIndexUpgradeService.class).toInstance(new MetaDataIndexUpgradeService(settings, xContentRegistry,
474+
indicesModule.getMapperRegistry(), settingsModule.getIndexScopedSettings(), indexMetaDataUpgraders));
467475
b.bind(ClusterInfoService.class).toInstance(clusterInfoService);
468476
b.bind(Discovery.class).toInstance(discoveryModule.getDiscovery());
469477
{

core/src/main/java/org/elasticsearch/plugins/MetaDataUpgrader.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
package org.elasticsearch.plugins;
2121

22+
import org.elasticsearch.cluster.metadata.IndexTemplateMetaData;
2223
import org.elasticsearch.cluster.metadata.MetaData;
2324

2425
import java.util.Collection;
@@ -32,13 +33,24 @@
3233
public class MetaDataUpgrader {
3334
public final UnaryOperator<Map<String, MetaData.Custom>> customMetaDataUpgraders;
3435

35-
public MetaDataUpgrader(Collection<UnaryOperator<Map<String, MetaData.Custom>>> customMetaDataUpgraders) {
36+
public final UnaryOperator<Map<String, IndexTemplateMetaData>> indexTemplateMetaDataUpgraders;
37+
38+
public MetaDataUpgrader(Collection<UnaryOperator<Map<String, MetaData.Custom>>> customMetaDataUpgraders,
39+
Collection<UnaryOperator<Map<String, IndexTemplateMetaData>>> indexTemplateMetaDataUpgraders) {
3640
this.customMetaDataUpgraders = customs -> {
3741
Map<String, MetaData.Custom> upgradedCustoms = new HashMap<>(customs);
3842
for (UnaryOperator<Map<String, MetaData.Custom>> customMetaDataUpgrader : customMetaDataUpgraders) {
3943
upgradedCustoms = customMetaDataUpgrader.apply(upgradedCustoms);
4044
}
4145
return upgradedCustoms;
4246
};
47+
48+
this.indexTemplateMetaDataUpgraders = templates -> {
49+
Map<String, IndexTemplateMetaData> upgradedTemplates = new HashMap<>(templates);
50+
for (UnaryOperator<Map<String, IndexTemplateMetaData>> upgrader : indexTemplateMetaDataUpgraders) {
51+
upgradedTemplates = upgrader.apply(upgradedTemplates);
52+
}
53+
return upgradedTemplates;
54+
};
4355
}
4456
}

core/src/main/java/org/elasticsearch/plugins/Plugin.java

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
import org.elasticsearch.bootstrap.BootstrapCheck;
2424
import org.elasticsearch.client.Client;
2525
import org.elasticsearch.cluster.ClusterModule;
26+
import org.elasticsearch.cluster.metadata.IndexMetaData;
27+
import org.elasticsearch.cluster.metadata.IndexTemplateMetaData;
2628
import org.elasticsearch.cluster.metadata.MetaData;
2729
import org.elasticsearch.cluster.service.ClusterService;
2830
import org.elasticsearch.common.component.LifecycleComponent;
@@ -153,14 +155,49 @@ public void onIndexModule(IndexModule indexModule) {}
153155
* Provides a function to modify global custom meta data on startup.
154156
* <p>
155157
* Plugins should return the input custom map via {@link UnaryOperator#identity()} if no upgrade is required.
158+
* <p>
159+
* The order of custom meta data upgraders calls is undefined and can change between runs so, it is expected that
160+
* plugins will modify only data owned by them to avoid conflicts.
161+
* <p>
156162
* @return Never {@code null}. The same or upgraded {@code MetaData.Custom} map.
157163
* @throws IllegalStateException if the node should not start because at least one {@code MetaData.Custom}
158-
* is unsupported
164+
* is unsupported
159165
*/
160166
public UnaryOperator<Map<String, MetaData.Custom>> getCustomMetaDataUpgrader() {
161167
return UnaryOperator.identity();
162168
}
163169

170+
/**
171+
* Provides a function to modify index template meta data on startup.
172+
* <p>
173+
* Plugins should return the input template map via {@link UnaryOperator#identity()} if no upgrade is required.
174+
* <p>
175+
* The order of the template upgrader calls is undefined and can change between runs so, it is expected that
176+
* plugins will modify only templates owned by them to avoid conflicts.
177+
* <p>
178+
* @return Never {@code null}. The same or upgraded {@code IndexTemplateMetaData} map.
179+
* @throws IllegalStateException if the node should not start because at least one {@code IndexTemplateMetaData}
180+
* cannot be upgraded
181+
*/
182+
public UnaryOperator<Map<String, IndexTemplateMetaData>> getIndexTemplateMetaDataUpgrader() {
183+
return UnaryOperator.identity();
184+
}
185+
186+
/**
187+
* Provides a function to modify index meta data when an index is introduced into the cluster state for the first time.
188+
* <p>
189+
* Plugins should return the input index metadata via {@link UnaryOperator#identity()} if no upgrade is required.
190+
* <p>
191+
* The order of the index upgrader calls for the same index is undefined and can change between runs so, it is expected that
192+
* plugins will modify only indices owned by them to avoid conflicts.
193+
* <p>
194+
* @return Never {@code null}. The same or upgraded {@code IndexMetaData}.
195+
* @throws IllegalStateException if the node should not start because the index is unsupported
196+
*/
197+
public UnaryOperator<IndexMetaData> getIndexMetaDataUpgrader() {
198+
return UnaryOperator.identity();
199+
}
200+
164201
/**
165202
* Provides the list of this plugin's custom thread pools, empty if
166203
* none.

0 commit comments

Comments
 (0)