Merge branch 'master' into feature/query-refactoring

This commit is contained in:
Christoph Büscher 2015-07-30 11:33:53 +02:00
commit af75117845
41 changed files with 979 additions and 111 deletions

View File

@ -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.

View File

@ -438,26 +438,6 @@
</execution>
</executions>
</plugin>
<!-- execution and integration tests -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<!-- start up elasticsearch in foreground -->
<execution>
<id>execute</id>
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target if="${run}">
<ant antfile="${elasticsearch.integ.antfile}" target="start-foreground"/>
</target>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<pluginManagement>
<plugins>

View File

@ -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:

View File

@ -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);

View File

@ -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 <code>null</code>
* is specified then whether the operation will be realtime depends on the api of the concrete request
* subclass.
*/
<R extends RealtimeRequest> R realtime(Boolean realtime);
}

View File

@ -74,13 +74,14 @@ public class TransportBulkAction extends HandledTransportAction<BulkRequest, Bul
@Inject
public TransportBulkAction(Settings settings, ThreadPool threadPool, TransportService transportService, ClusterService clusterService,
TransportShardBulkAction shardBulkAction, TransportCreateIndexAction createIndexAction,
ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver) {
ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver,
AutoCreateIndex autoCreateIndex) {
super(settings, BulkAction.NAME, threadPool, transportService, actionFilters, indexNameExpressionResolver, BulkRequest.class);
this.clusterService = clusterService;
this.shardBulkAction = shardBulkAction;
this.createIndexAction = createIndexAction;
this.autoCreateIndex = new AutoCreateIndex(settings);
this.autoCreateIndex = autoCreateIndex;
this.allowIdGeneration = this.settings.getAsBoolean("action.bulk.action.allow_id_generation", true);
}

View File

@ -60,17 +60,19 @@ public class TransportDeleteAction extends TransportReplicationAction<DeleteRequ
public TransportDeleteAction(Settings settings, TransportService transportService, ClusterService clusterService,
IndicesService indicesService, ThreadPool threadPool, ShardStateAction shardStateAction,
TransportCreateIndexAction createIndexAction, ActionFilters actionFilters,
IndexNameExpressionResolver indexNameExpressionResolver, MappingUpdatedAction mappingUpdatedAction) {
IndexNameExpressionResolver indexNameExpressionResolver, MappingUpdatedAction mappingUpdatedAction,
AutoCreateIndex autoCreateIndex) {
super(settings, DeleteAction.NAME, transportService, clusterService, indicesService, threadPool, shardStateAction,
mappingUpdatedAction, actionFilters, indexNameExpressionResolver,
DeleteRequest.class, DeleteRequest.class, ThreadPool.Names.INDEX);
this.createIndexAction = createIndexAction;
this.autoCreateIndex = new AutoCreateIndex(settings);
this.autoCreateIndex = autoCreateIndex;
}
@Override
protected void doExecute(final DeleteRequest request, final ActionListener<DeleteResponse> 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<CreateIndexResponse>() {
@Override
public void onResponse(CreateIndexResponse result) {

View File

@ -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<GetRequest> {
public class GetRequest extends SingleShardRequest<GetRequest> implements RealtimeRequest {
private String type;
private String id;
@ -244,6 +245,7 @@ public class GetRequest extends SingleShardRequest<GetRequest> {
return this.realtime == null ? true : this.realtime;
}
@Override
public GetRequest realtime(Boolean realtime) {
this.realtime = realtime;
return this;

View File

@ -42,7 +42,7 @@ import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
public class MultiGetRequest extends ActionRequest<MultiGetRequest> implements Iterable<MultiGetRequest.Item>, CompositeIndicesRequest {
public class MultiGetRequest extends ActionRequest<MultiGetRequest> implements Iterable<MultiGetRequest.Item>, CompositeIndicesRequest, RealtimeRequest {
/**
* A single get item.
@ -319,6 +319,7 @@ public class MultiGetRequest extends ActionRequest<MultiGetRequest> implements I
return this.realtime == null ? true : this.realtime;
}
@Override
public MultiGetRequest realtime(Boolean realtime) {
this.realtime = realtime;
return this;

View File

@ -75,11 +75,12 @@ public class TransportIndexAction extends TransportReplicationAction<IndexReques
public TransportIndexAction(Settings settings, TransportService transportService, ClusterService clusterService,
IndicesService indicesService, ThreadPool threadPool, ShardStateAction shardStateAction,
TransportCreateIndexAction createIndexAction, MappingUpdatedAction mappingUpdatedAction,
ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver) {
ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver,
AutoCreateIndex autoCreateIndex) {
super(settings, IndexAction.NAME, transportService, clusterService, indicesService, threadPool, shardStateAction, mappingUpdatedAction,
actionFilters, indexNameExpressionResolver, IndexRequest.class, IndexRequest.class, ThreadPool.Names.INDEX);
this.createIndexAction = createIndexAction;
this.autoCreateIndex = new AutoCreateIndex(settings);
this.autoCreateIndex = autoCreateIndex;
this.allowIdGeneration = settings.getAsBoolean("action.allow_id_generation", true);
this.clusterService = clusterService;
}
@ -87,7 +88,8 @@ public class TransportIndexAction extends TransportReplicationAction<IndexReques
@Override
protected void doExecute(final IndexRequest request, final ActionListener<IndexResponse> 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());

View File

@ -115,7 +115,10 @@ public abstract class TransportSearchTypeAction extends TransportAction<SearchRe
clusterState.blocks().globalBlockedRaiseException(ClusterBlockLevel.READ);
String[] concreteIndices = indexNameExpressionResolver.concreteIndices(clusterState, request.indicesOptions(), request.indices());
// TODO: I think startTime() should become part of ActionRequest and that should be used both for index name
// date math expressions and $now in scripts. This way all apis will deal with now in the same way instead
// of just for the _search api
String[] concreteIndices = indexNameExpressionResolver.concreteIndices(clusterState, request.indicesOptions(), startTime(), request.indices());
for (String index : concreteIndices) {
clusterState.blocks().indexBlockedRaiseException(ClusterBlockLevel.READ, index);

View File

@ -20,21 +20,28 @@
package org.elasticsearch.action.support;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.common.Booleans;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.regex.Regex;
import org.elasticsearch.common.settings.Settings;
/**
* Encapsulates the logic of whether a new index should be automatically created when
* a write operation is about to happen in a non existing index.
*/
public class AutoCreateIndex {
public final class AutoCreateIndex {
private final boolean needToCheck;
private final boolean globallyDisabled;
private final String[] matches;
private final String[] matches2;
private final IndexNameExpressionResolver resolver;
public AutoCreateIndex(Settings settings) {
@Inject
public AutoCreateIndex(Settings settings, IndexNameExpressionResolver resolver) {
this.resolver = resolver;
String value = settings.get("action.auto_create_index");
if (value == null || Booleans.isExplicitTrue(value)) {
needToCheck = true;
@ -71,7 +78,8 @@ public class AutoCreateIndex {
if (!needToCheck) {
return false;
}
if (state.metaData().hasConcreteIndex(index)) {
boolean exists = resolver.hasIndexOrAlias(index, state);
if (exists) {
return false;
}
if (globallyDisabled) {

View File

@ -32,7 +32,7 @@ import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
import java.util.*;
public class MultiTermVectorsRequest extends ActionRequest<MultiTermVectorsRequest> implements Iterable<TermVectorsRequest>, CompositeIndicesRequest {
public class MultiTermVectorsRequest extends ActionRequest<MultiTermVectorsRequest> implements Iterable<TermVectorsRequest>, CompositeIndicesRequest, RealtimeRequest {
String preference;
List<TermVectorsRequest> requests = new ArrayList<>();
@ -162,4 +162,12 @@ public class MultiTermVectorsRequest extends ActionRequest<MultiTermVectorsReque
public int size() {
return requests.size();
}
@Override
public MultiTermVectorsRequest realtime(Boolean realtime) {
for (TermVectorsRequest request : requests) {
request.realtime(realtime);
}
return this;
}
}

View File

@ -25,6 +25,7 @@ import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.DocumentRequest;
import org.elasticsearch.action.RealtimeRequest;
import org.elasticsearch.action.ValidateActions;
import org.elasticsearch.action.get.MultiGetRequest;
import org.elasticsearch.action.support.single.shard.SingleShardRequest;
@ -50,7 +51,7 @@ import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
* Note, the {@link #index()}, {@link #type(String)} and {@link #id(String)} are
* required.
*/
public class TermVectorsRequest extends SingleShardRequest<TermVectorsRequest> implements DocumentRequest<TermVectorsRequest> {
public class TermVectorsRequest extends SingleShardRequest<TermVectorsRequest> implements DocumentRequest<TermVectorsRequest>, RealtimeRequest {
private String type;
@ -405,6 +406,7 @@ public class TermVectorsRequest extends SingleShardRequest<TermVectorsRequest> i
/**
* Choose whether term vectors be generated real-time.
*/
@Override
public TermVectorsRequest realtime(Boolean realtime) {
this.realtime = realtime;
return this;

View File

@ -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

View File

@ -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<ExpressionResolver> expressionResolvers;
private DateMathExpressionResolver dateMathExpressionResolver;
@Inject
public IndexNameExpressionResolver() {
expressionResolvers = ImmutableList.<ExpressionResolver>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<String> resolve(final Context context, List<String> expressions) {
List<String> 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<Long>() {
@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();
}
}
}

View File

@ -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();
}

View File

@ -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<ClusterStateResponse>(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<NodesInfoResponse>(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<NodesStatsResponse>(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<String, String> 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;
}
}

View File

@ -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<ActionFilter>()), new IndexNameExpressionResolver(), Request.class, Request.class, ThreadPool.Names.SAME);
new ActionFilters(new HashSet<ActionFilter>()), new IndexNameExpressionResolver(Settings.EMPTY), Request.class, Request.class, ThreadPool.Names.SAME);
}
@Override

View File

@ -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()));

View File

@ -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<String> indexExpressions = new ArrayList<>(numIndexExpressions);
for (int i = 0; i < numIndexExpressions; i++) {
indexExpressions.add(randomAsciiOfLength(10));
}
List<String> 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<String> indexExpressions = Arrays.asList("<.marvel-{now}>", "<.watch_history-{now}>", "<logstash-{now}>");
List<String> 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<String> result = expressionResolver.resolve(context, Collections.<String>emptyList());
assertThat(result.size(), equalTo(0));
}
public void testExpression_Static() throws Exception {
List<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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>"));
}
}

View File

@ -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<String>()));
@ -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 {

View File

@ -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"));
}
}

View File

@ -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<String, Object> params = new HashMap<>();

View File

@ -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<String, Object> params = new HashMap<>();
params.put("dec", 1);

View File

@ -141,7 +141,7 @@
<!-- TODO: doesn't belong here, but we will figure it out -->
<target name="start-foreground" depends="stop-external-cluster">
<delete dir="${integ.scratch}"/>
<unzip src="${project.build.directory}/releases/${project.artifactId}-${project.version}.zip" dest="${integ.scratch}"/>
<unzip src="${project.build.directory}/releases/elasticsearch-${project.version}.zip" dest="${integ.scratch}"/>
<local name="home"/>
<property name="home" location="${integ.scratch}/elasticsearch-${elasticsearch.version}"/>
<run-script dir="${home}" script="bin/elasticsearch" spawn="false"

View File

@ -1 +0,0 @@
d1831874fb3c769fd126c4826e69e6b40c703ee0

View File

@ -0,0 +1 @@
7495feb7f71ee124bd2a7e7d83590e296d71d80e

View File

@ -315,6 +315,8 @@
<goal>run</goal>
</goals>
<configuration>
<!-- https://github.com/elastic/elasticsearch/issues/12528 -->
<skip>true</skip>
<target>
<condition property="licenses.exists">
<available file="${basedir}/../licenses" type="dir"/>
@ -336,31 +338,6 @@
</build>
<profiles>
<profile>
<id>release</id>
<activation>
<property>
<name>package.rpm</name>
<value>true</value>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>rpm-maven-plugin</artifactId>
<executions>
<execution>
<id>attach-rpm</id>
<goals>
<goal>attached-rpm</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>sign-rpm</id>
<activation>

View File

@ -98,6 +98,19 @@
</configuration>
</execution>
<!-- execution and integration tests -->
<!-- start up elasticsearch in foreground -->
<execution>
<id>execute</id>
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target if="${run}">
<ant antfile="${elasticsearch.integ.antfile}" target="start-foreground"/>
</target>
</configuration>
</execution>
<!-- start up external cluster -->
<execution>
<id>integ-setup</id>

View File

@ -9,6 +9,7 @@ The conventions listed in this chapter can be applied throughout the REST
API, unless otherwise specified.
* <<multi-index>>
* <<date-math-index-names>>
* <<common-options>>
--
@ -55,6 +56,76 @@ The defaults settings for the above parameters depend on the api being used.
NOTE: Single index APIs such as the <<docs>> and the
<<indices-aliases,single-index `alias` APIs>> 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]
----------------------------------------------------------------------
<static_name{date_math_expr{date_format|time_zone}}>
----------------------------------------------------------------------
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/<logstash-{now/d-2d}>/_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-{now/d}>` | `logstash-2024.03.22`
| `<logstash-{now/M}>` | `logstash-2024.03.01`
| `<logstash-{now/M{YYYY.MM}}>` | `logstash-2024.03`
| `<logstash-{now/M-1M{YYYY.MM}}>` | `logstash-2024.02`
| `<logstash-{now/d{YYYY.MM.dd\|+12:00}}` | `logstash-2024.03.23`
|======
To use the characters `{` and `}` in the static part of an index name template, escape them
with a backslash `\`, for example:
* `<elastic\\{ON\\}-{now/M}>` 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/<logstash-{now/d-2d}>,<logstash-{now/d-1d}>,<logstash-{now/d}>/_search' {
"query" : {
...
}
}
----------------------------------------------------------------------
[[common-options]]
== Common options

View File

@ -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[]

View File

@ -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
|=======================================================================

View File

@ -397,7 +397,7 @@
<dependency>
<groupId>org.hdrhistogram</groupId>
<artifactId>HdrHistogram</artifactId>
<version>2.1.5</version>
<version>2.1.6</version>
</dependency>
<dependency>
@ -587,7 +587,12 @@
<jvmOutputAction>warn</jvmOutputAction>
<leaveTemporary>true</leaveTemporary>
<parallelism>${tests.jvms}</parallelism>
<assertions enableSystemAssertions="true">
<!-- System assertions (-esa) are disabled for now because of what looks like a
JDK bug triggered by Groovy on JDK7. We should look at re-enabling system
assertions when we upgrade to a new version of Groovy (currently 2.4.4) or
require JDK8. See https://issues.apache.org/jira/browse/GROOVY-7528.
-->
<assertions enableSystemAssertions="false">
<enable/>
<disable package="${tests.assertion.disabled}"/>
<!-- pass org.elasticsearch to run without assertions -->

View File

@ -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
}
}

View File

@ -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*)+/

7
run.sh Executable file
View File

@ -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