[7.x] Prevent NullPointerException in TransportRolloverAction (#43353) (#43397)

It's possible for the passed in `IndexMetaData` to be null (for
instance, cluster state passed in does not have the index in its
metadata) which in turn can cause a `NullPointerException` when
evaluating the conditions for an index. This commit adds null protection
and unit tests for this case.

Resolves #43296
This commit is contained in:
Lee Hinman 2019-06-19 16:07:28 -06:00 committed by GitHub
parent 77ce3260dd
commit 6b084e55c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 50 additions and 5 deletions

View File

@ -44,6 +44,7 @@ import org.elasticsearch.cluster.metadata.MetaDataCreateIndexService;
import org.elasticsearch.cluster.metadata.MetaDataIndexAliasesService; import org.elasticsearch.cluster.metadata.MetaDataIndexAliasesService;
import org.elasticsearch.cluster.metadata.MetaDataIndexTemplateService; import org.elasticsearch.cluster.metadata.MetaDataIndexTemplateService;
import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.unit.ByteSizeValue;
import org.elasticsearch.index.shard.DocsStats; import org.elasticsearch.index.shard.DocsStats;
@ -55,6 +56,7 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -238,7 +240,11 @@ public class TransportRolloverAction extends TransportMasterNodeAction<RolloverR
} }
static Map<String, Boolean> evaluateConditions(final Collection<Condition<?>> conditions, static Map<String, Boolean> evaluateConditions(final Collection<Condition<?>> conditions,
final DocsStats docsStats, final IndexMetaData metaData) { @Nullable final DocsStats docsStats,
@Nullable final IndexMetaData metaData) {
if (metaData == null) {
return conditions.stream().collect(Collectors.toMap(Condition::toString, cond -> false));
}
final long numDocs = docsStats == null ? 0 : docsStats.getCount(); final long numDocs = docsStats == null ? 0 : docsStats.getCount();
final long indexSize = docsStats == null ? 0 : docsStats.getTotalSizeInBytes(); final long indexSize = docsStats == null ? 0 : docsStats.getTotalSizeInBytes();
final Condition.Stats stats = new Condition.Stats(numDocs, metaData.getCreationDate(), new ByteSizeValue(indexSize)); final Condition.Stats stats = new Condition.Stats(numDocs, metaData.getCreationDate(), new ByteSizeValue(indexSize));
@ -247,9 +253,18 @@ public class TransportRolloverAction extends TransportMasterNodeAction<RolloverR
.collect(Collectors.toMap(result -> result.condition.toString(), result -> result.matched)); .collect(Collectors.toMap(result -> result.condition.toString(), result -> result.matched));
} }
static Map<String, Boolean> evaluateConditions(final Collection<Condition<?>> conditions, final IndexMetaData metaData, static Map<String, Boolean> evaluateConditions(final Collection<Condition<?>> conditions,
final IndicesStatsResponse statsResponse) { @Nullable final IndexMetaData metaData,
return evaluateConditions(conditions, statsResponse.getIndex(metaData.getIndex().getName()).getPrimaries().getDocs(), metaData); @Nullable final IndicesStatsResponse statsResponse) {
if (metaData == null) {
return conditions.stream().collect(Collectors.toMap(Condition::toString, cond -> false));
} else {
final DocsStats docsStats = Optional.ofNullable(statsResponse)
.map(stats -> stats.getIndex(metaData.getIndex().getName()))
.map(indexStats -> indexStats.getPrimaries().getDocs())
.orElse(null);
return evaluateConditions(conditions, docsStats, metaData);
}
} }
static void validate(MetaData metaData, RolloverRequest request) { static void validate(MetaData metaData, RolloverRequest request) {

View File

@ -51,6 +51,7 @@ import org.elasticsearch.common.unit.ByteSizeValue;
import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.index.shard.DocsStats; import org.elasticsearch.index.shard.DocsStats;
import org.elasticsearch.rest.action.cat.RestIndicesActionTests;
import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService; import org.elasticsearch.transport.TransportService;
@ -165,6 +166,35 @@ public class TransportRolloverActionTests extends ESTestCase {
} }
} }
public void testEvaluateWithoutMetaData() {
MaxDocsCondition maxDocsCondition = new MaxDocsCondition(100L);
MaxAgeCondition maxAgeCondition = new MaxAgeCondition(TimeValue.timeValueHours(2));
MaxSizeCondition maxSizeCondition = new MaxSizeCondition(new ByteSizeValue(randomIntBetween(10, 100), ByteSizeUnit.MB));
long matchMaxDocs = randomIntBetween(100, 1000);
final Set<Condition<?>> conditions = Sets.newHashSet(maxDocsCondition, maxAgeCondition, maxSizeCondition);
Map<String, Boolean> results = evaluateConditions(conditions,
new DocsStats(matchMaxDocs, 0L, ByteSizeUnit.MB.toBytes(120)), null);
assertThat(results.size(), equalTo(3));
results.forEach((k, v) -> assertFalse(v));
final Settings settings = Settings.builder()
.put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT)
.put(IndexMetaData.SETTING_INDEX_UUID, UUIDs.randomBase64UUID())
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, randomIntBetween(1, 1000))
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, randomInt(10))
.build();
final IndexMetaData metaData = IndexMetaData.builder(randomAlphaOfLength(10))
.creationDate(System.currentTimeMillis() - TimeValue.timeValueHours(randomIntBetween(5, 10)).getMillis())
.settings(settings)
.build();
IndicesStatsResponse indicesStats = RestIndicesActionTests.randomIndicesStatsResponse(new IndexMetaData[]{metaData});
Map<String, Boolean> results2 = evaluateConditions(conditions, null, indicesStats);
assertThat(results2.size(), equalTo(3));
results2.forEach((k, v) -> assertFalse(v));
}
public void testCreateUpdateAliasRequest() { public void testCreateUpdateAliasRequest() {
String sourceAlias = randomAlphaOfLength(10); String sourceAlias = randomAlphaOfLength(10);
String sourceIndex = randomAlphaOfLength(10); String sourceIndex = randomAlphaOfLength(10);

View File

@ -151,7 +151,7 @@ public class RestIndicesActionTests extends ESTestCase {
} }
} }
private IndicesStatsResponse randomIndicesStatsResponse(final IndexMetaData[] indices) { public static IndicesStatsResponse randomIndicesStatsResponse(final IndexMetaData[] indices) {
List<ShardStats> shardStats = new ArrayList<>(); List<ShardStats> shardStats = new ArrayList<>();
for (final IndexMetaData index : indices) { for (final IndexMetaData index : indices) {
int numShards = randomIntBetween(1, 3); int numShards = randomIntBetween(1, 3);