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