/**
* This file is part of Graylog.
*
* Graylog is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Graylog is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Graylog. If not, see <http://www.gnu.org/licenses/>.
*/
package org.graylog2.indexer.ranges;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.eventbus.EventBus;
import com.lordofthejars.nosqlunit.annotation.UsingDataSet;
import com.lordofthejars.nosqlunit.core.LoadStrategyEnum;
import com.lordofthejars.nosqlunit.mongodb.InMemoryMongoDb;
import org.assertj.jodatime.api.Assertions;
import org.bson.types.ObjectId;
import org.graylog2.audit.NullAuditEventSender;
import org.graylog2.bindings.providers.MongoJackObjectMapperProvider;
import org.graylog2.database.MongoConnectionRule;
import org.graylog2.database.NotFoundException;
import org.graylog2.indexer.ElasticsearchException;
import org.graylog2.indexer.IndexSetRegistry;
import org.graylog2.indexer.indices.Indices;
import org.graylog2.indexer.indices.events.IndicesClosedEvent;
import org.graylog2.indexer.indices.events.IndicesDeletedEvent;
import org.graylog2.indexer.indices.events.IndicesReopenedEvent;
import org.graylog2.indexer.searches.IndexRangeStats;
import org.graylog2.plugin.system.NodeId;
import org.graylog2.shared.bindings.providers.ObjectMapperProvider;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.util.Collections;
import java.util.Set;
import java.util.SortedSet;
import static com.lordofthejars.nosqlunit.mongodb.InMemoryMongoDb.InMemoryMongoRuleBuilder.newInMemoryMongoDbRule;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class MongoIndexRangeServiceTest {
@ClassRule
public static final InMemoryMongoDb IN_MEMORY_MONGO_DB = newInMemoryMongoDbRule().build();
@Rule
public MongoConnectionRule mongoRule = MongoConnectionRule.build("test");
@Rule
public final MockitoRule mockitoRule = MockitoJUnit.rule();
private final ObjectMapper objectMapper = new ObjectMapperProvider().get();
private final MongoJackObjectMapperProvider objectMapperProvider = new MongoJackObjectMapperProvider(objectMapper);
@Mock
private Indices indices;
@Mock
private IndexSetRegistry indexSetRegistry;
private EventBus localEventBus;
private MongoIndexRangeService indexRangeService;
@Before
public void setUp() throws Exception {
localEventBus = new EventBus("local-event-bus");
indexRangeService = new MongoIndexRangeService(mongoRule.getMongoConnection(), objectMapperProvider, indices, indexSetRegistry, new NullAuditEventSender(), mock(NodeId.class), localEventBus);
}
@Test
@UsingDataSet(loadStrategy = LoadStrategyEnum.CLEAN_INSERT)
public void getReturnsExistingIndexRange() throws Exception {
IndexRange indexRange = indexRangeService.get("graylog_1");
assertThat(indexRange.indexName()).isEqualTo("graylog_1");
assertThat(indexRange.begin()).isEqualTo(new DateTime(2015, 1, 1, 0, 0, DateTimeZone.UTC));
assertThat(indexRange.end()).isEqualTo(new DateTime(2015, 1, 2, 0, 0, DateTimeZone.UTC));
assertThat(indexRange.calculatedAt()).isEqualTo(new DateTime(2015, 1, 2, 0, 0, DateTimeZone.UTC));
assertThat(indexRange.calculationDuration()).isEqualTo(23);
}
@Test(expected = NotFoundException.class)
@UsingDataSet(locations = "MongoIndexRangeServiceTest-LegacyIndexRanges.json", loadStrategy = LoadStrategyEnum.CLEAN_INSERT)
public void getIgnoresLegacyIndexRange() throws Exception {
indexRangeService.get("graylog_0");
}
@Test(expected = NotFoundException.class)
public void getThrowsNotFoundException() throws Exception {
indexRangeService.get("does-not-exist");
}
/**
* Test the following constellation:
* <pre>
* [- index range -]
* [- graylog_1 -][- graylog_2 -][- graylog_3 -][- graylog_4 -][- graylog_5 -]
* </pre>
*/
@Test
@UsingDataSet(locations = "MongoIndexRangeServiceTest-distinct.json", loadStrategy = LoadStrategyEnum.CLEAN_INSERT)
public void findReturnsIndexRangesWithinGivenRange() throws Exception {
final DateTime begin = new DateTime(2015, 1, 2, 12, 0, DateTimeZone.UTC);
final DateTime end = new DateTime(2015, 1, 4, 12, 0, DateTimeZone.UTC);
final SortedSet<IndexRange> indexRanges = indexRangeService.find(begin, end);
assertThat(indexRanges).containsExactly(
MongoIndexRange.create(new ObjectId("55e0261a0cc6980000000002"), "graylog_2", new DateTime(2015, 1, 2, 0, 0, DateTimeZone.UTC), new DateTime(2015, 1, 3, 0, 0, DateTimeZone.UTC), new DateTime(2015, 1, 3, 0, 0, DateTimeZone.UTC), 42),
MongoIndexRange.create(new ObjectId("55e0261a0cc6980000000003"), "graylog_3", new DateTime(2015, 1, 3, 0, 0, DateTimeZone.UTC), new DateTime(2015, 1, 4, 0, 0, DateTimeZone.UTC), new DateTime(2015, 1, 4, 0, 0, DateTimeZone.UTC), 42),
MongoIndexRange.create(new ObjectId("55e0261a0cc6980000000004"), "graylog_4", new DateTime(2015, 1, 4, 0, 0, DateTimeZone.UTC), new DateTime(2015, 1, 5, 0, 0, DateTimeZone.UTC), new DateTime(2015, 1, 5, 0, 0, DateTimeZone.UTC), 42)
);
}
@Test
@UsingDataSet(locations = "MongoIndexRangeServiceTest-LegacyIndexRanges.json", loadStrategy = LoadStrategyEnum.CLEAN_INSERT)
public void findIgnoresLegacyIndexRanges() throws Exception {
final DateTime begin = new DateTime(2015, 1, 1, 0, 0, DateTimeZone.UTC);
final DateTime end = new DateTime(2015, 2, 1, 0, 0, DateTimeZone.UTC);
final SortedSet<IndexRange> indexRanges = indexRangeService.find(begin, end);
assertThat(indexRanges).containsOnly(
MongoIndexRange.create(new ObjectId("55e0261a0cc6980000000003"), "graylog_1", new DateTime(2015, 1, 1, 0, 0, DateTimeZone.UTC), new DateTime(2015, 1, 2, 0, 0, DateTimeZone.UTC), new DateTime(2015, 1, 2, 0, 0, DateTimeZone.UTC), 42)
);
}
@Test
@UsingDataSet(loadStrategy = LoadStrategyEnum.CLEAN_INSERT)
public void findReturnsNothingBeforeBegin() throws Exception {
final DateTime begin = new DateTime(2016, 1, 1, 0, 0, DateTimeZone.UTC);
final DateTime end = new DateTime(2016, 1, 2, 0, 0, DateTimeZone.UTC);
Set<IndexRange> indexRanges = indexRangeService.find(begin, end);
assertThat(indexRanges).isEmpty();
}
@Test
@UsingDataSet(loadStrategy = LoadStrategyEnum.CLEAN_INSERT)
public void findAllReturnsAllIndexRanges() throws Exception {
assertThat(indexRangeService.findAll()).hasSize(2);
}
@Test
@UsingDataSet(locations = "MongoIndexRangeServiceTest-LegacyIndexRanges.json", loadStrategy = LoadStrategyEnum.CLEAN_INSERT)
public void findAllReturnsAllIgnoresLegacyIndexRanges() throws Exception {
assertThat(indexRangeService.findAll()).hasSize(1);
}
@Test
@UsingDataSet(loadStrategy = LoadStrategyEnum.CLEAN_INSERT)
public void calculateRangeReturnsIndexRange() throws Exception {
final String index = "graylog";
final DateTime min = new DateTime(2015, 1, 1, 1, 0, DateTimeZone.UTC);
final DateTime max = new DateTime(2015, 1, 1, 5, 0, DateTimeZone.UTC);
when(indices.indexRangeStatsOfIndex(index)).thenReturn(IndexRangeStats.create(min, max));
final IndexRange indexRange = indexRangeService.calculateRange(index);
assertThat(indexRange.indexName()).isEqualTo(index);
assertThat(indexRange.begin()).isEqualTo(min);
assertThat(indexRange.end()).isEqualTo(max);
Assertions.assertThat(indexRange.calculatedAt()).isEqualToIgnoringHours(DateTime.now(DateTimeZone.UTC));
}
@Test(expected = ElasticsearchException.class)
public void calculateRangeFailsIfIndexIsNotHealthy() throws Exception {
final String index = "graylog";
when(indices.waitForRecovery(index)).thenThrow(new ElasticsearchException("TEST"));
indexRangeService.calculateRange(index);
}
@Test
@UsingDataSet(locations = "MongoIndexRangeServiceTest-EmptyCollection.json", loadStrategy = LoadStrategyEnum.CLEAN_INSERT)
public void testCalculateRangeWithEmptyIndex() throws Exception {
final String index = "graylog";
when(indices.indexRangeStatsOfIndex(index)).thenReturn(IndexRangeStats.EMPTY);
final IndexRange range = indexRangeService.calculateRange(index);
assertThat(range).isNotNull();
assertThat(range.indexName()).isEqualTo(index);
assertThat(range.begin()).isEqualTo(new DateTime(0L, DateTimeZone.UTC));
assertThat(range.end()).isEqualTo(new DateTime(0L, DateTimeZone.UTC));
}
@Test(expected = ElasticsearchException.class)
public void testCalculateRangeWithNonExistingIndex() throws Exception {
when(indices.indexRangeStatsOfIndex("does-not-exist")).thenThrow(new ElasticsearchException("does-not-exist"));
indexRangeService.calculateRange("does-not-exist");
}
@Test
@UsingDataSet(loadStrategy = LoadStrategyEnum.DELETE_ALL)
public void savePersistsIndexRange() throws Exception {
final String indexName = "graylog";
final DateTime begin = new DateTime(2015, 1, 1, 0, 0, DateTimeZone.UTC);
final DateTime end = new DateTime(2015, 1, 2, 0, 0, DateTimeZone.UTC);
final DateTime now = DateTime.now(DateTimeZone.UTC);
final IndexRange indexRange = MongoIndexRange.create(indexName, begin, end, now, 42);
indexRangeService.save(indexRange);
final IndexRange result = indexRangeService.get(indexName);
assertThat(result.indexName()).isEqualTo(indexName);
assertThat(result.begin()).isEqualTo(begin);
assertThat(result.end()).isEqualTo(end);
assertThat(result.calculatedAt()).isEqualTo(now);
assertThat(result.calculationDuration()).isEqualTo(42);
}
@Test
@UsingDataSet(loadStrategy = LoadStrategyEnum.DELETE_ALL)
public void saveOverwritesExistingIndexRange() throws Exception {
final String indexName = "graylog";
final DateTime begin = new DateTime(2015, 1, 1, 0, 0, DateTimeZone.UTC);
final DateTime end = new DateTime(2015, 1, 2, 0, 0, DateTimeZone.UTC);
final DateTime now = DateTime.now(DateTimeZone.UTC);
final IndexRange indexRangeBefore = MongoIndexRange.create(indexName, begin, end, now, 1);
final IndexRange indexRangeAfter = MongoIndexRange.create(indexName, begin, end, now, 2);
indexRangeService.save(indexRangeBefore);
final IndexRange before = indexRangeService.get(indexName);
assertThat(before.calculationDuration()).isEqualTo(1);
indexRangeService.save(indexRangeAfter);
final IndexRange after = indexRangeService.get(indexName);
assertThat(after.calculationDuration()).isEqualTo(2);
}
@Test
@UsingDataSet(loadStrategy = LoadStrategyEnum.CLEAN_INSERT)
public void remove() throws Exception {
assertThat(indexRangeService.findAll()).hasSize(2);
assertThat(indexRangeService.remove("graylog_1")).isTrue();
assertThat(indexRangeService.remove("graylog_1")).isFalse();
assertThat(indexRangeService.findAll()).hasSize(1);
}
@Test
@UsingDataSet(loadStrategy = LoadStrategyEnum.CLEAN_INSERT)
public void testHandleIndexDeletion() throws Exception {
when(indexSetRegistry.isManagedIndex("graylog_1")).thenReturn(true);
assertThat(indexRangeService.findAll()).hasSize(2);
localEventBus.post(IndicesDeletedEvent.create(Collections.singleton("graylog_1")));
assertThat(indexRangeService.findAll()).hasSize(1);
}
@Test
@UsingDataSet(loadStrategy = LoadStrategyEnum.CLEAN_INSERT)
public void testHandleIndexClosing() throws Exception {
when(indexSetRegistry.isManagedIndex("graylog_1")).thenReturn(true);
assertThat(indexRangeService.findAll()).hasSize(2);
localEventBus.post(IndicesClosedEvent.create(Collections.singleton("graylog_1")));
assertThat(indexRangeService.findAll()).hasSize(1);
}
@Test
@UsingDataSet(loadStrategy = LoadStrategyEnum.CLEAN_INSERT)
public void testHandleIndexReopening() throws Exception {
final DateTime begin = new DateTime(2016, 1, 1, 0, 0, DateTimeZone.UTC);
final DateTime end = new DateTime(2016, 1, 15, 0, 0, DateTimeZone.UTC);
when(indices.indexRangeStatsOfIndex("graylog_3")).thenReturn(IndexRangeStats.create(begin, end));
when(indexSetRegistry.isManagedIndex("graylog_3")).thenReturn(true);
localEventBus.post(IndicesReopenedEvent.create(Collections.singleton("graylog_3")));
final SortedSet<IndexRange> indexRanges = indexRangeService.find(begin, end);
assertThat(indexRanges).hasSize(1);
assertThat(indexRanges.first().indexName()).isEqualTo("graylog_3");
assertThat(indexRanges.first().begin()).isEqualTo(begin);
assertThat(indexRanges.first().end()).isEqualTo(end);
}
@Test
@UsingDataSet(loadStrategy = LoadStrategyEnum.CLEAN_INSERT)
public void testHandleIndexReopeningWhenNotManaged() throws Exception {
final DateTime begin = new DateTime(2016, 1, 1, 0, 0, DateTimeZone.UTC);
final DateTime end = new DateTime(2016, 1, 15, 0, 0, DateTimeZone.UTC);
when(indexSetRegistry.isManagedIndex("graylog_3")).thenReturn(false);
when(indices.indexRangeStatsOfIndex("graylog_3")).thenReturn(IndexRangeStats.EMPTY);
localEventBus.post(IndicesReopenedEvent.create(Collections.singleton("graylog_3")));
final SortedSet<IndexRange> indexRanges = indexRangeService.find(begin, end);
assertThat(indexRanges).isEmpty();
}
}