mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-02-18 10:54:54 +00:00
* ILM: parse origination date from index name (#46755) Introduce the `index.lifecycle.parse_origination_date` setting that indicates if the origination date should be parsed from the index name. If set to true an index which doesn't match the expected format (namely `indexName-{dateFormat}-optional_digits` will fail before being created. The origination date will be parsed when initialising a lifecycle for an index and it will be set as the `index.lifecycle.origination_date` for that index. A user set value for `index.lifecycle.origination_date` will always override a possible parsable date from the index name. (cherry picked from commit c363d27f0210733dad0c307d54fa224a92ddb569) Signed-off-by: Andrei Dan <andrei.dan@elastic.co> * Drop usage of Map.of to be java 8 compliant
This commit is contained in:
parent
a267df30fa
commit
27520cac3b
@ -27,6 +27,14 @@ information about rollover, see <<using-policies-rollover>>.
|
|||||||
(<<time-units, time units>>) How often {ilm} checks for indices that meet policy
|
(<<time-units, time units>>) How often {ilm} checks for indices that meet policy
|
||||||
criteria. Defaults to `10m`.
|
criteria. Defaults to `10m`.
|
||||||
|
|
||||||
|
`index.lifecycle.parse_origination_date`::
|
||||||
|
When configured to `true` the origination date will be parsed from the index
|
||||||
|
name. The index format must match the pattern `^.*-{date_format}-\\d+`, where
|
||||||
|
the `date_format` is `yyyy.MM.dd` and the trailing digits are optional (an
|
||||||
|
index that was rolled over would normally match the full format eg.
|
||||||
|
`logs-2016.10.31-000002`). If the index name doesn't match the pattern
|
||||||
|
the index creation will fail.
|
||||||
|
|
||||||
`index.lifecycle.origination_date`::
|
`index.lifecycle.origination_date`::
|
||||||
The timestamp that will be used to calculate the index age for its phase
|
The timestamp that will be used to calculate the index age for its phase
|
||||||
transitions. This allows the users to create an index containing old data and
|
transitions. This allows the users to create an index containing old data and
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
package org.elasticsearch.xpack.core.ilm;
|
||||||
|
|
||||||
|
import org.elasticsearch.ElasticsearchParseException;
|
||||||
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.common.time.DateFormatter;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import static org.elasticsearch.xpack.core.ilm.LifecycleSettings.LIFECYCLE_ORIGINATION_DATE;
|
||||||
|
import static org.elasticsearch.xpack.core.ilm.LifecycleSettings.LIFECYCLE_PARSE_ORIGINATION_DATE;
|
||||||
|
|
||||||
|
public class IndexLifecycleOriginationDateParser {
|
||||||
|
|
||||||
|
private static final DateFormatter DATE_FORMATTER = DateFormatter.forPattern("yyyy.MM.dd");
|
||||||
|
private static final String INDEX_NAME_REGEX = "^.*-(\\d{4}.\\d{2}.\\d{2})(-[\\d]+)?$";
|
||||||
|
private static final Pattern INDEX_NAME_PATTERN = Pattern.compile(INDEX_NAME_REGEX);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the origination date needs to be parsed from the index name.
|
||||||
|
*/
|
||||||
|
public static boolean shouldParseIndexName(Settings indexSettings) {
|
||||||
|
return indexSettings.getAsLong(LIFECYCLE_ORIGINATION_DATE, -1L) == -1L &&
|
||||||
|
indexSettings.getAsBoolean(LIFECYCLE_PARSE_ORIGINATION_DATE, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the index according to the supported format and extracts the origination date. If the index does not match the expected
|
||||||
|
* format or the date in the index name doesn't match the `yyyy.MM.dd` format it throws an {@link IllegalArgumentException}
|
||||||
|
*/
|
||||||
|
public static long parseIndexNameAndExtractDate(String indexName) {
|
||||||
|
Matcher matcher = INDEX_NAME_PATTERN.matcher(indexName);
|
||||||
|
if (matcher.matches()) {
|
||||||
|
String dateAsString = matcher.group(1);
|
||||||
|
try {
|
||||||
|
return DATE_FORMATTER.parseMillis(dateAsString);
|
||||||
|
} catch (ElasticsearchParseException | IllegalArgumentException e) {
|
||||||
|
throw new IllegalArgumentException("index name [" + indexName + "] contains date [" + dateAsString + "] which " +
|
||||||
|
"couldn't be parsed using the 'yyyy.MM.dd' format", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException("index name [" + indexName + "] does not match pattern '" + INDEX_NAME_REGEX + "'");
|
||||||
|
}
|
||||||
|
}
|
@ -10,8 +10,11 @@ import org.apache.logging.log4j.Logger;
|
|||||||
import org.elasticsearch.cluster.ClusterState;
|
import org.elasticsearch.cluster.ClusterState;
|
||||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||||
import org.elasticsearch.cluster.metadata.MetaData;
|
import org.elasticsearch.cluster.metadata.MetaData;
|
||||||
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.index.Index;
|
import org.elasticsearch.index.Index;
|
||||||
|
|
||||||
|
import static org.elasticsearch.xpack.core.ilm.IndexLifecycleOriginationDateParser.parseIndexNameAndExtractDate;
|
||||||
|
import static org.elasticsearch.xpack.core.ilm.IndexLifecycleOriginationDateParser.shouldParseIndexName;
|
||||||
import static org.elasticsearch.xpack.core.ilm.LifecycleExecutionState.ILM_CUSTOM_METADATA_KEY;
|
import static org.elasticsearch.xpack.core.ilm.LifecycleExecutionState.ILM_CUSTOM_METADATA_KEY;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -34,19 +37,34 @@ public final class InitializePolicyContextStep extends ClusterStateActionStep {
|
|||||||
// Index must have been since deleted, ignore it
|
// Index must have been since deleted, ignore it
|
||||||
return clusterState;
|
return clusterState;
|
||||||
}
|
}
|
||||||
|
|
||||||
LifecycleExecutionState lifecycleState = LifecycleExecutionState
|
LifecycleExecutionState lifecycleState = LifecycleExecutionState
|
||||||
.fromIndexMetadata(indexMetaData);
|
.fromIndexMetadata(indexMetaData);
|
||||||
|
|
||||||
if (lifecycleState.getLifecycleDate() != null) {
|
if (lifecycleState.getLifecycleDate() != null) {
|
||||||
return clusterState;
|
return clusterState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IndexMetaData.Builder indexMetadataBuilder = IndexMetaData.builder(indexMetaData);
|
||||||
|
if (shouldParseIndexName(indexMetaData.getSettings())) {
|
||||||
|
long parsedOriginationDate = parseIndexNameAndExtractDate(index.getName());
|
||||||
|
indexMetadataBuilder.settingsVersion(indexMetaData.getSettingsVersion() + 1)
|
||||||
|
.settings(Settings.builder()
|
||||||
|
.put(indexMetaData.getSettings())
|
||||||
|
.put(LifecycleSettings.LIFECYCLE_ORIGINATION_DATE, parsedOriginationDate)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
ClusterState.Builder newClusterStateBuilder = ClusterState.builder(clusterState);
|
ClusterState.Builder newClusterStateBuilder = ClusterState.builder(clusterState);
|
||||||
|
|
||||||
LifecycleExecutionState.Builder newCustomData = LifecycleExecutionState.builder(lifecycleState);
|
LifecycleExecutionState.Builder newCustomData = LifecycleExecutionState.builder(lifecycleState);
|
||||||
newCustomData.setIndexCreationDate(indexMetaData.getCreationDate());
|
newCustomData.setIndexCreationDate(indexMetaData.getCreationDate());
|
||||||
newClusterStateBuilder.metaData(MetaData.builder(clusterState.getMetaData()).put(IndexMetaData
|
indexMetadataBuilder.putCustom(ILM_CUSTOM_METADATA_KEY, newCustomData.build().asMap());
|
||||||
.builder(indexMetaData)
|
|
||||||
.putCustom(ILM_CUSTOM_METADATA_KEY, newCustomData.build().asMap())));
|
newClusterStateBuilder.metaData(
|
||||||
|
MetaData.builder(clusterState.getMetaData()).put(indexMetadataBuilder)
|
||||||
|
);
|
||||||
return newClusterStateBuilder.build();
|
return newClusterStateBuilder.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ public class LifecycleSettings {
|
|||||||
public static final String LIFECYCLE_NAME = "index.lifecycle.name";
|
public static final String LIFECYCLE_NAME = "index.lifecycle.name";
|
||||||
public static final String LIFECYCLE_INDEXING_COMPLETE = "index.lifecycle.indexing_complete";
|
public static final String LIFECYCLE_INDEXING_COMPLETE = "index.lifecycle.indexing_complete";
|
||||||
public static final String LIFECYCLE_ORIGINATION_DATE = "index.lifecycle.origination_date";
|
public static final String LIFECYCLE_ORIGINATION_DATE = "index.lifecycle.origination_date";
|
||||||
|
public static final String LIFECYCLE_PARSE_ORIGINATION_DATE = "index.lifecycle.parse_origination_date";
|
||||||
|
|
||||||
public static final String SLM_HISTORY_INDEX_ENABLED = "slm.history_index_enabled";
|
public static final String SLM_HISTORY_INDEX_ENABLED = "slm.history_index_enabled";
|
||||||
public static final String SLM_RETENTION_SCHEDULE = "slm.retention_schedule";
|
public static final String SLM_RETENTION_SCHEDULE = "slm.retention_schedule";
|
||||||
@ -32,6 +33,8 @@ public class LifecycleSettings {
|
|||||||
Setting.Property.Dynamic, Setting.Property.IndexScope);
|
Setting.Property.Dynamic, Setting.Property.IndexScope);
|
||||||
public static final Setting<Long> LIFECYCLE_ORIGINATION_DATE_SETTING =
|
public static final Setting<Long> LIFECYCLE_ORIGINATION_DATE_SETTING =
|
||||||
Setting.longSetting(LIFECYCLE_ORIGINATION_DATE, -1, -1, Setting.Property.Dynamic, Setting.Property.IndexScope);
|
Setting.longSetting(LIFECYCLE_ORIGINATION_DATE, -1, -1, Setting.Property.Dynamic, Setting.Property.IndexScope);
|
||||||
|
public static final Setting<Boolean> LIFECYCLE_PARSE_ORIGINATION_DATE_SETTING = Setting.boolSetting(LIFECYCLE_PARSE_ORIGINATION_DATE,
|
||||||
|
false, Setting.Property.Dynamic, Setting.Property.IndexScope);
|
||||||
|
|
||||||
public static final Setting<Boolean> SLM_HISTORY_INDEX_ENABLED_SETTING = Setting.boolSetting(SLM_HISTORY_INDEX_ENABLED, true,
|
public static final Setting<Boolean> SLM_HISTORY_INDEX_ENABLED_SETTING = Setting.boolSetting(SLM_HISTORY_INDEX_ENABLED, true,
|
||||||
Setting.Property.NodeScope);
|
Setting.Property.NodeScope);
|
||||||
|
@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
package org.elasticsearch.xpack.core.ilm;
|
||||||
|
|
||||||
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.test.ESTestCase;
|
||||||
|
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import static org.elasticsearch.xpack.core.ilm.IndexLifecycleOriginationDateParser.parseIndexNameAndExtractDate;
|
||||||
|
import static org.elasticsearch.xpack.core.ilm.IndexLifecycleOriginationDateParser.shouldParseIndexName;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
|
||||||
|
public class IndexLifecycleOriginationDateParserTests extends ESTestCase {
|
||||||
|
|
||||||
|
public void testShouldParseIndexNameReturnsFalseWhenOriginationDateIsSet() {
|
||||||
|
Settings settings = Settings.builder()
|
||||||
|
.put(LifecycleSettings.LIFECYCLE_ORIGINATION_DATE, 1L)
|
||||||
|
.build();
|
||||||
|
assertThat(shouldParseIndexName(settings), is(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testShouldParseIndexNameReturnsFalseIfParseOriginationDateIsDisabled() {
|
||||||
|
Settings settings = Settings.builder()
|
||||||
|
.put(LifecycleSettings.LIFECYCLE_PARSE_ORIGINATION_DATE, false)
|
||||||
|
.build();
|
||||||
|
assertThat(shouldParseIndexName(settings), is(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testShouldParseIndexNameReturnsTrueIfParseOriginationDateIsTrueAndOriginationDateIsNotSet() {
|
||||||
|
Settings settings = Settings.builder()
|
||||||
|
.put(LifecycleSettings.LIFECYCLE_PARSE_ORIGINATION_DATE, true)
|
||||||
|
.build();
|
||||||
|
assertThat(shouldParseIndexName(settings), is(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testParseIndexNameThatMatchesExpectedFormat() throws ParseException {
|
||||||
|
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd", Locale.getDefault());
|
||||||
|
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
|
long expectedDate = dateFormat.parse("2019.09.04").getTime();
|
||||||
|
|
||||||
|
{
|
||||||
|
long parsedDate = parseIndexNameAndExtractDate("indexName-2019.09.04");
|
||||||
|
assertThat("indexName-yyyy.MM.dd is a valid index format", parsedDate, is(expectedDate));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
long parsedDate = parseIndexNameAndExtractDate("indexName-2019.09.04-0000001");
|
||||||
|
assertThat("indexName-yyyy.MM.dd-\\d+$ is a valid index format", parsedDate, is(expectedDate));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
long parsedDate = parseIndexNameAndExtractDate("indexName-2019.09.04-2019.09.24");
|
||||||
|
long secondDateInIndexName = dateFormat.parse("2019.09.24").getTime();
|
||||||
|
assertThat("indexName-yyyy.MM.dd-yyyy.MM.dd is a valid index format and the second date should be parsed",
|
||||||
|
parsedDate, is(secondDateInIndexName));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
long parsedDate = parseIndexNameAndExtractDate("index-2019.09.04-2019.09.24-00002");
|
||||||
|
long secondDateInIndexName = dateFormat.parse("2019.09.24").getTime();
|
||||||
|
assertThat("indexName-yyyy.MM.dd-yyyy.MM.dd-digits is a valid index format and the second date should be parsed",
|
||||||
|
parsedDate, is(secondDateInIndexName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testParseIndexNameThrowsIllegalArgumentExceptionForInvalidIndexFormat() {
|
||||||
|
expectThrows(
|
||||||
|
IllegalArgumentException.class,
|
||||||
|
"plainIndexName does not match the expected pattern",
|
||||||
|
() -> parseIndexNameAndExtractDate("plainIndexName")
|
||||||
|
);
|
||||||
|
|
||||||
|
expectThrows(
|
||||||
|
IllegalArgumentException.class,
|
||||||
|
"indexName--00001 does not match the expected pattern as the origination date is missing",
|
||||||
|
() -> parseIndexNameAndExtractDate("indexName--00001")
|
||||||
|
);
|
||||||
|
|
||||||
|
expectThrows(
|
||||||
|
IllegalArgumentException.class,
|
||||||
|
"indexName-00001 does not match the expected pattern as the origination date is missing",
|
||||||
|
() -> parseIndexNameAndExtractDate("indexName-00001")
|
||||||
|
);
|
||||||
|
|
||||||
|
expectThrows(
|
||||||
|
IllegalArgumentException.class,
|
||||||
|
"indexName_2019.09.04_00001 does not match the expected pattern as _ is not the expected delimiter",
|
||||||
|
() -> parseIndexNameAndExtractDate("indexName_2019.09.04_00001")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testParseIndexNameThrowsIllegalArgumentExceptionForInvalidDateFormat() {
|
||||||
|
expectThrows(
|
||||||
|
IllegalArgumentException.class,
|
||||||
|
"indexName-2019.04-00001 does not match the expected pattern as the date does not conform with the yyyy.MM.dd pattern",
|
||||||
|
() -> parseIndexNameAndExtractDate("indexName-2019.04-00001")
|
||||||
|
);
|
||||||
|
|
||||||
|
expectThrows(
|
||||||
|
IllegalArgumentException.class,
|
||||||
|
"java.lang.IllegalArgumentException: failed to parse date field [2019.09.44] with format [yyyy.MM.dd]",
|
||||||
|
() -> parseIndexNameAndExtractDate("index-2019.09.44")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -28,6 +28,7 @@ import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
|||||||
import org.elasticsearch.core.internal.io.IOUtils;
|
import org.elasticsearch.core.internal.io.IOUtils;
|
||||||
import org.elasticsearch.env.Environment;
|
import org.elasticsearch.env.Environment;
|
||||||
import org.elasticsearch.env.NodeEnvironment;
|
import org.elasticsearch.env.NodeEnvironment;
|
||||||
|
import org.elasticsearch.index.IndexModule;
|
||||||
import org.elasticsearch.plugins.ActionPlugin;
|
import org.elasticsearch.plugins.ActionPlugin;
|
||||||
import org.elasticsearch.plugins.Plugin;
|
import org.elasticsearch.plugins.Plugin;
|
||||||
import org.elasticsearch.rest.RestController;
|
import org.elasticsearch.rest.RestController;
|
||||||
@ -155,6 +156,7 @@ public class IndexLifecycle extends Plugin implements ActionPlugin {
|
|||||||
LifecycleSettings.LIFECYCLE_POLL_INTERVAL_SETTING,
|
LifecycleSettings.LIFECYCLE_POLL_INTERVAL_SETTING,
|
||||||
LifecycleSettings.LIFECYCLE_NAME_SETTING,
|
LifecycleSettings.LIFECYCLE_NAME_SETTING,
|
||||||
LifecycleSettings.LIFECYCLE_ORIGINATION_DATE_SETTING,
|
LifecycleSettings.LIFECYCLE_ORIGINATION_DATE_SETTING,
|
||||||
|
LifecycleSettings.LIFECYCLE_PARSE_ORIGINATION_DATE_SETTING,
|
||||||
LifecycleSettings.LIFECYCLE_INDEXING_COMPLETE_SETTING,
|
LifecycleSettings.LIFECYCLE_INDEXING_COMPLETE_SETTING,
|
||||||
RolloverAction.LIFECYCLE_ROLLOVER_ALIAS_SETTING,
|
RolloverAction.LIFECYCLE_ROLLOVER_ALIAS_SETTING,
|
||||||
LifecycleSettings.SLM_HISTORY_INDEX_ENABLED_SETTING,
|
LifecycleSettings.SLM_HISTORY_INDEX_ENABLED_SETTING,
|
||||||
@ -280,6 +282,14 @@ public class IndexLifecycle extends Plugin implements ActionPlugin {
|
|||||||
return actions;
|
return actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onIndexModule(IndexModule indexModule) {
|
||||||
|
if (ilmEnabled) {
|
||||||
|
assert indexLifecycleInitialisationService.get() != null;
|
||||||
|
indexModule.addIndexEventListener(indexLifecycleInitialisationService.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
try {
|
try {
|
||||||
|
@ -22,6 +22,8 @@ import org.elasticsearch.common.component.Lifecycle.State;
|
|||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
||||||
|
import org.elasticsearch.index.Index;
|
||||||
|
import org.elasticsearch.index.shard.IndexEventListener;
|
||||||
import org.elasticsearch.threadpool.ThreadPool;
|
import org.elasticsearch.threadpool.ThreadPool;
|
||||||
import org.elasticsearch.xpack.core.XPackField;
|
import org.elasticsearch.xpack.core.XPackField;
|
||||||
import org.elasticsearch.xpack.core.ilm.IndexLifecycleMetadata;
|
import org.elasticsearch.xpack.core.ilm.IndexLifecycleMetadata;
|
||||||
@ -39,11 +41,14 @@ import java.util.Collections;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.LongSupplier;
|
import java.util.function.LongSupplier;
|
||||||
|
|
||||||
|
import static org.elasticsearch.xpack.core.ilm.IndexLifecycleOriginationDateParser.parseIndexNameAndExtractDate;
|
||||||
|
import static org.elasticsearch.xpack.core.ilm.IndexLifecycleOriginationDateParser.shouldParseIndexName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A service which runs the {@link LifecyclePolicy}s associated with indexes.
|
* A service which runs the {@link LifecyclePolicy}s associated with indexes.
|
||||||
*/
|
*/
|
||||||
public class IndexLifecycleService
|
public class IndexLifecycleService
|
||||||
implements ClusterStateListener, ClusterStateApplier, SchedulerEngine.Listener, Closeable, LocalNodeMasterListener {
|
implements ClusterStateListener, ClusterStateApplier, SchedulerEngine.Listener, Closeable, LocalNodeMasterListener, IndexEventListener {
|
||||||
private static final Logger logger = LogManager.getLogger(IndexLifecycleService.class);
|
private static final Logger logger = LogManager.getLogger(IndexLifecycleService.class);
|
||||||
private static final Set<String> IGNORE_STEPS_MAINTENANCE_REQUESTED = Collections.singleton(ShrinkStep.NAME);
|
private static final Set<String> IGNORE_STEPS_MAINTENANCE_REQUESTED = Collections.singleton(ShrinkStep.NAME);
|
||||||
private volatile boolean isMaster = false;
|
private volatile boolean isMaster = false;
|
||||||
@ -148,6 +153,13 @@ public class IndexLifecycleService
|
|||||||
return ThreadPool.Names.MANAGEMENT;
|
return ThreadPool.Names.MANAGEMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeIndexAddedToCluster(Index index, Settings indexSettings) {
|
||||||
|
if (shouldParseIndexName(indexSettings)) {
|
||||||
|
parseIndexNameAndExtractDate(index.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void updatePollInterval(TimeValue newInterval) {
|
private void updatePollInterval(TimeValue newInterval) {
|
||||||
this.pollInterval = newInterval;
|
this.pollInterval = newInterval;
|
||||||
maybeScheduleJob();
|
maybeScheduleJob();
|
||||||
|
@ -51,13 +51,17 @@ import org.elasticsearch.xpack.core.ilm.action.StopILMAction;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.TimeZone;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -72,6 +76,7 @@ import static org.elasticsearch.xpack.core.ilm.LifecyclePolicyTestsUtils.newLock
|
|||||||
import static org.hamcrest.CoreMatchers.not;
|
import static org.hamcrest.CoreMatchers.not;
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
import static org.hamcrest.Matchers.greaterThan;
|
||||||
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
||||||
import static org.hamcrest.Matchers.lessThanOrEqualTo;
|
import static org.hamcrest.Matchers.lessThanOrEqualTo;
|
||||||
import static org.hamcrest.core.CombinableMatcher.both;
|
import static org.hamcrest.core.CombinableMatcher.both;
|
||||||
@ -229,7 +234,7 @@ public class IndexLifecycleInitialisationTests extends ESIntegTestCase {
|
|||||||
{
|
{
|
||||||
PhaseExecutionInfo expectedExecutionInfo = new PhaseExecutionInfo(lifecyclePolicy.getName(), mockPhase, 1L, actualModifiedDate);
|
PhaseExecutionInfo expectedExecutionInfo = new PhaseExecutionInfo(lifecyclePolicy.getName(), mockPhase, 1L, actualModifiedDate);
|
||||||
assertBusy(() -> {
|
assertBusy(() -> {
|
||||||
IndexLifecycleExplainResponse indexResponse = executeExplainRequestAndGetTestIndexResponse();
|
IndexLifecycleExplainResponse indexResponse = executeExplainRequestAndGetTestIndexResponse("test");
|
||||||
assertThat(indexResponse.getStep(), equalTo("observable_cluster_state_action"));
|
assertThat(indexResponse.getStep(), equalTo("observable_cluster_state_action"));
|
||||||
assertThat(indexResponse.getPhaseExecutionInfo(), equalTo(expectedExecutionInfo));
|
assertThat(indexResponse.getPhaseExecutionInfo(), equalTo(expectedExecutionInfo));
|
||||||
originalLifecycleDate.set(indexResponse.getLifecycleDate());
|
originalLifecycleDate.set(indexResponse.getLifecycleDate());
|
||||||
@ -242,7 +247,7 @@ public class IndexLifecycleInitialisationTests extends ESIntegTestCase {
|
|||||||
|
|
||||||
{
|
{
|
||||||
assertBusy(() -> {
|
assertBusy(() -> {
|
||||||
IndexLifecycleExplainResponse indexResponse = executeExplainRequestAndGetTestIndexResponse();
|
IndexLifecycleExplainResponse indexResponse = executeExplainRequestAndGetTestIndexResponse("test");
|
||||||
assertThat("The configured origination date dictates the lifecycle date",
|
assertThat("The configured origination date dictates the lifecycle date",
|
||||||
indexResponse.getLifecycleDate(), equalTo(1000L));
|
indexResponse.getLifecycleDate(), equalTo(1000L));
|
||||||
});
|
});
|
||||||
@ -254,7 +259,7 @@ public class IndexLifecycleInitialisationTests extends ESIntegTestCase {
|
|||||||
|
|
||||||
{
|
{
|
||||||
assertBusy(() -> {
|
assertBusy(() -> {
|
||||||
IndexLifecycleExplainResponse indexResponse = executeExplainRequestAndGetTestIndexResponse();
|
IndexLifecycleExplainResponse indexResponse = executeExplainRequestAndGetTestIndexResponse("test");
|
||||||
assertThat("Without the origination date, the index create date should dictate the lifecycle date",
|
assertThat("Without the origination date, the index create date should dictate the lifecycle date",
|
||||||
indexResponse.getLifecycleDate(), equalTo(originalLifecycleDate.get()));
|
indexResponse.getLifecycleDate(), equalTo(originalLifecycleDate.get()));
|
||||||
});
|
});
|
||||||
@ -267,7 +272,7 @@ public class IndexLifecycleInitialisationTests extends ESIntegTestCase {
|
|||||||
{
|
{
|
||||||
PhaseExecutionInfo expectedExecutionInfo = new PhaseExecutionInfo(lifecyclePolicy.getName(), null, 1L, actualModifiedDate);
|
PhaseExecutionInfo expectedExecutionInfo = new PhaseExecutionInfo(lifecyclePolicy.getName(), null, 1L, actualModifiedDate);
|
||||||
assertBusy(() -> {
|
assertBusy(() -> {
|
||||||
IndexLifecycleExplainResponse indexResponse = executeExplainRequestAndGetTestIndexResponse();
|
IndexLifecycleExplainResponse indexResponse = executeExplainRequestAndGetTestIndexResponse("test");
|
||||||
assertThat(indexResponse.getPhase(), equalTo(TerminalPolicyStep.COMPLETED_PHASE));
|
assertThat(indexResponse.getPhase(), equalTo(TerminalPolicyStep.COMPLETED_PHASE));
|
||||||
assertThat(indexResponse.getStep(), equalTo(TerminalPolicyStep.KEY.getName()));
|
assertThat(indexResponse.getStep(), equalTo(TerminalPolicyStep.KEY.getName()));
|
||||||
assertThat(indexResponse.getPhaseExecutionInfo(), equalTo(expectedExecutionInfo));
|
assertThat(indexResponse.getPhaseExecutionInfo(), equalTo(expectedExecutionInfo));
|
||||||
@ -275,11 +280,80 @@ public class IndexLifecycleInitialisationTests extends ESIntegTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private IndexLifecycleExplainResponse executeExplainRequestAndGetTestIndexResponse() throws ExecutionException, InterruptedException {
|
public void testExplainParseOriginationDate() throws Exception {
|
||||||
|
// start node
|
||||||
|
logger.info("Starting server1");
|
||||||
|
internalCluster().startNode();
|
||||||
|
logger.info("Starting server2");
|
||||||
|
internalCluster().startNode();
|
||||||
|
logger.info("Creating lifecycle [test_lifecycle]");
|
||||||
|
PutLifecycleAction.Request putLifecycleRequest = new PutLifecycleAction.Request(lifecyclePolicy);
|
||||||
|
PutLifecycleAction.Response putLifecycleResponse = client().execute(PutLifecycleAction.INSTANCE, putLifecycleRequest).get();
|
||||||
|
assertAcked(putLifecycleResponse);
|
||||||
|
|
||||||
|
GetLifecycleAction.Response getLifecycleResponse = client().execute(GetLifecycleAction.INSTANCE,
|
||||||
|
new GetLifecycleAction.Request()).get();
|
||||||
|
assertThat(getLifecycleResponse.getPolicies().size(), equalTo(1));
|
||||||
|
GetLifecycleAction.LifecyclePolicyResponseItem responseItem = getLifecycleResponse.getPolicies().get(0);
|
||||||
|
assertThat(responseItem.getLifecyclePolicy(), equalTo(lifecyclePolicy));
|
||||||
|
assertThat(responseItem.getVersion(), equalTo(1L));
|
||||||
|
long actualModifiedDate = Instant.parse(responseItem.getModifiedDate()).toEpochMilli();
|
||||||
|
|
||||||
|
String indexName = "test-2019.09.14";
|
||||||
|
logger.info("Creating index [{}]", indexName);
|
||||||
|
CreateIndexResponse createIndexResponse =
|
||||||
|
client().admin().indices().create(createIndexRequest(indexName)
|
||||||
|
.settings(Settings.builder().put(settings).put(LifecycleSettings.LIFECYCLE_PARSE_ORIGINATION_DATE, true))
|
||||||
|
).actionGet();
|
||||||
|
assertAcked(createIndexResponse);
|
||||||
|
|
||||||
|
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd", Locale.getDefault());
|
||||||
|
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
|
long parsedLifecycleDate = dateFormat.parse("2019.09.14").getTime();
|
||||||
|
assertBusy(() -> {
|
||||||
|
IndexLifecycleExplainResponse indexResponse = executeExplainRequestAndGetTestIndexResponse(indexName);
|
||||||
|
assertThat(indexResponse.getLifecycleDate(), is(parsedLifecycleDate));
|
||||||
|
});
|
||||||
|
|
||||||
|
// disabling the lifecycle parsing would maintain the parsed value as that was set as the origination date
|
||||||
|
client().admin().indices().prepareUpdateSettings(indexName)
|
||||||
|
.setSettings(Collections.singletonMap(LifecycleSettings.LIFECYCLE_PARSE_ORIGINATION_DATE, false)).get();
|
||||||
|
|
||||||
|
assertBusy(() -> {
|
||||||
|
IndexLifecycleExplainResponse indexResponse = executeExplainRequestAndGetTestIndexResponse(indexName);
|
||||||
|
assertThat(indexResponse.getLifecycleDate(), is(parsedLifecycleDate));
|
||||||
|
});
|
||||||
|
|
||||||
|
// setting the lifecycle origination date setting to null should make the lifecyle date fallback on the index creation date
|
||||||
|
client().admin().indices().prepareUpdateSettings(indexName)
|
||||||
|
.setSettings(Collections.singletonMap(LifecycleSettings.LIFECYCLE_ORIGINATION_DATE, null)).get();
|
||||||
|
|
||||||
|
assertBusy(() -> {
|
||||||
|
IndexLifecycleExplainResponse indexResponse = executeExplainRequestAndGetTestIndexResponse(indexName);
|
||||||
|
assertThat(indexResponse.getLifecycleDate(), is(greaterThan(parsedLifecycleDate)));
|
||||||
|
});
|
||||||
|
|
||||||
|
// setting the lifecycle origination date to an explicit value overrides the date parsing
|
||||||
|
long originationDate = 42L;
|
||||||
|
Map<String, Object> settings = new HashMap<>();
|
||||||
|
settings.put(LifecycleSettings.LIFECYCLE_PARSE_ORIGINATION_DATE, true);
|
||||||
|
settings.put(LifecycleSettings.LIFECYCLE_ORIGINATION_DATE, originationDate);
|
||||||
|
client().admin().indices().prepareUpdateSettings(indexName)
|
||||||
|
.setSettings(settings)
|
||||||
|
.get();
|
||||||
|
|
||||||
|
assertBusy(() -> {
|
||||||
|
IndexLifecycleExplainResponse indexResponse = executeExplainRequestAndGetTestIndexResponse(indexName);
|
||||||
|
assertThat(indexResponse.getLifecycleDate(), is(originationDate));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private IndexLifecycleExplainResponse executeExplainRequestAndGetTestIndexResponse(String indexName) throws ExecutionException,
|
||||||
|
InterruptedException {
|
||||||
ExplainLifecycleRequest explainRequest = new ExplainLifecycleRequest();
|
ExplainLifecycleRequest explainRequest = new ExplainLifecycleRequest();
|
||||||
ExplainLifecycleResponse explainResponse = client().execute(ExplainLifecycleAction.INSTANCE, explainRequest).get();
|
ExplainLifecycleResponse explainResponse = client().execute(ExplainLifecycleAction.INSTANCE, explainRequest).get();
|
||||||
assertThat(explainResponse.getIndexResponses().size(), equalTo(1));
|
assertThat(explainResponse.getIndexResponses().size(), equalTo(1));
|
||||||
return explainResponse.getIndexResponses().get("test");
|
return explainResponse.getIndexResponses().get(indexName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testMasterDedicatedDataDedicated() throws Exception {
|
public void testMasterDedicatedDataDedicated() throws Exception {
|
||||||
|
@ -50,6 +50,7 @@ import java.time.ZoneId;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.SortedMap;
|
import java.util.SortedMap;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
||||||
import static org.elasticsearch.node.Node.NODE_MASTER_SETTING;
|
import static org.elasticsearch.node.Node.NODE_MASTER_SETTING;
|
||||||
@ -305,4 +306,21 @@ public class IndexLifecycleServiceTests extends ESTestCase {
|
|||||||
indexLifecycleService.triggered(schedulerEvent);
|
indexLifecycleService.triggered(schedulerEvent);
|
||||||
Mockito.verifyZeroInteractions(indicesClient, clusterService);
|
Mockito.verifyZeroInteractions(indicesClient, clusterService);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testParsingOriginationDateBeforeIndexCreation() {
|
||||||
|
Settings indexSettings = Settings.builder().put(LifecycleSettings.LIFECYCLE_PARSE_ORIGINATION_DATE, true).build();
|
||||||
|
Index index = new Index("invalid_index_name", UUID.randomUUID().toString());
|
||||||
|
expectThrows(IllegalArgumentException.class,
|
||||||
|
"The parse origination date setting was configured for index " + index.getName() +
|
||||||
|
" but the index name did not match the expected format",
|
||||||
|
() -> indexLifecycleService.beforeIndexAddedToCluster(index, indexSettings)
|
||||||
|
);
|
||||||
|
|
||||||
|
// disabling the parsing origination date setting should prevent the validation from throwing exception
|
||||||
|
try {
|
||||||
|
indexLifecycleService.beforeIndexAddedToCluster(index, Settings.EMPTY);
|
||||||
|
} catch (Exception e) {
|
||||||
|
fail("Did not expect the before index validation to throw an exception as the parse origination date setting was not set");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user