diff --git a/TESTING.asciidoc b/TESTING.asciidoc
index 212e215a201..2d6e6db8502 100644
--- a/TESTING.asciidoc
+++ b/TESTING.asciidoc
@@ -44,7 +44,7 @@ In order to run Elasticsearch from source without building a package, you can
run it using Maven:
-------------------------------------
-mvn package -Drun -DskipTests
+./run.sh
-------------------------------------
=== Test case filtering.
diff --git a/core/pom.xml b/core/pom.xml
index b507a36707d..def0b5e14f0 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -438,26 +438,6 @@
-
-
- org.apache.maven.plugins
- maven-antrun-plugin
-
-
-
- execute
- package
-
- run
-
-
-
-
-
-
-
-
-
diff --git a/core/src/main/java/org/elasticsearch/Version.java b/core/src/main/java/org/elasticsearch/Version.java
index 208770393f4..5ba63c4811b 100644
--- a/core/src/main/java/org/elasticsearch/Version.java
+++ b/core/src/main/java/org/elasticsearch/Version.java
@@ -243,11 +243,16 @@ public class Version {
public static final int V_1_6_1_ID = 1060199;
public static final Version V_1_6_1 = new Version(V_1_6_1_ID, false, org.apache.lucene.util.Version.LUCENE_4_10_4);
public static final int V_1_6_2_ID = 1060299;
- public static final Version V_1_6_2 = new Version(V_1_6_2_ID, true, org.apache.lucene.util.Version.LUCENE_4_10_4);
+ public static final Version V_1_6_2 = new Version(V_1_6_2_ID, false, org.apache.lucene.util.Version.LUCENE_4_10_4);
+ public static final int V_1_6_3_ID = 1060399;
+ public static final Version V_1_6_3 = new Version(V_1_6_3_ID, true, org.apache.lucene.util.Version.LUCENE_4_10_4);
public static final int V_1_7_0_ID = 1070099;
public static final Version V_1_7_0 = new Version(V_1_7_0_ID, false, org.apache.lucene.util.Version.LUCENE_4_10_4);
public static final int V_1_7_1_ID = 1070199;
- public static final Version V_1_7_1 = new Version(V_1_7_1_ID, true, org.apache.lucene.util.Version.LUCENE_4_10_4);
+ public static final Version V_1_7_1 = new Version(V_1_7_1_ID, false, org.apache.lucene.util.Version.LUCENE_4_10_4);
+ public static final int V_1_7_2_ID = 1070299;
+ public static final Version V_1_7_2 = new Version(V_1_7_2_ID, true, org.apache.lucene.util.Version.LUCENE_4_10_4);
+
public static final int V_2_0_0_beta1_ID = 2000001;
public static final Version V_2_0_0_beta1 = new Version(V_2_0_0_beta1_ID, true, org.apache.lucene.util.Version.LUCENE_5_2_1);
@@ -265,10 +270,14 @@ public class Version {
switch (id) {
case V_2_0_0_beta1_ID:
return V_2_0_0_beta1;
+ case V_1_7_2_ID:
+ return V_1_7_2;
case V_1_7_1_ID:
return V_1_7_1;
case V_1_7_0_ID:
return V_1_7_0;
+ case V_1_6_3_ID:
+ return V_1_6_3;
case V_1_6_2_ID:
return V_1_6_2;
case V_1_6_1_ID:
diff --git a/core/src/main/java/org/elasticsearch/action/ActionModule.java b/core/src/main/java/org/elasticsearch/action/ActionModule.java
index 07ded699dcc..a4b71626fa8 100644
--- a/core/src/main/java/org/elasticsearch/action/ActionModule.java
+++ b/core/src/main/java/org/elasticsearch/action/ActionModule.java
@@ -155,6 +155,7 @@ import org.elasticsearch.action.suggest.SuggestAction;
import org.elasticsearch.action.suggest.TransportSuggestAction;
import org.elasticsearch.action.support.ActionFilter;
import org.elasticsearch.action.support.ActionFilters;
+import org.elasticsearch.action.support.AutoCreateIndex;
import org.elasticsearch.action.support.TransportAction;
import org.elasticsearch.action.termvectors.*;
import org.elasticsearch.action.termvectors.dfs.TransportDfsOnlyAction;
@@ -221,6 +222,7 @@ public class ActionModule extends AbstractModule {
actionFilterMultibinder.addBinding().to(actionFilter);
}
bind(ActionFilters.class).asEagerSingleton();
+ bind(AutoCreateIndex.class).asEagerSingleton();
registerAction(NodesInfoAction.INSTANCE, TransportNodesInfoAction.class);
registerAction(NodesStatsAction.INSTANCE, TransportNodesStatsAction.class);
registerAction(NodesHotThreadsAction.INSTANCE, TransportNodesHotThreadsAction.class);
diff --git a/core/src/main/java/org/elasticsearch/action/RealtimeRequest.java b/core/src/main/java/org/elasticsearch/action/RealtimeRequest.java
new file mode 100644
index 00000000000..aefdcc5bdd9
--- /dev/null
+++ b/core/src/main/java/org/elasticsearch/action/RealtimeRequest.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.action;
+
+/**
+ * Indicates that a request can execute in realtime (reads from the translog).
+ * All {@link ActionRequest} that are realtime should implement this interface.
+ */
+public interface RealtimeRequest {
+
+ /**
+ * @param realtime Controls whether this request should be realtime by reading from the translog. If null
+ * is specified then whether the operation will be realtime depends on the api of the concrete request
+ * subclass.
+ */
+ R realtime(Boolean realtime);
+
+}
diff --git a/core/src/main/java/org/elasticsearch/action/bulk/TransportBulkAction.java b/core/src/main/java/org/elasticsearch/action/bulk/TransportBulkAction.java
index 67b31a3fac4..07427acfd82 100644
--- a/core/src/main/java/org/elasticsearch/action/bulk/TransportBulkAction.java
+++ b/core/src/main/java/org/elasticsearch/action/bulk/TransportBulkAction.java
@@ -74,13 +74,14 @@ public class TransportBulkAction extends HandledTransportAction listener) {
- if (autoCreateIndex.shouldAutoCreate(request.index(), clusterService.state())) {
+ ClusterState state = clusterService.state();
+ if (autoCreateIndex.shouldAutoCreate(request.index(), state)) {
createIndexAction.execute(new CreateIndexRequest(request).index(request.index()).cause("auto(delete api)").masterNodeTimeout(request.timeout()), new ActionListener() {
@Override
public void onResponse(CreateIndexResponse result) {
diff --git a/core/src/main/java/org/elasticsearch/action/get/GetRequest.java b/core/src/main/java/org/elasticsearch/action/get/GetRequest.java
index ca1273c8354..108abc9ae7c 100644
--- a/core/src/main/java/org/elasticsearch/action/get/GetRequest.java
+++ b/core/src/main/java/org/elasticsearch/action/get/GetRequest.java
@@ -21,6 +21,7 @@ package org.elasticsearch.action.get;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionRequestValidationException;
+import org.elasticsearch.action.RealtimeRequest;
import org.elasticsearch.action.ValidateActions;
import org.elasticsearch.action.support.single.shard.SingleShardRequest;
import org.elasticsearch.common.Nullable;
@@ -43,7 +44,7 @@ import java.io.IOException;
* @see org.elasticsearch.client.Requests#getRequest(String)
* @see org.elasticsearch.client.Client#get(GetRequest)
*/
-public class GetRequest extends SingleShardRequest {
+public class GetRequest extends SingleShardRequest implements RealtimeRequest {
private String type;
private String id;
@@ -244,6 +245,7 @@ public class GetRequest extends SingleShardRequest {
return this.realtime == null ? true : this.realtime;
}
+ @Override
public GetRequest realtime(Boolean realtime) {
this.realtime = realtime;
return this;
diff --git a/core/src/main/java/org/elasticsearch/action/get/MultiGetRequest.java b/core/src/main/java/org/elasticsearch/action/get/MultiGetRequest.java
index ae9b360fdbe..0d03bbbbf95 100644
--- a/core/src/main/java/org/elasticsearch/action/get/MultiGetRequest.java
+++ b/core/src/main/java/org/elasticsearch/action/get/MultiGetRequest.java
@@ -42,7 +42,7 @@ import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
-public class MultiGetRequest extends ActionRequest implements Iterable, CompositeIndicesRequest {
+public class MultiGetRequest extends ActionRequest implements Iterable, CompositeIndicesRequest, RealtimeRequest {
/**
* A single get item.
@@ -319,6 +319,7 @@ public class MultiGetRequest extends ActionRequest implements I
return this.realtime == null ? true : this.realtime;
}
+ @Override
public MultiGetRequest realtime(Boolean realtime) {
this.realtime = realtime;
return this;
diff --git a/core/src/main/java/org/elasticsearch/action/index/TransportIndexAction.java b/core/src/main/java/org/elasticsearch/action/index/TransportIndexAction.java
index 94a361a0eed..b1371e18a32 100644
--- a/core/src/main/java/org/elasticsearch/action/index/TransportIndexAction.java
+++ b/core/src/main/java/org/elasticsearch/action/index/TransportIndexAction.java
@@ -75,11 +75,12 @@ public class TransportIndexAction extends TransportReplicationAction listener) {
// if we don't have a master, we don't have metadata, that's fine, let it find a master using create index API
- if (autoCreateIndex.shouldAutoCreate(request.index(), clusterService.state())) {
+ ClusterState state = clusterService.state();
+ if (autoCreateIndex.shouldAutoCreate(request.index(), state)) {
CreateIndexRequest createIndexRequest = new CreateIndexRequest(request);
createIndexRequest.index(request.index());
createIndexRequest.mapping(request.type());
diff --git a/core/src/main/java/org/elasticsearch/action/search/type/TransportSearchTypeAction.java b/core/src/main/java/org/elasticsearch/action/search/type/TransportSearchTypeAction.java
index 371eb913fd4..c11162a0e77 100644
--- a/core/src/main/java/org/elasticsearch/action/search/type/TransportSearchTypeAction.java
+++ b/core/src/main/java/org/elasticsearch/action/search/type/TransportSearchTypeAction.java
@@ -115,7 +115,10 @@ public abstract class TransportSearchTypeAction extends TransportAction implements Iterable, CompositeIndicesRequest {
+public class MultiTermVectorsRequest extends ActionRequest implements Iterable, CompositeIndicesRequest, RealtimeRequest {
String preference;
List requests = new ArrayList<>();
@@ -162,4 +162,12 @@ public class MultiTermVectorsRequest extends ActionRequest implements DocumentRequest {
+public class TermVectorsRequest extends SingleShardRequest implements DocumentRequest, RealtimeRequest {
private String type;
@@ -405,6 +406,7 @@ public class TermVectorsRequest extends SingleShardRequest i
/**
* Choose whether term vectors be generated real-time.
*/
+ @Override
public TermVectorsRequest realtime(Boolean realtime) {
this.realtime = realtime;
return this;
diff --git a/core/src/main/java/org/elasticsearch/action/update/TransportUpdateAction.java b/core/src/main/java/org/elasticsearch/action/update/TransportUpdateAction.java
index 60fc6c0d210..1c77bed9747 100644
--- a/core/src/main/java/org/elasticsearch/action/update/TransportUpdateAction.java
+++ b/core/src/main/java/org/elasticsearch/action/update/TransportUpdateAction.java
@@ -75,14 +75,14 @@ public class TransportUpdateAction extends TransportInstanceSingleOperationActio
public TransportUpdateAction(Settings settings, ThreadPool threadPool, ClusterService clusterService, TransportService transportService,
TransportIndexAction indexAction, TransportDeleteAction deleteAction, TransportCreateIndexAction createIndexAction,
UpdateHelper updateHelper, ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver,
- IndicesService indicesService) {
+ IndicesService indicesService, AutoCreateIndex autoCreateIndex) {
super(settings, UpdateAction.NAME, threadPool, clusterService, transportService, actionFilters, indexNameExpressionResolver, UpdateRequest.class);
this.indexAction = indexAction;
this.deleteAction = deleteAction;
this.createIndexAction = createIndexAction;
this.updateHelper = updateHelper;
this.indicesService = indicesService;
- this.autoCreateIndex = new AutoCreateIndex(settings);
+ this.autoCreateIndex = autoCreateIndex;
}
@Override
diff --git a/core/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java b/core/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java
index f9454772f05..1669e5a0adb 100644
--- a/core/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java
+++ b/core/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java
@@ -21,31 +21,45 @@ package org.elasticsearch.cluster.metadata;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
+import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.action.IndicesRequest;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.collect.Tuple;
+import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject;
+import org.elasticsearch.common.joda.DateMathParser;
+import org.elasticsearch.common.joda.FormatDateTimeFormatter;
import org.elasticsearch.common.regex.Regex;
+import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.indices.IndexClosedException;
+import org.joda.time.DateTimeZone;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
import java.util.*;
+import java.util.concurrent.Callable;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Maps.filterEntries;
import static com.google.common.collect.Maps.newHashMap;
-public class IndexNameExpressionResolver {
+public class IndexNameExpressionResolver extends AbstractComponent {
private final ImmutableList expressionResolvers;
+ private DateMathExpressionResolver dateMathExpressionResolver;
@Inject
- public IndexNameExpressionResolver() {
- expressionResolvers = ImmutableList.of(new WildcardExpressionResolver());
+ public IndexNameExpressionResolver(Settings settings) {
+ super(settings);
+ expressionResolvers = ImmutableList.of(
+ dateMathExpressionResolver = new DateMathExpressionResolver(settings),
+ new WildcardExpressionResolver()
+ );
}
/**
@@ -75,6 +89,24 @@ public class IndexNameExpressionResolver {
return concreteIndices(context, indexExpressions);
}
+ /**
+ * Translates the provided index expression into actual concrete indices.
+ *
+ * @param state the cluster state containing all the data to resolve to expressions to concrete indices
+ * @param options defines how the aliases or indices need to be resolved to concrete indices
+ * @param startTime The start of the request where concrete indices is being invoked for
+ * @param indexExpressions expressions that can be resolved to alias or index names.
+ * @return the resolved concrete indices based on the cluster state, indices options and index expressions
+ * provided indices options in the context don't allow such a case, or if the final result of the indices resolution
+ * contains no indices and the indices options in the context don't allow such a case.
+ * @throws IllegalArgumentException if one of the aliases resolve to multiple indices and the provided
+ * indices options in the context don't allow such a case.
+ */
+ public String[] concreteIndices(ClusterState state, IndicesOptions options, long startTime, String... indexExpressions) {
+ Context context = new Context(state, options, startTime);
+ return concreteIndices(context, indexExpressions);
+ }
+
String[] concreteIndices(Context context, String... indexExpressions) {
if (indexExpressions == null || indexExpressions.length == 0) {
indexExpressions = new String[]{MetaData.ALL};
@@ -159,21 +191,30 @@ public class IndexNameExpressionResolver {
* that require a single index as a result. The indices resolution must in fact return a single index when
* using this method, an {@link IllegalArgumentException} gets thrown otherwise.
*
- * @param request request containing the index or alias to be resolved to concrete index and
- * the indices options to be used for the index resolution
- * @throws IndexNotFoundException if the resolved index or alias provided doesn't exist
+ * @param state the cluster state containing all the data to resolve to expression to a concrete index
+ * @param request The request that defines how the an alias or an index need to be resolved to a concrete index
+ * and the expression that can be resolved to an alias or an index name.
* @throws IllegalArgumentException if the index resolution lead to more than one index
* @return the concrete index obtained as a result of the index resolution
*/
public String concreteSingleIndex(ClusterState state, IndicesRequest request) {
- String indexOrAlias = request.indices() != null && request.indices().length > 0 ? request.indices()[0] : null;
- String[] indices = concreteIndices(state, request.indicesOptions(), indexOrAlias);
+ String indexExpression = request.indices() != null && request.indices().length > 0 ? request.indices()[0] : null;
+ String[] indices = concreteIndices(state, request.indicesOptions(), indexExpression);
if (indices.length != 1) {
throw new IllegalArgumentException("unable to return a single index as the index and options provided got resolved to multiple indices");
}
return indices[0];
}
+ /**
+ * @return whether the specified alias or index exists. If the alias or index contains datemath then that is resolved too.
+ */
+ public boolean hasIndexOrAlias(String aliasOrIndex, ClusterState state) {
+ Context context = new Context(state, IndicesOptions.lenientExpandOpen());
+ String resolvedAliasOrIndex = dateMathExpressionResolver.resolveExpression(aliasOrIndex, context);
+ return state.metaData().getAliasAndIndexLookup().containsKey(resolvedAliasOrIndex);
+ }
+
/**
* Iterates through the list of indices and selects the effective list of filtering aliases for the
* given index.
@@ -405,10 +446,18 @@ public class IndexNameExpressionResolver {
private final ClusterState state;
private final IndicesOptions options;
+ private final long startTime;
Context(ClusterState state, IndicesOptions options) {
this.state = state;
this.options = options;
+ startTime = System.currentTimeMillis();
+ }
+
+ public Context(ClusterState state, IndicesOptions options, long startTime) {
+ this.state = state;
+ this.options = options;
+ this.startTime = startTime;
}
public ClusterState getState() {
@@ -418,6 +467,10 @@ public class IndexNameExpressionResolver {
public IndicesOptions getOptions() {
return options;
}
+
+ public long getStartTime() {
+ return startTime;
+ }
}
private interface ExpressionResolver {
@@ -582,4 +635,163 @@ public class IndexNameExpressionResolver {
}
}
+ final static class DateMathExpressionResolver implements ExpressionResolver {
+
+ private static final String EXPRESSION_LEFT_BOUND = "<";
+ private static final String EXPRESSION_RIGHT_BOUND = ">";
+ private static final char LEFT_BOUND = '{';
+ private static final char RIGHT_BOUND = '}';
+ private static final char ESCAPE_CHAR = '\\';
+ private static final char TIME_ZONE_BOUND = '|';
+
+ private final DateTimeZone defaultTimeZone;
+ private final String defaultDateFormatterPattern;
+ private final DateTimeFormatter defaultDateFormatter;
+
+ public DateMathExpressionResolver(Settings settings) {
+ String defaultTimeZoneId = settings.get("date_math_expression_resolver.default_time_zone", "UTC");
+ this.defaultTimeZone = DateTimeZone.forID(defaultTimeZoneId);
+ defaultDateFormatterPattern = settings.get("date_math_expression_resolver.default_date_format", "YYYY.MM.dd");
+ this.defaultDateFormatter = DateTimeFormat.forPattern(defaultDateFormatterPattern);
+ }
+
+ @Override
+ public List resolve(final Context context, List expressions) {
+ List result = new ArrayList<>(expressions.size());
+ for (String expression : expressions) {
+ result.add(resolveExpression(expression, context));
+ }
+ return result;
+ }
+
+ String resolveExpression(String expression, final Context context) {
+ if (expression.startsWith(EXPRESSION_LEFT_BOUND) == false || expression.endsWith(EXPRESSION_RIGHT_BOUND) == false) {
+ return expression;
+ }
+
+ boolean escape = false;
+ boolean inDateFormat = false;
+ boolean inPlaceHolder = false;
+ final StringBuilder beforePlaceHolderSb = new StringBuilder();
+ StringBuilder inPlaceHolderSb = new StringBuilder();
+ final char[] text = expression.toCharArray();
+ final int from = 1;
+ final int length = text.length - 1;
+ for (int i = from; i < length; i++) {
+ boolean escapedChar = escape;
+ if (escape) {
+ escape = false;
+ }
+
+ char c = text[i];
+ if (c == ESCAPE_CHAR) {
+ if (escapedChar) {
+ beforePlaceHolderSb.append(c);
+ escape = false;
+ } else {
+ escape = true;
+ }
+ continue;
+ }
+ if (inPlaceHolder) {
+ switch (c) {
+ case LEFT_BOUND:
+ if (inDateFormat && escapedChar) {
+ inPlaceHolderSb.append(c);
+ } else if (!inDateFormat) {
+ inDateFormat = true;
+ inPlaceHolderSb.append(c);
+ } else {
+ throw new ElasticsearchParseException("invalid dynamic name expression [{}]. invalid character in placeholder at position [{}]", new String(text, from, length), i);
+ }
+ break;
+
+ case RIGHT_BOUND:
+ if (inDateFormat && escapedChar) {
+ inPlaceHolderSb.append(c);
+ } else if (inDateFormat) {
+ inDateFormat = false;
+ inPlaceHolderSb.append(c);
+ } else {
+ String inPlaceHolderString = inPlaceHolderSb.toString();
+ int dateTimeFormatLeftBoundIndex = inPlaceHolderString.indexOf(LEFT_BOUND);
+ String mathExpression;
+ String dateFormatterPattern;
+ DateTimeFormatter dateFormatter;
+ final DateTimeZone timeZone;
+ if (dateTimeFormatLeftBoundIndex < 0) {
+ mathExpression = inPlaceHolderString;
+ dateFormatterPattern = defaultDateFormatterPattern;
+ dateFormatter = defaultDateFormatter;
+ timeZone = defaultTimeZone;
+ } else {
+ if (inPlaceHolderString.lastIndexOf(RIGHT_BOUND) != inPlaceHolderString.length() - 1) {
+ throw new ElasticsearchParseException("invalid dynamic name expression [{}]. missing closing `}` for date math format", inPlaceHolderString);
+ }
+ if (dateTimeFormatLeftBoundIndex == inPlaceHolderString.length() - 2) {
+ throw new ElasticsearchParseException("invalid dynamic name expression [{}]. missing date format", inPlaceHolderString);
+ }
+ mathExpression = inPlaceHolderString.substring(0, dateTimeFormatLeftBoundIndex);
+ String dateFormatterPatternAndTimeZoneId = inPlaceHolderString.substring(dateTimeFormatLeftBoundIndex + 1, inPlaceHolderString.length() - 1);
+ int formatPatternTimeZoneSeparatorIndex = dateFormatterPatternAndTimeZoneId.indexOf(TIME_ZONE_BOUND);
+ if (formatPatternTimeZoneSeparatorIndex != -1) {
+ dateFormatterPattern = dateFormatterPatternAndTimeZoneId.substring(0, formatPatternTimeZoneSeparatorIndex);
+ timeZone = DateTimeZone.forID(dateFormatterPatternAndTimeZoneId.substring(formatPatternTimeZoneSeparatorIndex + 1));
+ } else {
+ dateFormatterPattern = dateFormatterPatternAndTimeZoneId;
+ timeZone = defaultTimeZone;
+ }
+ dateFormatter = DateTimeFormat.forPattern(dateFormatterPattern);
+ }
+ DateTimeFormatter parser = dateFormatter.withZone(timeZone);
+ FormatDateTimeFormatter formatter = new FormatDateTimeFormatter(dateFormatterPattern, parser, Locale.ROOT);
+ DateMathParser dateMathParser = new DateMathParser(formatter);
+ long millis = dateMathParser.parse(mathExpression, new Callable() {
+ @Override
+ public Long call() throws Exception {
+ return context.getStartTime();
+ }
+ }, false, timeZone);
+
+ String time = formatter.printer().print(millis);
+ beforePlaceHolderSb.append(time);
+ inPlaceHolderSb = new StringBuilder();
+ inPlaceHolder = false;
+ }
+ break;
+
+ default:
+ inPlaceHolderSb.append(c);
+ }
+ } else {
+ switch (c) {
+ case LEFT_BOUND:
+ if (escapedChar) {
+ beforePlaceHolderSb.append(c);
+ } else {
+ inPlaceHolder = true;
+ }
+ break;
+
+ case RIGHT_BOUND:
+ if (!escapedChar) {
+ throw new ElasticsearchParseException("invalid dynamic name expression [{}]. invalid character at position [{}]. " +
+ "`{` and `}` are reserved characters and should be escaped when used as part of the index name using `\\` (e.g. `\\{text\\}`)", new String(text, from, length), i);
+ }
+ default:
+ beforePlaceHolderSb.append(c);
+ }
+ }
+ }
+
+ if (inPlaceHolder) {
+ throw new ElasticsearchParseException("invalid dynamic name expression [{}]. date math placeholder is open ended", new String(text, from, length));
+ }
+ if (beforePlaceHolderSb.length() == 0) {
+ throw new ElasticsearchParseException("nothing captured");
+ }
+ return beforePlaceHolderSb.toString();
+ }
+ }
+
}
diff --git a/core/src/main/java/org/elasticsearch/rest/action/RestActionModule.java b/core/src/main/java/org/elasticsearch/rest/action/RestActionModule.java
index a13a038d6e5..0764ca2f536 100644
--- a/core/src/main/java/org/elasticsearch/rest/action/RestActionModule.java
+++ b/core/src/main/java/org/elasticsearch/rest/action/RestActionModule.java
@@ -250,6 +250,7 @@ public class RestActionModule extends AbstractModule {
catActionMultibinder.addBinding().to(RestThreadPoolAction.class).asEagerSingleton();
catActionMultibinder.addBinding().to(RestPluginsAction.class).asEagerSingleton();
catActionMultibinder.addBinding().to(RestFielddataAction.class).asEagerSingleton();
+ catActionMultibinder.addBinding().to(RestNodeAttrsAction.class).asEagerSingleton();
// no abstract cat action
bind(RestCatAction.class).asEagerSingleton();
}
diff --git a/core/src/main/java/org/elasticsearch/rest/action/cat/RestNodeAttrsAction.java b/core/src/main/java/org/elasticsearch/rest/action/cat/RestNodeAttrsAction.java
new file mode 100644
index 00000000000..48cbf8809b2
--- /dev/null
+++ b/core/src/main/java/org/elasticsearch/rest/action/cat/RestNodeAttrsAction.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.rest.action.cat;
+import com.google.common.collect.ImmutableMap;
+import org.elasticsearch.action.admin.cluster.node.info.NodeInfo;
+import org.elasticsearch.action.admin.cluster.node.info.NodesInfoRequest;
+import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse;
+import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsRequest;
+import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsResponse;
+import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest;
+import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse;
+import org.elasticsearch.client.Client;
+import org.elasticsearch.cluster.node.DiscoveryNode;
+import org.elasticsearch.cluster.node.DiscoveryNodes;
+import org.elasticsearch.common.Strings;
+import org.elasticsearch.common.Table;
+import org.elasticsearch.common.inject.Inject;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.transport.InetSocketTransportAddress;
+import org.elasticsearch.rest.*;
+import org.elasticsearch.rest.action.support.RestActionListener;
+import org.elasticsearch.rest.action.support.RestResponseListener;
+import org.elasticsearch.rest.action.support.RestTable;
+
+import static org.elasticsearch.rest.RestRequest.Method.GET;
+
+public class RestNodeAttrsAction extends AbstractCatAction {
+
+ @Inject
+ public RestNodeAttrsAction(Settings settings, RestController controller, Client client) {
+ super(settings, controller, client);
+ controller.registerHandler(GET, "/_cat/nodeattrs", this);
+ }
+
+ @Override
+ void documentation(StringBuilder sb) {
+ sb.append("/_cat/nodeattrs\n");
+ }
+
+ @Override
+ public void doRequest(final RestRequest request, final RestChannel channel, final Client client) {
+ final ClusterStateRequest clusterStateRequest = new ClusterStateRequest();
+ clusterStateRequest.clear().nodes(true);
+ clusterStateRequest.local(request.paramAsBoolean("local", clusterStateRequest.local()));
+ clusterStateRequest.masterNodeTimeout(request.paramAsTime("master_timeout", clusterStateRequest.masterNodeTimeout()));
+
+ client.admin().cluster().state(clusterStateRequest, new RestActionListener(channel) {
+ @Override
+ public void processResponse(final ClusterStateResponse clusterStateResponse) {
+ NodesInfoRequest nodesInfoRequest = new NodesInfoRequest();
+ nodesInfoRequest.clear().jvm(false).os(false).process(true);
+ client.admin().cluster().nodesInfo(nodesInfoRequest, new RestActionListener(channel) {
+ @Override
+ public void processResponse(final NodesInfoResponse nodesInfoResponse) {
+ NodesStatsRequest nodesStatsRequest = new NodesStatsRequest();
+ nodesStatsRequest.clear().jvm(false).os(false).fs(false).indices(false).process(false);
+ client.admin().cluster().nodesStats(nodesStatsRequest, new RestResponseListener(channel) {
+ @Override
+ public RestResponse buildResponse(NodesStatsResponse nodesStatsResponse) throws Exception {
+ return RestTable.buildResponse(buildTable(request, clusterStateResponse, nodesInfoResponse, nodesStatsResponse), channel);
+ }
+ });
+ }
+ });
+ }
+ });
+ }
+
+ @Override
+ Table getTableWithHeader(final RestRequest request) {
+ Table table = new Table();
+ table.startHeaders();
+ table.addCell("node", "default:true;alias:name;desc:node name");
+ table.addCell("id", "default:false;alias:id,nodeId;desc:unique node id");
+ table.addCell("pid", "default:false;alias:p;desc:process id");
+ table.addCell("host", "alias:h;desc:host name");
+ table.addCell("ip", "alias:i;desc:ip address");
+ table.addCell("port", "default:false;alias:po;desc:bound transport port");
+ table.addCell("attr", "default:true;alias:attr.name;desc:attribute description");
+ table.addCell("value","default:true;alias:attr.value;desc:attribute value");
+ table.endHeaders();
+ return table;
+ }
+
+ private Table buildTable(RestRequest req, ClusterStateResponse state, NodesInfoResponse nodesInfo, NodesStatsResponse nodesStats) {
+ boolean fullId = req.paramAsBoolean("full_id", false);
+
+ DiscoveryNodes nodes = state.getState().nodes();
+ Table table = getTableWithHeader(req);
+
+ for (DiscoveryNode node : nodes) {
+ NodeInfo info = nodesInfo.getNodesMap().get(node.id());
+ ImmutableMap attrs = node.getAttributes();
+ for(String att : attrs.keySet()) {
+ table.startRow();
+ table.addCell(node.name());
+ table.addCell(fullId ? node.id() : Strings.substring(node.getId(), 0, 4));
+ table.addCell(info == null ? null : info.getProcess().getId());
+ table.addCell(node.getHostName());
+ table.addCell(node.getHostAddress());
+ if (node.address() instanceof InetSocketTransportAddress) {
+ table.addCell(((InetSocketTransportAddress) node.address()).address().getPort());
+ } else {
+ table.addCell("-");
+ }
+ table.addCell(att);
+ table.addCell(attrs.containsKey(att) ? attrs.get(att) : null);
+ table.endRow();
+ }
+ }
+
+ return table;
+ }
+}
diff --git a/core/src/test/java/org/elasticsearch/action/support/replication/ShardReplicationTests.java b/core/src/test/java/org/elasticsearch/action/support/replication/ShardReplicationTests.java
index 8d3602396e3..5cfeceb3bd8 100644
--- a/core/src/test/java/org/elasticsearch/action/support/replication/ShardReplicationTests.java
+++ b/core/src/test/java/org/elasticsearch/action/support/replication/ShardReplicationTests.java
@@ -694,7 +694,7 @@ public class ShardReplicationTests extends ElasticsearchTestCase {
ThreadPool threadPool) {
super(settings, actionName, transportService, clusterService, null, threadPool,
new ShardStateAction(settings, clusterService, transportService, null, null), null,
- new ActionFilters(new HashSet()), new IndexNameExpressionResolver(), Request.class, Request.class, ThreadPool.Names.SAME);
+ new ActionFilters(new HashSet()), new IndexNameExpressionResolver(Settings.EMPTY), Request.class, Request.class, ThreadPool.Names.SAME);
}
@Override
diff --git a/core/src/test/java/org/elasticsearch/cluster/ClusterHealthResponsesTests.java b/core/src/test/java/org/elasticsearch/cluster/ClusterHealthResponsesTests.java
index 43fe4e1b7d8..36c63c0d3ea 100644
--- a/core/src/test/java/org/elasticsearch/cluster/ClusterHealthResponsesTests.java
+++ b/core/src/test/java/org/elasticsearch/cluster/ClusterHealthResponsesTests.java
@@ -32,6 +32,7 @@ import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.cluster.routing.*;
import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.test.ElasticsearchTestCase;
@@ -46,7 +47,7 @@ import static org.hamcrest.Matchers.*;
public class ClusterHealthResponsesTests extends ElasticsearchTestCase {
- private final IndexNameExpressionResolver indexNameExpressionResolver = new IndexNameExpressionResolver();
+ private final IndexNameExpressionResolver indexNameExpressionResolver = new IndexNameExpressionResolver(Settings.EMPTY);
private void assertIndexHealth(ClusterIndexHealth indexHealth, ShardCounter counter, IndexMetaData indexMetaData) {
assertThat(indexHealth.getStatus(), equalTo(counter.status()));
diff --git a/core/src/test/java/org/elasticsearch/cluster/metadata/DateMathExpressionResolverTests.java b/core/src/test/java/org/elasticsearch/cluster/metadata/DateMathExpressionResolverTests.java
new file mode 100644
index 00000000000..8dd5a1972d4
--- /dev/null
+++ b/core/src/test/java/org/elasticsearch/cluster/metadata/DateMathExpressionResolverTests.java
@@ -0,0 +1,199 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.cluster.metadata;
+
+import org.elasticsearch.ElasticsearchParseException;
+import org.elasticsearch.action.support.IndicesOptions;
+import org.elasticsearch.cluster.ClusterName;
+import org.elasticsearch.cluster.ClusterState;
+import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver.Context;
+import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver.DateMathExpressionResolver;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.test.ElasticsearchTestCase;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.format.DateTimeFormat;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.joda.time.DateTimeZone.UTC;
+
+public class DateMathExpressionResolverTests extends ElasticsearchTestCase {
+
+ private final DateMathExpressionResolver expressionResolver = new DateMathExpressionResolver(Settings.EMPTY);
+ private final Context context = new Context(
+ ClusterState.builder(new ClusterName("_name")).build(), IndicesOptions.strictExpand()
+ );
+
+ public void testNormal() throws Exception {
+ int numIndexExpressions = randomIntBetween(1, 9);
+ List indexExpressions = new ArrayList<>(numIndexExpressions);
+ for (int i = 0; i < numIndexExpressions; i++) {
+ indexExpressions.add(randomAsciiOfLength(10));
+ }
+ List result = expressionResolver.resolve(context, indexExpressions);
+ assertThat(result.size(), equalTo(indexExpressions.size()));
+ for (int i = 0; i < indexExpressions.size(); i++) {
+ assertThat(result.get(i), equalTo(indexExpressions.get(i)));
+ }
+ }
+
+ public void testExpression() throws Exception {
+ List indexExpressions = Arrays.asList("<.marvel-{now}>", "<.watch_history-{now}>", "");
+ List result = expressionResolver.resolve(context, indexExpressions);
+ assertThat(result.size(), equalTo(3));
+ assertThat(result.get(0), equalTo(".marvel-" + DateTimeFormat.forPattern("YYYY.MM.dd").print(new DateTime(context.getStartTime(), UTC))));
+ assertThat(result.get(1), equalTo(".watch_history-" + DateTimeFormat.forPattern("YYYY.MM.dd").print(new DateTime(context.getStartTime(), UTC))));
+ assertThat(result.get(2), equalTo("logstash-" + DateTimeFormat.forPattern("YYYY.MM.dd").print(new DateTime(context.getStartTime(), UTC))));
+ }
+
+ public void testEmpty() throws Exception {
+ List result = expressionResolver.resolve(context, Collections.emptyList());
+ assertThat(result.size(), equalTo(0));
+ }
+
+ public void testExpression_Static() throws Exception {
+ List result = expressionResolver.resolve(context, Arrays.asList("<.marvel-test>"));
+ assertThat(result.size(), equalTo(1));
+ assertThat(result.get(0), equalTo(".marvel-test"));
+ }
+
+ public void testExpression_MultiParts() throws Exception {
+ List result = expressionResolver.resolve(context, Arrays.asList("<.text1-{now/d}-text2-{now/M}>"));
+ assertThat(result.size(), equalTo(1));
+ assertThat(result.get(0), equalTo(".text1-"
+ + DateTimeFormat.forPattern("YYYY.MM.dd").print(new DateTime(context.getStartTime(), UTC))
+ + "-text2-"
+ + DateTimeFormat.forPattern("YYYY.MM.dd").print(new DateTime(context.getStartTime(), UTC).withDayOfMonth(1))));
+ }
+
+ public void testExpression_CustomFormat() throws Exception {
+ List results = expressionResolver.resolve(context, Arrays.asList("<.marvel-{now/d{YYYY.MM.dd}}>"));
+ assertThat(results.size(), equalTo(1));
+ assertThat(results.get(0), equalTo(".marvel-" + DateTimeFormat.forPattern("YYYY.MM.dd").print(new DateTime(context.getStartTime(), UTC))));
+ }
+
+ public void testExpression_EscapeStatic() throws Exception {
+ List result = expressionResolver.resolve(context, Arrays.asList("<.mar\\{v\\}el-{now/d}>"));
+ assertThat(result.size(), equalTo(1));
+ assertThat(result.get(0), equalTo(".mar{v}el-" + DateTimeFormat.forPattern("YYYY.MM.dd").print(new DateTime(context.getStartTime(), UTC))));
+ }
+
+ public void testExpression_EscapeDateFormat() throws Exception {
+ List result = expressionResolver.resolve(context, Arrays.asList("<.marvel-{now/d{'\\{year\\}'YYYY}}>"));
+ assertThat(result.size(), equalTo(1));
+ assertThat(result.get(0), equalTo(".marvel-" + DateTimeFormat.forPattern("'{year}'YYYY").print(new DateTime(context.getStartTime(), UTC))));
+ }
+
+ public void testExpression_MixedArray() throws Exception {
+ List result = expressionResolver.resolve(context, Arrays.asList(
+ "name1", "<.marvel-{now/d}>", "name2", "<.logstash-{now/M{YYYY.MM}}>"
+ ));
+ assertThat(result.size(), equalTo(4));
+ assertThat(result.get(0), equalTo("name1"));
+ assertThat(result.get(1), equalTo(".marvel-" + DateTimeFormat.forPattern("YYYY.MM.dd").print(new DateTime(context.getStartTime(), UTC))));
+ assertThat(result.get(2), equalTo("name2"));
+ assertThat(result.get(3), equalTo(".logstash-" + DateTimeFormat.forPattern("YYYY.MM").print(new DateTime(context.getStartTime(), UTC).withDayOfMonth(1))));
+ }
+
+ public void testExpression_CustomTimeZoneInSetting() throws Exception {
+ DateTimeZone timeZone;
+ int hoursOffset;
+ int minutesOffset = 0;
+ if (randomBoolean()) {
+ hoursOffset = randomIntBetween(-12, 14);
+ timeZone = DateTimeZone.forOffsetHours(hoursOffset);
+ } else {
+ hoursOffset = randomIntBetween(-11, 13);
+ minutesOffset = randomIntBetween(0, 59);
+ timeZone = DateTimeZone.forOffsetHoursMinutes(hoursOffset, minutesOffset);
+ }
+ DateTime now;
+ if (hoursOffset >= 0) {
+ // rounding to next day 00:00
+ now = DateTime.now(UTC).plusHours(hoursOffset).plusMinutes(minutesOffset).withHourOfDay(0).withMinuteOfHour(0).withSecondOfMinute(0);
+ } else {
+ // rounding to today 00:00
+ now = DateTime.now(UTC).withHourOfDay(0).withMinuteOfHour(0).withSecondOfMinute(0);
+ }
+ Settings settings = Settings.builder()
+ .put("date_math_expression_resolver.default_time_zone", timeZone.getID())
+ .build();
+ DateMathExpressionResolver expressionResolver = new DateMathExpressionResolver(settings);
+ Context context = new Context(this.context.getState(), this.context.getOptions(), now.getMillis());
+ List results = expressionResolver.resolve(context, Arrays.asList("<.marvel-{now/d{YYYY.MM.dd}}>"));
+ assertThat(results.size(), equalTo(1));
+ logger.info("timezone: [{}], now [{}], name: [{}]", timeZone, now, results.get(0));
+ assertThat(results.get(0), equalTo(".marvel-" + DateTimeFormat.forPattern("YYYY.MM.dd").print(now.withZone(timeZone))));
+ }
+
+ public void testExpression_CustomTimeZoneInIndexName() throws Exception {
+ DateTimeZone timeZone;
+ int hoursOffset;
+ int minutesOffset = 0;
+ if (randomBoolean()) {
+ hoursOffset = randomIntBetween(-12, 14);
+ timeZone = DateTimeZone.forOffsetHours(hoursOffset);
+ } else {
+ hoursOffset = randomIntBetween(-11, 13);
+ minutesOffset = randomIntBetween(0, 59);
+ timeZone = DateTimeZone.forOffsetHoursMinutes(hoursOffset, minutesOffset);
+ }
+ DateTime now;
+ if (hoursOffset >= 0) {
+ // rounding to next day 00:00
+ now = DateTime.now(UTC).plusHours(hoursOffset).plusMinutes(minutesOffset).withHourOfDay(0).withMinuteOfHour(0).withSecondOfMinute(0);
+ } else {
+ // rounding to today 00:00
+ now = DateTime.now(UTC).withHourOfDay(0).withMinuteOfHour(0).withSecondOfMinute(0);
+ }
+ Context context = new Context(this.context.getState(), this.context.getOptions(), now.getMillis());
+ List results = expressionResolver.resolve(context, Arrays.asList("<.marvel-{now/d{YYYY.MM.dd|" + timeZone.getID() + "}}>"));
+ assertThat(results.size(), equalTo(1));
+ logger.info("timezone: [{}], now [{}], name: [{}]", timeZone, now, results.get(0));
+ assertThat(results.get(0), equalTo(".marvel-" + DateTimeFormat.forPattern("YYYY.MM.dd").print(now.withZone(timeZone))));
+ }
+
+ @Test(expected = ElasticsearchParseException.class)
+ public void testExpression_Invalid_Unescaped() throws Exception {
+ expressionResolver.resolve(context, Arrays.asList("<.mar}vel-{now/d}>"));
+ }
+
+ @Test(expected = ElasticsearchParseException.class)
+ public void testExpression_Invalid_DateMathFormat() throws Exception {
+ expressionResolver.resolve(context, Arrays.asList("<.marvel-{now/d{}>"));
+ }
+
+ @Test(expected = ElasticsearchParseException.class)
+ public void testExpression_Invalid_EmptyDateMathFormat() throws Exception {
+ expressionResolver.resolve(context, Arrays.asList("<.marvel-{now/d{}}>"));
+ }
+
+ @Test(expected = ElasticsearchParseException.class)
+ public void testExpression_Invalid_OpenEnded() throws Exception {
+ expressionResolver.resolve(context, Arrays.asList("<.marvel-{now/d>"));
+ }
+
+}
diff --git a/core/src/test/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolverTests.java b/core/src/test/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolverTests.java
index 9ffaaa6c331..8be23e6f386 100644
--- a/core/src/test/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolverTests.java
+++ b/core/src/test/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolverTests.java
@@ -27,6 +27,7 @@ import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetaData.State;
import org.elasticsearch.common.Strings;
import org.elasticsearch.index.IndexNotFoundException;
+import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.indices.IndexClosedException;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.junit.Test;
@@ -42,6 +43,8 @@ import static org.hamcrest.Matchers.*;
*/
public class IndexNameExpressionResolverTests extends ElasticsearchTestCase {
+ private final IndexNameExpressionResolver indexNameExpressionResolver = new IndexNameExpressionResolver(Settings.EMPTY);
+
@Test
public void testIndexOptions_strict() {
MetaData.Builder mdBuilder = MetaData.builder()
@@ -51,7 +54,6 @@ public class IndexNameExpressionResolverTests extends ElasticsearchTestCase {
.put(indexBuilder("foofoo").putAlias(AliasMetaData.builder("barbaz")));
ClusterState state = ClusterState.builder(new ClusterName("_name")).metaData(mdBuilder).build();
- IndexNameExpressionResolver indexNameExpressionResolver = new IndexNameExpressionResolver();
IndicesOptions[] indicesOptions = new IndicesOptions[]{ IndicesOptions.strictExpandOpen(), IndicesOptions.strictExpand()};
for (IndicesOptions options : indicesOptions) {
@@ -141,7 +143,6 @@ public class IndexNameExpressionResolverTests extends ElasticsearchTestCase {
.put(indexBuilder("foofoo-closed").state(IndexMetaData.State.CLOSE))
.put(indexBuilder("foofoo").putAlias(AliasMetaData.builder("barbaz")));
ClusterState state = ClusterState.builder(new ClusterName("_name")).metaData(mdBuilder).build();
- IndexNameExpressionResolver indexNameExpressionResolver = new IndexNameExpressionResolver();
IndicesOptions lenientExpand = IndicesOptions.fromOptions(true, true, true, true);
IndicesOptions[] indicesOptions = new IndicesOptions[]{ IndicesOptions.lenientExpandOpen(), lenientExpand};
@@ -210,7 +211,6 @@ public class IndexNameExpressionResolverTests extends ElasticsearchTestCase {
.put(indexBuilder("foofoo-closed").state(IndexMetaData.State.CLOSE))
.put(indexBuilder("foofoo").putAlias(AliasMetaData.builder("barbaz")));
ClusterState state = ClusterState.builder(new ClusterName("_name")).metaData(mdBuilder).build();
- IndexNameExpressionResolver indexNameExpressionResolver = new IndexNameExpressionResolver();
IndicesOptions expandOpen = IndicesOptions.fromOptions(true, false, true, false);
IndicesOptions expand = IndicesOptions.fromOptions(true, false, true, true);
@@ -260,7 +260,6 @@ public class IndexNameExpressionResolverTests extends ElasticsearchTestCase {
.put(indexBuilder("bar"))
.put(indexBuilder("foobar").putAlias(AliasMetaData.builder("barbaz")));
ClusterState state = ClusterState.builder(new ClusterName("_name")).metaData(mdBuilder).build();
- IndexNameExpressionResolver indexNameExpressionResolver = new IndexNameExpressionResolver();
// Only closed
IndicesOptions options = IndicesOptions.fromOptions(false, true, false, true);
@@ -333,7 +332,6 @@ public class IndexNameExpressionResolverTests extends ElasticsearchTestCase {
.put(indexBuilder("foofoo-closed").state(IndexMetaData.State.CLOSE))
.put(indexBuilder("foofoo").putAlias(AliasMetaData.builder("barbaz")));
ClusterState state = ClusterState.builder(new ClusterName("_name")).metaData(mdBuilder).build();
- IndexNameExpressionResolver indexNameExpressionResolver = new IndexNameExpressionResolver();
//ignore unavailable and allow no indices
{
@@ -428,7 +426,6 @@ public class IndexNameExpressionResolverTests extends ElasticsearchTestCase {
.put(indexBuilder("foofoo-closed").state(IndexMetaData.State.CLOSE))
.put(indexBuilder("foofoo").putAlias(AliasMetaData.builder("barbaz")));
ClusterState state = ClusterState.builder(new ClusterName("_name")).metaData(mdBuilder).build();
- IndexNameExpressionResolver indexNameExpressionResolver = new IndexNameExpressionResolver();
//error on both unavailable and no indices + every alias needs to expand to a single index
@@ -482,7 +479,6 @@ public class IndexNameExpressionResolverTests extends ElasticsearchTestCase {
@Test
public void testIndexOptions_emptyCluster() {
ClusterState state = ClusterState.builder(new ClusterName("_name")).metaData(MetaData.builder().build()).build();
- IndexNameExpressionResolver indexNameExpressionResolver = new IndexNameExpressionResolver();
IndicesOptions options = IndicesOptions.strictExpandOpen();
IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, options);
@@ -532,7 +528,6 @@ public class IndexNameExpressionResolverTests extends ElasticsearchTestCase {
.put(indexBuilder("testXXX"))
.put(indexBuilder("kuku"));
ClusterState state = ClusterState.builder(new ClusterName("_name")).metaData(mdBuilder).build();
- IndexNameExpressionResolver indexNameExpressionResolver = new IndexNameExpressionResolver();
IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, IndicesOptions.strictExpandOpen());
indexNameExpressionResolver.concreteIndices(context, "testZZZ");
@@ -544,7 +539,6 @@ public class IndexNameExpressionResolverTests extends ElasticsearchTestCase {
.put(indexBuilder("testXXX"))
.put(indexBuilder("kuku"));
ClusterState state = ClusterState.builder(new ClusterName("_name")).metaData(mdBuilder).build();
- IndexNameExpressionResolver indexNameExpressionResolver = new IndexNameExpressionResolver();
IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen());
assertThat(newHashSet(indexNameExpressionResolver.concreteIndices(context, "testXXX", "testZZZ")), equalTo(newHashSet("testXXX")));
@@ -556,7 +550,6 @@ public class IndexNameExpressionResolverTests extends ElasticsearchTestCase {
.put(indexBuilder("testXXX"))
.put(indexBuilder("kuku"));
ClusterState state = ClusterState.builder(new ClusterName("_name")).metaData(mdBuilder).build();
- IndexNameExpressionResolver indexNameExpressionResolver = new IndexNameExpressionResolver();
IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, IndicesOptions.strictExpandOpen());
assertThat(newHashSet(indexNameExpressionResolver.concreteIndices(context, "testMo", "testMahdy")), equalTo(newHashSet("testXXX")));
@@ -568,9 +561,7 @@ public class IndexNameExpressionResolverTests extends ElasticsearchTestCase {
.put(indexBuilder("testXXX"))
.put(indexBuilder("kuku"));
ClusterState state = ClusterState.builder(new ClusterName("_name")).metaData(mdBuilder).build();
- IndexNameExpressionResolver indexNameExpressionResolver = new IndexNameExpressionResolver();
IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, IndicesOptions.lenientExpandOpen());
-
assertThat(newHashSet(indexNameExpressionResolver.concreteIndices(context, new String[]{})), equalTo(Sets.newHashSet("kuku", "testXXX")));
}
@@ -583,7 +574,6 @@ public class IndexNameExpressionResolverTests extends ElasticsearchTestCase {
.put(indexBuilder("testYYY").state(State.OPEN))
.put(indexBuilder("testYYX").state(State.OPEN));
ClusterState state = ClusterState.builder(new ClusterName("_name")).metaData(mdBuilder).build();
- IndexNameExpressionResolver indexNameExpressionResolver = new IndexNameExpressionResolver();
IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, IndicesOptions.fromOptions(true, true, false, false));
assertThat(newHashSet(indexNameExpressionResolver.concreteIndices(context, "testX*")), equalTo(new HashSet()));
@@ -615,7 +605,6 @@ public class IndexNameExpressionResolverTests extends ElasticsearchTestCase {
IndicesOptions indicesOptions = IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), randomBoolean(), randomBoolean());
ClusterState state = ClusterState.builder(new ClusterName("_name")).metaData(MetaData.builder().build()).build();
- IndexNameExpressionResolver indexNameExpressionResolver = new IndexNameExpressionResolver();
IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, indicesOptions);
// with no indices, asking for all indices should return empty list or exception, depending on indices options
@@ -633,7 +622,6 @@ public class IndexNameExpressionResolverTests extends ElasticsearchTestCase {
.put(indexBuilder("bbb").state(State.OPEN).putAlias(AliasMetaData.builder("bbb_alias1")))
.put(indexBuilder("ccc").state(State.CLOSE).putAlias(AliasMetaData.builder("ccc_alias1")));
state = ClusterState.builder(new ClusterName("_name")).metaData(mdBuilder).build();
- indexNameExpressionResolver = new IndexNameExpressionResolver();
context = new IndexNameExpressionResolver.Context(state, indicesOptions);
if (indicesOptions.expandWildcardsOpen() || indicesOptions.expandWildcardsClosed() || indicesOptions.allowNoIndices()) {
String[] concreteIndices = indexNameExpressionResolver.concreteIndices(context, allIndices);
@@ -676,7 +664,6 @@ public class IndexNameExpressionResolverTests extends ElasticsearchTestCase {
.put(indexBuilder("bbb").state(State.OPEN).putAlias(AliasMetaData.builder("bbb_alias1")))
.put(indexBuilder("ccc").state(State.CLOSE).putAlias(AliasMetaData.builder("ccc_alias1")));
ClusterState state = ClusterState.builder(new ClusterName("_name")).metaData(mdBuilder).build();
- IndexNameExpressionResolver indexNameExpressionResolver = new IndexNameExpressionResolver();
IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, indicesOptions);
// asking for non existing wildcard pattern should return empty list or exception
@@ -760,7 +747,6 @@ public class IndexNameExpressionResolverTests extends ElasticsearchTestCase {
//even though it does identify all indices, it's not a pattern but just an explicit list of them
String[] concreteIndices = new String[]{"index1", "index2", "index3"};
MetaData metaData = metaDataBuilder(concreteIndices);
- IndexNameExpressionResolver indexNameExpressionResolver = new IndexNameExpressionResolver();
assertThat(indexNameExpressionResolver.isPatternMatchingAllIndices(metaData, concreteIndices, concreteIndices), equalTo(false));
}
@@ -769,7 +755,6 @@ public class IndexNameExpressionResolverTests extends ElasticsearchTestCase {
String[] indicesOrAliases = new String[]{"*"};
String[] concreteIndices = new String[]{"index1", "index2", "index3"};
MetaData metaData = metaDataBuilder(concreteIndices);
- IndexNameExpressionResolver indexNameExpressionResolver = new IndexNameExpressionResolver();
assertThat(indexNameExpressionResolver.isPatternMatchingAllIndices(metaData, indicesOrAliases, concreteIndices), equalTo(true));
}
@@ -778,7 +763,6 @@ public class IndexNameExpressionResolverTests extends ElasticsearchTestCase {
String[] indicesOrAliases = new String[]{"index*"};
String[] concreteIndices = new String[]{"index1", "index2", "index3"};
MetaData metaData = metaDataBuilder(concreteIndices);
- IndexNameExpressionResolver indexNameExpressionResolver = new IndexNameExpressionResolver();
assertThat(indexNameExpressionResolver.isPatternMatchingAllIndices(metaData, indicesOrAliases, concreteIndices), equalTo(true));
}
@@ -788,7 +772,6 @@ public class IndexNameExpressionResolverTests extends ElasticsearchTestCase {
String[] concreteIndices = new String[]{"index1", "index2", "index3"};
String[] allConcreteIndices = new String[]{"index1", "index2", "index3", "a", "b"};
MetaData metaData = metaDataBuilder(allConcreteIndices);
- IndexNameExpressionResolver indexNameExpressionResolver = new IndexNameExpressionResolver();
assertThat(indexNameExpressionResolver.isPatternMatchingAllIndices(metaData, indicesOrAliases, concreteIndices), equalTo(false));
}
@@ -797,7 +780,6 @@ public class IndexNameExpressionResolverTests extends ElasticsearchTestCase {
String[] indicesOrAliases = new String[]{"-index1", "+index1"};
String[] concreteIndices = new String[]{"index1", "index2", "index3"};
MetaData metaData = metaDataBuilder(concreteIndices);
- IndexNameExpressionResolver indexNameExpressionResolver = new IndexNameExpressionResolver();
assertThat(indexNameExpressionResolver.isPatternMatchingAllIndices(metaData, indicesOrAliases, concreteIndices), equalTo(true));
}
@@ -807,7 +789,6 @@ public class IndexNameExpressionResolverTests extends ElasticsearchTestCase {
String[] concreteIndices = new String[]{"index2", "index3"};
String[] allConcreteIndices = new String[]{"index1", "index2", "index3"};
MetaData metaData = metaDataBuilder(allConcreteIndices);
- IndexNameExpressionResolver indexNameExpressionResolver = new IndexNameExpressionResolver();
assertThat(indexNameExpressionResolver.isPatternMatchingAllIndices(metaData, indicesOrAliases, concreteIndices), equalTo(false));
}
@@ -816,7 +797,6 @@ public class IndexNameExpressionResolverTests extends ElasticsearchTestCase {
String[] indicesOrAliases = new String[]{"index*", "-index1", "+index1"};
String[] concreteIndices = new String[]{"index1", "index2", "index3"};
MetaData metaData = metaDataBuilder(concreteIndices);
- IndexNameExpressionResolver indexNameExpressionResolver = new IndexNameExpressionResolver();
assertThat(indexNameExpressionResolver.isPatternMatchingAllIndices(metaData, indicesOrAliases, concreteIndices), equalTo(true));
}
@@ -826,7 +806,6 @@ public class IndexNameExpressionResolverTests extends ElasticsearchTestCase {
String[] concreteIndices = new String[]{"index2", "index3"};
String[] allConcreteIndices = new String[]{"index1", "index2", "index3"};
MetaData metaData = metaDataBuilder(allConcreteIndices);
- IndexNameExpressionResolver indexNameExpressionResolver = new IndexNameExpressionResolver();
assertThat(indexNameExpressionResolver.isPatternMatchingAllIndices(metaData, indicesOrAliases, concreteIndices), equalTo(false));
}
@@ -837,7 +816,6 @@ public class IndexNameExpressionResolverTests extends ElasticsearchTestCase {
.put(indexBuilder("foo2-closed").state(IndexMetaData.State.CLOSE).putAlias(AliasMetaData.builder("foobar2-closed")))
.put(indexBuilder("foo3").putAlias(AliasMetaData.builder("foobar2-closed")));
ClusterState state = ClusterState.builder(new ClusterName("_name")).metaData(mdBuilder).build();
- IndexNameExpressionResolver indexNameExpressionResolver = new IndexNameExpressionResolver();
IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, IndicesOptions.strictExpandOpenAndForbidClosed());
try {
diff --git a/core/src/test/java/org/elasticsearch/indices/DateMathIndexExpressionsIntegrationTests.java b/core/src/test/java/org/elasticsearch/indices/DateMathIndexExpressionsIntegrationTests.java
new file mode 100644
index 00000000000..42fd8fa2736
--- /dev/null
+++ b/core/src/test/java/org/elasticsearch/indices/DateMathIndexExpressionsIntegrationTests.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.indices;
+
+import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse;
+import org.elasticsearch.action.delete.DeleteResponse;
+import org.elasticsearch.action.get.GetResponse;
+import org.elasticsearch.action.search.SearchResponse;
+import org.elasticsearch.test.ElasticsearchIntegrationTest;
+import org.elasticsearch.test.hamcrest.ElasticsearchAssertions;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.format.DateTimeFormat;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+
+public class DateMathIndexExpressionsIntegrationTests extends ElasticsearchIntegrationTest {
+
+ public void testIndexNameDateMathExpressions() {
+ DateTime now = new DateTime(DateTimeZone.UTC);
+ String index1 = ".marvel-" + DateTimeFormat.forPattern("YYYY.MM.dd").print(now);
+ String index2 = ".marvel-" + DateTimeFormat.forPattern("YYYY.MM.dd").print(now.minusDays(1));
+ String index3 = ".marvel-" + DateTimeFormat.forPattern("YYYY.MM.dd").print(now.minusDays(2));
+ createIndex(index1, index2, index3);
+
+ String dateMathExp1 = "<.marvel-{now/d}>";
+ String dateMathExp2 = "<.marvel-{now/d-1d}>";
+ String dateMathExp3 = "<.marvel-{now/d-2d}>";
+ client().prepareIndex(dateMathExp1, "type", "1").setSource("{}").get();
+ client().prepareIndex(dateMathExp2, "type", "2").setSource("{}").get();
+ client().prepareIndex(dateMathExp3, "type", "3").setSource("{}").get();
+ refresh();
+
+ SearchResponse searchResponse = client().prepareSearch(dateMathExp1, dateMathExp2, dateMathExp3).get();
+ ElasticsearchAssertions.assertHitCount(searchResponse, 3);
+ ElasticsearchAssertions.assertSearchHits(searchResponse, "1", "2", "3");
+
+ GetResponse getResponse = client().prepareGet(dateMathExp1, "type", "1").get();
+ assertThat(getResponse.isExists(), is(true));
+ assertThat(getResponse.getId(), equalTo("1"));
+
+ getResponse = client().prepareGet(dateMathExp2, "type", "2").get();
+ assertThat(getResponse.isExists(), is(true));
+ assertThat(getResponse.getId(), equalTo("2"));
+
+ getResponse = client().prepareGet(dateMathExp3, "type", "3").get();
+ assertThat(getResponse.isExists(), is(true));
+ assertThat(getResponse.getId(), equalTo("3"));
+
+ IndicesStatsResponse indicesStatsResponse = client().admin().indices().prepareStats(dateMathExp1, dateMathExp2, dateMathExp3).get();
+ assertThat(indicesStatsResponse.getIndex(index1), notNullValue());
+ assertThat(indicesStatsResponse.getIndex(index2), notNullValue());
+ assertThat(indicesStatsResponse.getIndex(index3), notNullValue());
+
+ DeleteResponse deleteResponse = client().prepareDelete(dateMathExp1, "type", "1").get();
+ assertThat(deleteResponse.isFound(), equalTo(true));
+ assertThat(deleteResponse.getId(), equalTo("1"));
+
+ deleteResponse = client().prepareDelete(dateMathExp2, "type", "2").get();
+ assertThat(deleteResponse.isFound(), equalTo(true));
+ assertThat(deleteResponse.getId(), equalTo("2"));
+
+ deleteResponse = client().prepareDelete(dateMathExp3, "type", "3").get();
+ assertThat(deleteResponse.isFound(), equalTo(true));
+ assertThat(deleteResponse.getId(), equalTo("3"));
+ }
+
+}
diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/HDRPercentileRanksTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/HDRPercentileRanksTests.java
index 989132a6c9c..2048949a862 100644
--- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/HDRPercentileRanksTests.java
+++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/HDRPercentileRanksTests.java
@@ -20,7 +20,6 @@ package org.elasticsearch.search.aggregations.metrics;
import com.google.common.collect.Lists;
-import org.apache.lucene.util.LuceneTestCase.AwaitsFix;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.script.Script;
@@ -51,7 +50,6 @@ import static org.hamcrest.Matchers.sameInstance;
/**
*
*/
-@AwaitsFix(bugUrl = "single test methods fail with occassional seeds (see HDRPercentilesTests.testScript_ExplicitSingleValued_WithParams for example) but only if run as a whole test class not if run as a single test method")
public class HDRPercentileRanksTests extends AbstractNumericTests {
private static double[] randomPercents(long minValue, long maxValue) {
@@ -372,7 +370,6 @@ public class HDRPercentileRanksTests extends AbstractNumericTests {
@Override
@Test
- @AwaitsFix(bugUrl="Fails with seed: B75FCDC119D90BBE, Colin to fix")
public void testScript_SingleValued_WithParams() throws Exception {
int sigDigits = randomSignificantDigits();
Map params = new HashMap<>();
diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/HDRPercentilesTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/HDRPercentilesTests.java
index c5588c06ce3..a131933ecb3 100644
--- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/HDRPercentilesTests.java
+++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/HDRPercentilesTests.java
@@ -20,7 +20,6 @@ package org.elasticsearch.search.aggregations.metrics;
import com.google.common.collect.Lists;
-import org.apache.lucene.util.LuceneTestCase.AwaitsFix;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.script.Script;
@@ -52,7 +51,6 @@ import static org.hamcrest.Matchers.sameInstance;
/**
*
*/
-@AwaitsFix(bugUrl = "single test methods fail with occassional seeds (see testScript_ExplicitSingleValued_WithParams for example) but only if run as a whole test class not if run as a single test method")
public class HDRPercentilesTests extends AbstractNumericTests {
private static double[] randomPercentiles() {
@@ -379,7 +377,6 @@ public class HDRPercentilesTests extends AbstractNumericTests {
@Override
@Test
- @AwaitsFix(bugUrl = "fails with -Dtests.seed=5BFFA768633A0A59 but only if run as a whole test class not if run as a single test method")
public void testScript_ExplicitSingleValued_WithParams() throws Exception {
Map params = new HashMap<>();
params.put("dec", 1);
diff --git a/core/src/test/resources/org/elasticsearch/bwcompat/index-1.6.2.zip b/core/src/test/resources/org/elasticsearch/bwcompat/index-1.6.2.zip
new file mode 100644
index 00000000000..af6ce561fa6
Binary files /dev/null and b/core/src/test/resources/org/elasticsearch/bwcompat/index-1.6.2.zip differ
diff --git a/core/src/test/resources/org/elasticsearch/bwcompat/index-1.7.1.zip b/core/src/test/resources/org/elasticsearch/bwcompat/index-1.7.1.zip
new file mode 100644
index 00000000000..debd797162b
Binary files /dev/null and b/core/src/test/resources/org/elasticsearch/bwcompat/index-1.7.1.zip differ
diff --git a/core/src/test/resources/org/elasticsearch/bwcompat/repo-1.6.2.zip b/core/src/test/resources/org/elasticsearch/bwcompat/repo-1.6.2.zip
new file mode 100644
index 00000000000..de4c5be7928
Binary files /dev/null and b/core/src/test/resources/org/elasticsearch/bwcompat/repo-1.6.2.zip differ
diff --git a/core/src/test/resources/org/elasticsearch/bwcompat/repo-1.7.1.zip b/core/src/test/resources/org/elasticsearch/bwcompat/repo-1.7.1.zip
new file mode 100644
index 00000000000..cd5c6c07c98
Binary files /dev/null and b/core/src/test/resources/org/elasticsearch/bwcompat/repo-1.7.1.zip differ
diff --git a/dev-tools/src/main/resources/ant/integration-tests.xml b/dev-tools/src/main/resources/ant/integration-tests.xml
index 0c67009dbdb..b190bca7476 100644
--- a/dev-tools/src/main/resources/ant/integration-tests.xml
+++ b/dev-tools/src/main/resources/ant/integration-tests.xml
@@ -141,7 +141,7 @@
-
+
run
+
+ true
@@ -336,31 +338,6 @@
-
- release
-
-
- package.rpm
- true
-
-
-
-
-
- org.codehaus.mojo
- rpm-maven-plugin
-
-
- attach-rpm
-
- attached-rpm
-
-
-
-
-
-
-
sign-rpm
diff --git a/distribution/zip/pom.xml b/distribution/zip/pom.xml
index 1ca8ed117c0..462b967b380 100644
--- a/distribution/zip/pom.xml
+++ b/distribution/zip/pom.xml
@@ -98,6 +98,19 @@
+
+
+ execute
+ package
+
+ run
+
+
+
+
+
+
+
integ-setup
diff --git a/docs/reference/api-conventions.asciidoc b/docs/reference/api-conventions.asciidoc
index a1f4f66f59b..f7fbde44d81 100644
--- a/docs/reference/api-conventions.asciidoc
+++ b/docs/reference/api-conventions.asciidoc
@@ -9,6 +9,7 @@ The conventions listed in this chapter can be applied throughout the REST
API, unless otherwise specified.
* <>
+* <>
* <>
--
@@ -55,6 +56,76 @@ The defaults settings for the above parameters depend on the api being used.
NOTE: Single index APIs such as the <> and the
<> do not support multiple indices.
+[[date-math-index-names]]
+== Date math support in index names
+
+Date math index name resolution enables you to search a range of time-series indices, rather
+than searching all of your time-series indices and filtering the results or maintaining aliases.
+Limiting the number of indices that are searched reduces the load on the cluster and improves
+execution performance. For example, if you are searching for errors in your
+daily logs, you can use a date math name template to restrict the search to the past
+two days.
+
+Almost all APIs that have an `index` parameter, support date math in the `index` parameter
+value.
+
+A date math index name takes the following form:
+
+[source,txt]
+----------------------------------------------------------------------
+
+----------------------------------------------------------------------
+
+Where:
+
+[horizontal]
+`static_name`:: is the static text part of the name
+`date_math_expr`:: is a dynamic date math expression that computes the date dynamically
+`date_format`:: is the optional format in which the computed date should be rendered. Defaults to `YYYY.MM.dd`.
+`time_zone`:: is the optional time zone . Defaults to `utc`.
+
+You must enclose date math index name expressions within angle brackets. For example:
+
+[source,js]
+----------------------------------------------------------------------
+curl -XGET 'localhost:9200//_search' {
+ "query" : {
+ ...
+ }
+}
+----------------------------------------------------------------------
+
+The following example shows different forms of date math index names and the final index names
+they resolve to given the current time is 22rd March 2024 noon utc.
+
+[options="header"]
+|======
+| Expression |Resolves to
+| `` | `logstash-2024.03.22`
+| `` | `logstash-2024.03.01`
+| `` | `logstash-2024.03`
+| `` | `logstash-2024.02`
+| `` resolves to `elastic{ON}-2024.03.01`
+
+The following example shows a search request that searches the Logstash indices for the past
+three days, assuming the indices use the default Logstash index name format,
+`logstash-YYYY.MM.dd`.
+
+[source,js]
+----------------------------------------------------------------------
+curl -XGET 'localhost:9200/,,/_search' {
+ "query" : {
+ ...
+ }
+}
+----------------------------------------------------------------------
+
[[common-options]]
== Common options
diff --git a/docs/reference/cat.asciidoc b/docs/reference/cat.asciidoc
index e5f3d967498..0e61c27618d 100644
--- a/docs/reference/cat.asciidoc
+++ b/docs/reference/cat.asciidoc
@@ -112,6 +112,8 @@ include::cat/indices.asciidoc[]
include::cat/master.asciidoc[]
+include::cat/nodeattrs.asciidoc[]
+
include::cat/nodes.asciidoc[]
include::cat/pending_tasks.asciidoc[]
diff --git a/docs/reference/cat/nodeattrs.asciidoc b/docs/reference/cat/nodeattrs.asciidoc
new file mode 100644
index 00000000000..f6494e547cc
--- /dev/null
+++ b/docs/reference/cat/nodeattrs.asciidoc
@@ -0,0 +1,71 @@
+[[cat-nodeattrs]]
+== cat nodeattrs
+
+The `nodeattrs` command shows custom node attributes.
+
+["source","sh",subs="attributes,callouts"]
+--------------------------------------------------
+% curl 192.168.56.10:9200/_cat/nodeattrs
+node host ip attr value
+Black Bolt epsilon 192.168.1.8 rack rack314
+Black Bolt epsilon 192.168.1.8 azone us-east-1
+--------------------------------------------------
+
+The first few columns give you basic info per node.
+
+
+["source","sh",subs="attributes,callouts"]
+--------------------------------------------------
+node host ip
+Black Bolt epsilon 192.168.1.8
+Black Bolt epsilon 192.168.1.8
+--------------------------------------------------
+
+
+The attr and value columns can give you a picture of custom node attributes.
+
+[source,sh]
+--------------------------------------------------
+attr value
+rack rack314
+azone us-east-1
+--------------------------------------------------
+
+[float]
+=== Columns
+
+Below is an exhaustive list of the existing headers that can be
+passed to `nodes?h=` to retrieve the relevant details in ordered
+columns. If no headers are specified, then those marked to Appear
+by Default will appear. If any header is specified, then the defaults
+are not used.
+
+Aliases can be used in place of the full header name for brevity.
+Columns appear in the order that they are listed below unless a
+different order is specified (e.g., `h=attr,value` versus `h=value,attr`).
+
+When specifying headers, the headers are not placed in the output
+by default. To have the headers appear in the output, use verbose
+mode (`v`). The header name will match the supplied value (e.g.,
+`pid` versus `p`). For example:
+
+["source","sh",subs="attributes,callouts"]
+--------------------------------------------------
+% curl 192.168.56.10:9200/_cat/nodeattrs?v&h=name,pid,attr,value
+name pid attr value
+Black Bolt 28000 rack rack314
+Black Bolt 28000 azone us-east-1
+--------------------------------------------------
+
+[cols="<,<,<,<,<",options="header",subs="normal"]
+|=======================================================================
+|Header |Alias |Appear by Default |Description |Example
+|`node`|`name`|Yes|Name of the node|Black Bolt
+|`id` |`nodeId` |No |Unique node ID |k0zy
+|`pid` |`p` |No |Process ID |13061
+|`host` |`h` |Yes |Host name |n1
+|`ip` |`i` |Yes |IP address |127.0.1.1
+|`port` |`po` |No |Bound transport port |9300
+|`attr` | `attr.name` | Yes | Attribute name | rack
+|`value` | `attr.value` | Yes | Attribute value | rack123
+|=======================================================================
diff --git a/pom.xml b/pom.xml
index 21158cdfd88..8092781a4cf 100644
--- a/pom.xml
+++ b/pom.xml
@@ -397,7 +397,7 @@
org.hdrhistogram
HdrHistogram
- 2.1.5
+ 2.1.6
@@ -587,7 +587,12 @@
warn
true
${tests.jvms}
-
+
+
diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cat.nodeattrs.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cat.nodeattrs.json
new file mode 100644
index 00000000000..157a33f0408
--- /dev/null
+++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cat.nodeattrs.json
@@ -0,0 +1,37 @@
+{
+ "cat.nodeattrs": {
+ "documentation": "http://www.elastic.co/guide/en/elasticsearch/reference/master/cat-nodeattrs.html",
+ "methods": ["GET"],
+ "url": {
+ "path": "/_cat/nodeattrs",
+ "paths": ["/_cat/nodeattrs"],
+ "parts": {
+ },
+ "params": {
+ "local": {
+ "type" : "boolean",
+ "description" : "Return local information, do not retrieve the state from master node (default: false)"
+ },
+ "master_timeout": {
+ "type" : "time",
+ "description" : "Explicit operation timeout for connection to master node"
+ },
+ "h": {
+ "type": "list",
+ "description" : "Comma-separated list of column names to display"
+ },
+ "help": {
+ "type": "boolean",
+ "description": "Return help information",
+ "default": false
+ },
+ "v": {
+ "type": "boolean",
+ "description": "Verbose mode. Display column headers",
+ "default": true
+ }
+ }
+ },
+ "body": null
+ }
+}
diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.nodeattrs/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.nodeattrs/10_basic.yaml
new file mode 100755
index 00000000000..73d970fb27a
--- /dev/null
+++ b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.nodeattrs/10_basic.yaml
@@ -0,0 +1,27 @@
+---
+"Test cat nodes attrs output":
+
+ - do:
+ cat.nodeattrs:
+ v: false
+
+ - match:
+ $body: |
+ /((\S+)\s+(\S+)\s+(\d{1,3}\.){3}\d{1,3}\s+(\S+)\s+(\S+)\s*)+/
+
+ - do:
+ cat.nodeattrs:
+ v: true
+
+ - match:
+ $body: |
+ /((\S+)\s+(\S+)\s+(\d{1,3}\.){3}\d{1,3}\s+(\S+)\s+(\S+)\s*)+/
+
+ - do:
+ cat.nodeattrs:
+ h: attr,value
+ v: true
+
+ - match:
+ $body: |
+ /((\S+)\s+(\S+)\s*)+/
diff --git a/run.sh b/run.sh
new file mode 100755
index 00000000000..4e8ac0dad08
--- /dev/null
+++ b/run.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+#
+# build zip package, but ensuring its from the current source
+# turn off tests and other validation to speed it up
+# TODO: can be sped up more, if shading is moved out of core/
+# TODO: this will work on windows too. feel free to make a .bat
+mvn -am -pl dev-tools,distribution/zip package -DskipTests -Drun -Pdev