Skip to content

Commit 0c187e0

Browse files
author
Christoph Büscher
authored
Add migration tool checks for _field_names disabling (#46972)
This change adds a check to the migration tool that warns about the deprecated `enabled` setting for the `_field_names` field on 7.x indices and issues a warning for templates containing this setting, which has been removed with 8.0. Relates to #42854, #46681
1 parent db63e78 commit 0c187e0

File tree

5 files changed

+180
-4
lines changed

5 files changed

+180
-4
lines changed

x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/ClusterDeprecationChecks.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,19 @@
1212
import org.elasticsearch.cluster.metadata.MappingMetaData;
1313
import org.elasticsearch.common.Strings;
1414
import org.elasticsearch.common.unit.TimeValue;
15+
import org.elasticsearch.common.xcontent.XContentHelper;
1516
import org.elasticsearch.index.IndexSettings;
17+
import org.elasticsearch.index.mapper.FieldNamesFieldMapper;
1618
import org.elasticsearch.ingest.IngestService;
1719
import org.elasticsearch.ingest.PipelineConfiguration;
1820
import org.elasticsearch.xpack.core.deprecation.DeprecationIssue;
1921

2022
import java.util.ArrayList;
23+
import java.util.HashSet;
2124
import java.util.List;
2225
import java.util.Map;
2326
import java.util.Objects;
27+
import java.util.Set;
2428
import java.util.concurrent.atomic.AtomicInteger;
2529
import java.util.stream.Collectors;
2630

@@ -91,6 +95,53 @@ static DeprecationIssue checkTemplatesWithTooManyFields(ClusterState state) {
9195
return null;
9296
}
9397

98+
/**
99+
* Check templates that use `_field_names` explicitly, which was deprecated in https://github.com/elastic/elasticsearch/pull/42854
100+
* and will throw an error on new indices in 8.0
101+
*/
102+
@SuppressWarnings("unchecked")
103+
static DeprecationIssue checkTemplatesWithFieldNamesDisabled(ClusterState state) {
104+
Set<String> templatesContainingFieldNames = new HashSet<>();
105+
state.getMetaData().getTemplates().forEach((templateCursor) -> {
106+
String templateName = templateCursor.key;
107+
templateCursor.value.getMappings().forEach((mappingCursor) -> {
108+
String type = mappingCursor.key;
109+
// there should be the type name at this level, but there was a bug where mappings could be stored without a type (#45120)
110+
// to make sure, we try to detect this like we try to do in MappingMetaData#sourceAsMap()
111+
Map<String, Object> mapping = XContentHelper.convertToMap(mappingCursor.value.compressedReference(), true).v2();
112+
if (mapping.size() == 1 && mapping.containsKey(type)) {
113+
// the type name is the root value, reduce it
114+
mapping = (Map<String, Object>) mapping.get(type);
115+
}
116+
if (mapContainsFieldNamesDisabled(mapping)) {
117+
templatesContainingFieldNames.add(templateName);
118+
}
119+
});
120+
});
121+
122+
if (templatesContainingFieldNames.isEmpty() == false) {
123+
return new DeprecationIssue(DeprecationIssue.Level.WARNING, "Index templates contain _field_names settings.",
124+
"https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-8.0.html#fieldnames-enabling",
125+
"Index templates " + templatesContainingFieldNames + " use the deprecated `enable` setting for the `"
126+
+ FieldNamesFieldMapper.NAME + "` field. Using this setting in new index mappings will throw an error "
127+
+ "in the next major version and needs to be removed from existing mappings and templates.");
128+
}
129+
return null;
130+
}
131+
132+
/**
133+
* check for "_field_names" entries in the map that contain another property "enabled" in the sub-map
134+
*/
135+
static boolean mapContainsFieldNamesDisabled(Map<?, ?> map) {
136+
Object fieldNamesMapping = map.get(FieldNamesFieldMapper.NAME);
137+
if (fieldNamesMapping != null) {
138+
if (((Map<?, ?>) fieldNamesMapping).keySet().contains("enabled")) {
139+
return true;
140+
}
141+
}
142+
return false;
143+
}
144+
94145
static DeprecationIssue checkPollIntervalTooLow(ClusterState state) {
95146
String pollIntervalString = state.metaData().settings().get(LIFECYCLE_POLL_INTERVAL_SETTING.getKey());
96147
if (Strings.isNullOrEmpty(pollIntervalString)) {

x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationChecks.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ private DeprecationChecks() {
3535
Collections.unmodifiableList(Arrays.asList(
3636
ClusterDeprecationChecks::checkUserAgentPipelines,
3737
ClusterDeprecationChecks::checkTemplatesWithTooManyFields,
38-
ClusterDeprecationChecks::checkPollIntervalTooLow
38+
ClusterDeprecationChecks::checkPollIntervalTooLow,
39+
ClusterDeprecationChecks::checkTemplatesWithFieldNamesDisabled
3940
));
4041

4142

@@ -51,7 +52,8 @@ private DeprecationChecks() {
5152
IndexDeprecationChecks::tooManyFieldsCheck,
5253
IndexDeprecationChecks::chainedMultiFieldsCheck,
5354
IndexDeprecationChecks::deprecatedDateTimeFormat,
54-
IndexDeprecationChecks::translogRetentionSettingCheck
55+
IndexDeprecationChecks::translogRetentionSettingCheck,
56+
IndexDeprecationChecks::fieldNamesDisabledCheck
5557
));
5658

5759
static List<BiFunction<DatafeedConfig, NamedXContentRegistry, DeprecationIssue>> ML_SETTINGS_CHECKS =

x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecks.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88

99
import com.carrotsearch.hppc.cursors.ObjectCursor;
10+
1011
import org.elasticsearch.Version;
1112
import org.elasticsearch.cluster.metadata.IndexMetaData;
1213
import org.elasticsearch.cluster.metadata.MappingMetaData;
@@ -185,6 +186,21 @@ private static boolean containsChainedMultiFields(Map<?, ?> property) {
185186
return false;
186187
}
187188

189+
/**
190+
* warn about existing explicit "_field_names" settings in existing mappings
191+
*/
192+
static DeprecationIssue fieldNamesDisabledCheck(IndexMetaData indexMetaData) {
193+
MappingMetaData mapping = indexMetaData.mapping();
194+
if ((mapping != null) && ClusterDeprecationChecks.mapContainsFieldNamesDisabled(mapping.getSourceAsMap())) {
195+
return new DeprecationIssue(DeprecationIssue.Level.WARNING,
196+
"Index mapping contains explicit `_field_names` enabling settings.",
197+
"https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-8.0.html" +
198+
"#fieldnames-enabling",
199+
"The index mapping contains a deprecated `enabled` setting for `_field_names` that should be removed moving foward.");
200+
}
201+
return null;
202+
}
203+
188204
private static final Set<String> TYPES_THAT_DONT_COUNT;
189205
static {
190206
HashSet<String> typesThatDontCount = new HashSet<>();

x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/ClusterDeprecationChecksTests.java

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.elasticsearch.common.xcontent.XContentBuilder;
1717
import org.elasticsearch.common.xcontent.XContentType;
1818
import org.elasticsearch.index.IndexSettings;
19+
import org.elasticsearch.index.mapper.FieldNamesFieldMapper;
1920
import org.elasticsearch.ingest.IngestService;
2021
import org.elasticsearch.test.ESTestCase;
2122
import org.elasticsearch.xpack.core.deprecation.DeprecationIssue;
@@ -158,6 +159,84 @@ public void testTemplateWithTooManyFields() throws IOException {
158159
assertEquals(singletonList(expected), issues);
159160
}
160161

162+
public void testTemplatesWithFieldNamesDisabled() throws IOException {
163+
XContentBuilder goodMappingBuilder = jsonBuilder();
164+
goodMappingBuilder.startObject();
165+
{
166+
goodMappingBuilder.startObject("_doc");
167+
{
168+
goodMappingBuilder.startObject("properties");
169+
{
170+
addRandomFields(10, goodMappingBuilder);
171+
}
172+
goodMappingBuilder.endObject();
173+
}
174+
goodMappingBuilder.endObject();
175+
}
176+
goodMappingBuilder.endObject();
177+
assertFieldNamesEnabledTemplate(goodMappingBuilder, false);
178+
179+
XContentBuilder badMappingBuilder = jsonBuilder();
180+
badMappingBuilder.startObject();
181+
{
182+
// we currently always store a type level internally
183+
badMappingBuilder.startObject("_doc");
184+
{
185+
badMappingBuilder.startObject(FieldNamesFieldMapper.NAME);
186+
{
187+
badMappingBuilder.field("enabled", randomBoolean());
188+
}
189+
badMappingBuilder.endObject();
190+
}
191+
badMappingBuilder.endObject();
192+
}
193+
badMappingBuilder.endObject();
194+
assertFieldNamesEnabledTemplate(badMappingBuilder, true);
195+
196+
// however, there was a bug where mappings could be stored without a type (#45120)
197+
// so we also should try to check these cases
198+
199+
XContentBuilder badMappingWithoutTypeBuilder = jsonBuilder();
200+
badMappingWithoutTypeBuilder.startObject();
201+
{
202+
badMappingWithoutTypeBuilder.startObject(FieldNamesFieldMapper.NAME);
203+
{
204+
badMappingWithoutTypeBuilder.field("enabled", randomBoolean());
205+
}
206+
badMappingWithoutTypeBuilder.endObject();
207+
}
208+
badMappingWithoutTypeBuilder.endObject();
209+
assertFieldNamesEnabledTemplate(badMappingWithoutTypeBuilder, true);
210+
}
211+
212+
private void assertFieldNamesEnabledTemplate(XContentBuilder templateBuilder, boolean expectIssue) throws IOException {
213+
String badTemplateName = randomAlphaOfLength(5);
214+
final ClusterState state = ClusterState.builder(new ClusterName(randomAlphaOfLength(5)))
215+
.metaData(MetaData.builder()
216+
.put(IndexTemplateMetaData.builder(badTemplateName)
217+
.patterns(Collections.singletonList(randomAlphaOfLength(5)))
218+
.putMapping("_doc", Strings.toString(templateBuilder))
219+
.build())
220+
.build())
221+
.build();
222+
223+
List<DeprecationIssue> issues = DeprecationChecks.filterChecks(CLUSTER_SETTINGS_CHECKS, c -> c.apply(state));
224+
if (expectIssue) {
225+
assertEquals(1, issues.size());
226+
DeprecationIssue issue = issues.get(0);
227+
assertEquals(DeprecationIssue.Level.WARNING, issue.getLevel());
228+
assertEquals("https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-8.0.html#fieldnames-enabling"
229+
, issue.getUrl());
230+
assertEquals("Index templates contain _field_names settings.", issue.getMessage());
231+
assertEquals("Index templates [" + badTemplateName + "] "
232+
+ "use the deprecated `enable` setting for the `" + FieldNamesFieldMapper.NAME +
233+
"` field. Using this setting in new index mappings will throw an error in the next major version and " +
234+
"needs to be removed from existing mappings and templates.", issue.getDetails());
235+
} else {
236+
assertTrue(issues.isEmpty());
237+
}
238+
}
239+
161240
public void testPollIntervalTooLow() {
162241
{
163242
final String tooLowInterval = randomTimeValue(1, 999, "ms", "micros", "nanos");

x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/IndexDeprecationChecksTests.java

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@
1111
import org.elasticsearch.common.Strings;
1212
import org.elasticsearch.common.bytes.BytesReference;
1313
import org.elasticsearch.common.joda.JodaDeprecationPatterns;
14+
import org.elasticsearch.common.settings.Settings;
1415
import org.elasticsearch.common.xcontent.XContentBuilder;
1516
import org.elasticsearch.common.xcontent.XContentFactory;
1617
import org.elasticsearch.index.IndexSettings;
17-
import org.elasticsearch.common.settings.Settings;
18+
import org.elasticsearch.index.mapper.FieldNamesFieldMapper;
1819
import org.elasticsearch.test.ESTestCase;
1920
import org.elasticsearch.test.VersionUtils;
2021
import org.elasticsearch.xpack.core.deprecation.DeprecationIssue;
@@ -27,8 +28,8 @@
2728
import static java.util.Collections.singletonList;
2829
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
2930
import static org.elasticsearch.xpack.deprecation.DeprecationChecks.INDEX_SETTINGS_CHECKS;
30-
import static org.hamcrest.Matchers.hasItem;
3131
import static org.hamcrest.Matchers.empty;
32+
import static org.hamcrest.Matchers.hasItem;
3233
import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
3334

3435
public class IndexDeprecationChecksTests extends ESTestCase {
@@ -161,6 +162,7 @@ public void testChainedMultiFields() throws IOException {
161162
"The names of fields that contain chained multi-fields: [[type: _doc, field: invalid-field]]");
162163
assertEquals(singletonList(expected), issues);
163164
}
165+
164166
public void testDefinedPatternsDoNotWarn() throws IOException {
165167
String simpleMapping = "{\n" +
166168
"\"properties\" : {\n" +
@@ -412,4 +414,30 @@ public void testDefaultTranslogRetentionSettings() {
412414
List<DeprecationIssue> issues = DeprecationChecks.filterChecks(INDEX_SETTINGS_CHECKS, c -> c.apply(indexMetaData));
413415
assertThat(issues, empty());
414416
}
417+
418+
public void testFieldNamesEnabling() throws IOException {
419+
XContentBuilder xContent = XContentFactory.jsonBuilder().startObject()
420+
.startObject(FieldNamesFieldMapper.NAME)
421+
.field("enabled", randomBoolean())
422+
.endObject()
423+
.endObject();
424+
String mapping = BytesReference.bytes(xContent).utf8ToString();
425+
426+
IndexMetaData simpleIndex = IndexMetaData.builder(randomAlphaOfLengthBetween(5, 10))
427+
.settings(settings(
428+
VersionUtils.randomVersionBetween(random(), Version.V_7_0_0, Version.CURRENT)))
429+
.numberOfShards(1)
430+
.numberOfReplicas(0)
431+
.putMapping("_doc", mapping).build();
432+
List<DeprecationIssue> issues = DeprecationChecks.filterChecks(INDEX_SETTINGS_CHECKS, c -> c.apply(simpleIndex));
433+
assertEquals(1, issues.size());
434+
435+
DeprecationIssue issue = issues.get(0);
436+
assertEquals(DeprecationIssue.Level.WARNING, issue.getLevel());
437+
assertEquals("https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-8.0.html#fieldnames-enabling"
438+
, issue.getUrl());
439+
assertEquals("Index mapping contains explicit `_field_names` enabling settings.", issue.getMessage());
440+
assertEquals("The index mapping contains a deprecated `enabled` setting for `_field_names` that should be removed moving foward.",
441+
issue.getDetails());
442+
}
415443
}

0 commit comments

Comments
 (0)