Add an alias action to delete an index

While removing an index isn't actually an alias action, if we add
an alias action that deletes an index then we can delete and index
and add an alias with the same name as the index atomically, in
the same cluster state update.

Closes #20064
This commit is contained in:
Nik Everett 2016-08-18 16:51:37 -04:00
parent 70f4d718f8
commit df73292256
28 changed files with 1521 additions and 971 deletions

View File

@ -248,8 +248,6 @@
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]cluster[/\\]metadata[/\\]MappingMetaData.java" checks="LineLength" />
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]cluster[/\\]metadata[/\\]MetaData.java" checks="LineLength" />
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]cluster[/\\]metadata[/\\]MetaDataCreateIndexService.java" checks="LineLength" />
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]cluster[/\\]metadata[/\\]MetaDataDeleteIndexService.java" checks="LineLength" />
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]cluster[/\\]metadata[/\\]MetaDataIndexAliasesService.java" checks="LineLength" />
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]cluster[/\\]metadata[/\\]MetaDataIndexStateService.java" checks="LineLength" />
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]cluster[/\\]metadata[/\\]MetaDataIndexTemplateService.java" checks="LineLength" />
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]cluster[/\\]metadata[/\\]MetaDataIndexUpgradeService.java" checks="LineLength" />

View File

@ -21,29 +21,22 @@ package org.elasticsearch.action.admin.indices.alias;
import org.elasticsearch.cluster.ack.ClusterStateUpdateRequest;
import org.elasticsearch.cluster.metadata.AliasAction;
import java.util.List;
/**
* Cluster state update request that allows to add or remove aliases
*/
public class IndicesAliasesClusterStateUpdateRequest extends ClusterStateUpdateRequest<IndicesAliasesClusterStateUpdateRequest> {
private final List<AliasAction> actions;
AliasAction[] actions;
public IndicesAliasesClusterStateUpdateRequest() {
public IndicesAliasesClusterStateUpdateRequest(List<AliasAction> actions) {
this.actions = actions;
}
/**
* Returns the alias actions to be performed
*/
public AliasAction[] actions() {
public List<AliasAction> actions() {
return actions;
}
/**
* Sets the alias actions to be executed
*/
public IndicesAliasesClusterStateUpdateRequest actions(AliasAction[] actions) {
this.actions = actions;
return this;
}
}

View File

@ -20,6 +20,8 @@
package org.elasticsearch.action.admin.indices.alias;
import com.carrotsearch.hppc.cursors.ObjectCursor;
import org.elasticsearch.ElasticsearchGenerationException;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.AliasesRequest;
import org.elasticsearch.action.CompositeIndicesRequest;
@ -27,30 +29,41 @@ import org.elasticsearch.action.IndicesRequest;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.action.support.master.AcknowledgedRequest;
import org.elasticsearch.cluster.metadata.AliasAction;
import org.elasticsearch.cluster.metadata.AliasAction.Type;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.ParseFieldMatcherSupplier;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.util.CollectionUtils;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.ObjectParser.ValueType;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.QueryBuilder;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.function.Supplier;
import static org.elasticsearch.action.ValidateActions.addValidationError;
import static org.elasticsearch.cluster.metadata.AliasAction.readAliasAction;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;
import static org.elasticsearch.common.xcontent.ObjectParser.fromList;
/**
* A request to add/remove aliases for one or more indices.
*/
public class IndicesAliasesRequest extends AcknowledgedRequest<IndicesAliasesRequest> implements CompositeIndicesRequest {
private List<AliasActions> allAliasActions = new ArrayList<>();
//indices options that require every specified index to exist, expand wildcards only to open indices and
@ -61,94 +74,317 @@ public class IndicesAliasesRequest extends AcknowledgedRequest<IndicesAliasesReq
}
/*
* Aliases can be added by passing multiple indices to the Request and
* deleted by passing multiple indices and aliases. They are expanded into
* distinct AliasAction instances when the request is processed. This class
* holds the AliasAction and in addition the arrays or alias names and
* indices that is later used to create the final AliasAction instances.
/**
* Request to take one or more actions on one or more indexes and alias combinations.
*/
public static class AliasActions implements AliasesRequest {
private String[] indices = Strings.EMPTY_ARRAY;
public static class AliasActions implements AliasesRequest, Writeable {
public enum Type {
ADD((byte) 0),
REMOVE((byte) 1),
REMOVE_INDEX((byte) 2);
private final byte value;
Type(byte value) {
this.value = value;
}
public byte value() {
return value;
}
public static Type fromValue(byte value) {
switch (value) {
case 0: return ADD;
case 1: return REMOVE;
case 2: return REMOVE_INDEX;
default: throw new IllegalArgumentException("No type for action [" + value + "]");
}
}
}
/**
* Build a new {@link AliasAction} to add aliases.
*/
public static AliasActions add() {
return new AliasActions(AliasActions.Type.ADD);
}
/**
* Build a new {@link AliasAction} to remove aliases.
*/
public static AliasActions remove() {
return new AliasActions(AliasActions.Type.REMOVE);
}
/**
* Build a new {@link AliasAction} to remove aliases.
*/
public static AliasActions removeIndex() {
return new AliasActions(AliasActions.Type.REMOVE_INDEX);
}
private static ObjectParser<AliasActions, ParseFieldMatcherSupplier> parser(String name, Supplier<AliasActions> supplier) {
ObjectParser<AliasActions, ParseFieldMatcherSupplier> parser = new ObjectParser<>(name, supplier);
parser.declareString((action, index) -> {
if (action.indices() != null) {
throw new IllegalArgumentException("Only one of [index] and [indices] is supported");
}
action.index(index);
}, new ParseField("index"));
parser.declareStringArray(fromList(String.class, (action, indices) -> {
if (action.indices() != null) {
throw new IllegalArgumentException("Only one of [index] and [indices] is supported");
}
action.indices(indices);
}), new ParseField("indices"));
parser.declareString((action, alias) -> {
if (action.aliases() != null && action.aliases().length != 0) {
throw new IllegalArgumentException("Only one of [alias] and [aliases] is supported");
}
action.alias(alias);
}, new ParseField("alias"));
parser.declareStringArray(fromList(String.class, (action, aliases) -> {
if (action.aliases() != null && action.aliases().length != 0) {
throw new IllegalArgumentException("Only one of [alias] and [aliases] is supported");
}
action.aliases(aliases);
}), new ParseField("aliases"));
return parser;
}
private static final ObjectParser<AliasActions, ParseFieldMatcherSupplier> ADD_PARSER = parser("add", AliasActions::add);
static {
ADD_PARSER.declareObject(AliasActions::filter, (parser, m) -> {
try {
return parser.mapOrdered();
} catch (IOException e) {
throw new ParsingException(parser.getTokenLocation(), "Problems parsing [filter]", e);
}
}, new ParseField("filter"));
// Since we need to support numbers AND strings here we have to use ValueType.INT.
ADD_PARSER.declareField(AliasActions::routing, p -> p.text(), new ParseField("routing"), ValueType.INT);
ADD_PARSER.declareField(AliasActions::indexRouting, p -> p.text(), new ParseField("index_routing"), ValueType.INT);
ADD_PARSER.declareField(AliasActions::searchRouting, p -> p.text(), new ParseField("search_routing"), ValueType.INT);
}
private static final ObjectParser<AliasActions, ParseFieldMatcherSupplier> REMOVE_PARSER = parser("remove", AliasActions::remove);
private static final ObjectParser<AliasActions, ParseFieldMatcherSupplier> REMOVE_INDEX_PARSER = parser("remove_index",
AliasActions::removeIndex);
/**
* Parser for any one {@link AliasAction}.
*/
public static final ConstructingObjectParser<AliasActions, ParseFieldMatcherSupplier> PARSER = new ConstructingObjectParser<>(
"alias_action", a -> {
// Take the first action and complain if there are more than one actions
AliasActions action = null;
for (Object o : a) {
if (o != null) {
if (action == null) {
action = (AliasActions) o;
} else {
throw new IllegalArgumentException("Too many operations declared in on opeation entry");
}
}
}
return action;
});
static {
PARSER.declareObject(optionalConstructorArg(), ADD_PARSER, new ParseField("add"));
PARSER.declareObject(optionalConstructorArg(), REMOVE_PARSER, new ParseField("remove"));
PARSER.declareObject(optionalConstructorArg(), REMOVE_INDEX_PARSER, new ParseField("remove_index"));
}
private final AliasActions.Type type;
private String[] indices;
private String[] aliases = Strings.EMPTY_ARRAY;
private AliasAction aliasAction;
private String filter;
private String routing;
private String indexRouting;
private String searchRouting;
public AliasActions(AliasAction.Type type, String[] indices, String[] aliases) {
aliasAction = new AliasAction(type);
indices(indices);
aliases(aliases);
AliasActions(AliasActions.Type type) {
this.type = type;
}
public AliasActions(AliasAction.Type type, String index, String alias) {
aliasAction = new AliasAction(type);
indices(index);
aliases(alias);
/**
* Read from a stream.
*/
public AliasActions(StreamInput in) throws IOException {
type = AliasActions.Type.fromValue(in.readByte());
indices = in.readStringArray();
aliases = in.readStringArray();
filter = in.readOptionalString();
routing = in.readOptionalString();
searchRouting = in.readOptionalString();
indexRouting = in.readOptionalString();
}
AliasActions(AliasAction.Type type, String[] index, String alias) {
aliasAction = new AliasAction(type);
indices(index);
aliases(alias);
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeByte(type.value());
out.writeStringArray(indices);
out.writeStringArray(aliases);
out.writeOptionalString(filter);
out.writeOptionalString(routing);
out.writeOptionalString(searchRouting);
out.writeOptionalString(indexRouting);
}
public AliasActions(AliasAction action) {
this.aliasAction = action;
indices(action.index());
aliases(action.alias());
/**
* Validate that the action is sane. Called when the action is added to the request because actions can be invalid while being
* built.
*/
void validate() {
if (indices == null) {
throw new IllegalArgumentException("One of [index] or [indices] is required");
}
if (type != AliasActions.Type.REMOVE_INDEX && (aliases == null || aliases.length == 0)) {
throw new IllegalArgumentException("One of [alias] or [aliases] is required");
}
}
public AliasActions(Type type, String index, String[] aliases) {
aliasAction = new AliasAction(type);
indices(index);
aliases(aliases);
}
public AliasActions() {
}
public AliasActions filter(Map<String, Object> filter) {
aliasAction.filter(filter);
return this;
}
public AliasActions filter(QueryBuilder filter) {
aliasAction.filter(filter);
return this;
}
public Type actionType() {
return aliasAction.actionType();
}
public void routing(String routing) {
aliasAction.routing(routing);
}
public void searchRouting(String searchRouting) {
aliasAction.searchRouting(searchRouting);
}
public void indexRouting(String indexRouting) {
aliasAction.indexRouting(indexRouting);
}
public AliasActions filter(String filter) {
aliasAction.filter(filter);
return this;
/**
* Type of the action to perform.
*/
public AliasActions.Type actionType() {
return type;
}
@Override
public AliasActions indices(String... indices) {
if (indices == null || indices.length == 0) {
throw new IllegalArgumentException("[indices] can't be empty");
}
for (String index : indices) {
if (false == Strings.hasLength(index)) {
throw new IllegalArgumentException("[indices] can't contain empty string");
}
}
this.indices = indices;
return this;
}
/**
* Set the index this action is operating on.
*/
public AliasActions index(String index) {
if (false == Strings.hasLength(index)) {
throw new IllegalArgumentException("[index] can't be empty string");
}
this.indices = new String[] {index};
return this;
}
/**
* Aliases to use with this action.
*/
@Override
public AliasActions aliases(String... aliases) {
if (type == AliasActions.Type.REMOVE_INDEX) {
throw new IllegalArgumentException("[aliases] is unsupported for [" + type + "]");
}
if (aliases == null || aliases.length == 0) {
throw new IllegalArgumentException("[aliases] can't be empty");
}
for (String alias : aliases) {
if (false == Strings.hasLength(alias)) {
throw new IllegalArgumentException("[aliases] can't contain empty string");
}
}
this.aliases = aliases;
return this;
}
/**
* Set the alias this action is operating on.
*/
public AliasActions alias(String alias) {
if (type == AliasActions.Type.REMOVE_INDEX) {
throw new IllegalArgumentException("[alias] is unsupported for [" + type + "]");
}
if (false == Strings.hasLength(alias)) {
throw new IllegalArgumentException("[alias] can't be empty string");
}
this.aliases = new String[] {alias};
return this;
}
/**
* Set the default routing.
*/
public AliasActions routing(String routing) {
if (type != AliasActions.Type.ADD) {
throw new IllegalArgumentException("[routing] is unsupported for [" + type + "]");
}
this.routing = routing;
return this;
}
public String searchRouting() {
return searchRouting == null ? routing : searchRouting;
}
public AliasActions searchRouting(String searchRouting) {
if (type != AliasActions.Type.ADD) {
throw new IllegalArgumentException("[search_routing] is unsupported for [" + type + "]");
}
this.searchRouting = searchRouting;
return this;
}
public String indexRouting() {
return indexRouting == null ? routing : indexRouting;
}
public AliasActions indexRouting(String indexRouting) {
if (type != AliasActions.Type.ADD) {
throw new IllegalArgumentException("[index_routing] is unsupported for [" + type + "]");
}
this.indexRouting = indexRouting;
return this;
}
public String filter() {
return filter;
}
public AliasActions filter(String filter) {
if (type != AliasActions.Type.ADD) {
throw new IllegalArgumentException("[filter] is unsupported for [" + type + "]");
}
this.filter = filter;
return this;
}
public AliasActions filter(Map<String, Object> filter) {
if (filter == null || filter.isEmpty()) {
this.filter = null;
return this;
}
try {
XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
builder.map(filter);
this.filter = builder.string();
return this;
} catch (IOException e) {
throw new ElasticsearchGenerationException("Failed to generate [" + filter + "]", e);
}
}
public AliasActions filter(QueryBuilder filter) {
if (filter == null) {
this.filter = null;
return this;
}
try {
XContentBuilder builder = XContentFactory.jsonBuilder();
filter.toXContent(builder, ToXContent.EMPTY_PARAMS);
builder.close();
this.filter = builder.string();
return this;
} catch (IOException e) {
throw new ElasticsearchGenerationException("Failed to build json for alias request", e);
}
}
@Override
public String[] aliases() {
return aliases;
@ -157,7 +393,7 @@ public class IndicesAliasesRequest extends AcknowledgedRequest<IndicesAliasesReq
@Override
public boolean expandAliasesWildcards() {
//remove operations support wildcards among aliases, add operations don't
return aliasAction.actionType() == Type.REMOVE;
return type == Type.REMOVE;
}
@Override
@ -170,10 +406,6 @@ public class IndicesAliasesRequest extends AcknowledgedRequest<IndicesAliasesReq
return INDICES_OPTIONS;
}
public AliasAction aliasAction() {
return aliasAction;
}
public String[] concreteAliases(MetaData metaData, String concreteIndex) {
if (expandAliasesWildcards()) {
//for DELETE we expand the aliases
@ -191,83 +423,48 @@ public class IndicesAliasesRequest extends AcknowledgedRequest<IndicesAliasesReq
return aliases;
}
}
public AliasActions readFrom(StreamInput in) throws IOException {
indices = in.readStringArray();
aliases = in.readStringArray();
aliasAction = readAliasAction(in);
return this;
@Override
public String toString() {
return "AliasActions["
+ "type=" + type
+ ",indices=" + Arrays.toString(indices)
+ ",aliases=" + Arrays.deepToString(aliases)
+ ",filter=" + filter
+ ",routing=" + routing
+ ",indexRouting=" + indexRouting
+ ",searchRouting=" + searchRouting
+ "]";
}
public void writeTo(StreamOutput out) throws IOException {
out.writeStringArray(indices);
out.writeStringArray(aliases);
this.aliasAction.writeTo(out);
// equals, and hashCode implemented for easy testing of round trip
@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass()) {
return false;
}
AliasActions other = (AliasActions) obj;
return Objects.equals(type, other.type)
&& Arrays.equals(indices, other.indices)
&& Arrays.equals(aliases, other.aliases)
&& Objects.equals(filter, other.filter)
&& Objects.equals(routing, other.routing)
&& Objects.equals(indexRouting, other.indexRouting)
&& Objects.equals(searchRouting, other.searchRouting);
}
@Override
public int hashCode() {
return Objects.hash(type, indices, aliases, filter, routing, indexRouting, searchRouting);
}
}
/**
* Adds an alias to the index.
* @param alias The alias
* @param indices The indices
* Add the action to this request and validate it.
*/
public IndicesAliasesRequest addAlias(String alias, String... indices) {
addAliasAction(new AliasActions(AliasAction.Type.ADD, indices, alias));
return this;
}
public void addAliasAction(AliasActions aliasAction) {
public IndicesAliasesRequest addAliasAction(AliasActions aliasAction) {
aliasAction.validate();
allAliasActions.add(aliasAction);
}
public IndicesAliasesRequest addAliasAction(AliasAction action) {
addAliasAction(new AliasActions(action));
return this;
}
/**
* Adds an alias to the index.
* @param alias The alias
* @param filter The filter
* @param indices The indices
*/
public IndicesAliasesRequest addAlias(String alias, Map<String, Object> filter, String... indices) {
addAliasAction(new AliasActions(AliasAction.Type.ADD, indices, alias).filter(filter));
return this;
}
/**
* Adds an alias to the index.
* @param alias The alias
* @param filterBuilder The filter
* @param indices The indices
*/
public IndicesAliasesRequest addAlias(String alias, QueryBuilder filterBuilder, String... indices) {
addAliasAction(new AliasActions(AliasAction.Type.ADD, indices, alias).filter(filterBuilder));
return this;
}
/**
* Removes an alias to the index.
*
* @param indices The indices
* @param aliases The aliases
*/
public IndicesAliasesRequest removeAlias(String[] indices, String... aliases) {
addAliasAction(new AliasActions(AliasAction.Type.REMOVE, indices, aliases));
return this;
}
/**
* Removes an alias to the index.
*
* @param index The index
* @param aliases The aliases
*/
public IndicesAliasesRequest removeAlias(String index, String... aliases) {
addAliasAction(new AliasActions(AliasAction.Type.REMOVE, index, aliases));
return this;
}
@ -285,50 +482,20 @@ public class IndicesAliasesRequest extends AcknowledgedRequest<IndicesAliasesReq
if (allAliasActions.isEmpty()) {
return addValidationError("Must specify at least one alias action", validationException);
}
for (AliasActions aliasAction : allAliasActions) {
if (CollectionUtils.isEmpty(aliasAction.aliases)) {
validationException = addValidationError("Alias action [" + aliasAction.actionType().name().toLowerCase(Locale.ENGLISH)
+ "]: Property [alias/aliases] is either missing or null", validationException);
} else {
for (String alias : aliasAction.aliases) {
if (!Strings.hasText(alias)) {
validationException = addValidationError("Alias action [" + aliasAction.actionType().name().toLowerCase(Locale.ENGLISH)
+ "]: [alias/aliases] may not be empty string", validationException);
}
}
}
if (CollectionUtils.isEmpty(aliasAction.indices)) {
validationException = addValidationError("Alias action [" + aliasAction.actionType().name().toLowerCase(Locale.ENGLISH)
+ "]: Property [index/indices] is either missing or null", validationException);
} else {
for (String index : aliasAction.indices) {
if (!Strings.hasText(index)) {
validationException = addValidationError("Alias action [" + aliasAction.actionType().name().toLowerCase(Locale.ENGLISH)
+ "]: [index/indices] may not be empty string", validationException);
}
}
}
}
return validationException;
}
@Override
public void readFrom(StreamInput in) throws IOException {
super.readFrom(in);
int size = in.readVInt();
for (int i = 0; i < size; i++) {
allAliasActions.add(readAliasActions(in));
}
allAliasActions = in.readList(AliasActions::new);
readTimeout(in);
}
@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeVInt(allAliasActions.size());
for (AliasActions aliasAction : allAliasActions) {
aliasAction.writeTo(out);
}
out.writeList(allAliasActions);
writeTimeout(out);
}
@ -336,11 +503,6 @@ public class IndicesAliasesRequest extends AcknowledgedRequest<IndicesAliasesReq
return INDICES_OPTIONS;
}
private static AliasActions readAliasActions(StreamInput in) throws IOException {
AliasActions actions = new AliasActions();
return actions.readFrom(in);
}
@Override
public List<? extends IndicesRequest> subRequests() {
return allAliasActions;

View File

@ -22,15 +22,15 @@ package org.elasticsearch.action.admin.indices.alias;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
import org.elasticsearch.action.support.master.AcknowledgedRequestBuilder;
import org.elasticsearch.client.ElasticsearchClient;
import org.elasticsearch.cluster.metadata.AliasAction;
import org.elasticsearch.index.query.QueryBuilder;
import java.util.Map;
/**
*
* Builder for request to modify many aliases at once.
*/
public class IndicesAliasesRequestBuilder extends AcknowledgedRequestBuilder<IndicesAliasesRequest, IndicesAliasesResponse, IndicesAliasesRequestBuilder> {
public class IndicesAliasesRequestBuilder
extends AcknowledgedRequestBuilder<IndicesAliasesRequest, IndicesAliasesResponse, IndicesAliasesRequestBuilder> {
public IndicesAliasesRequestBuilder(ElasticsearchClient client, IndicesAliasesAction action) {
super(client, action, new IndicesAliasesRequest());
@ -43,7 +43,7 @@ public class IndicesAliasesRequestBuilder extends AcknowledgedRequestBuilder<Ind
* @param alias The alias
*/
public IndicesAliasesRequestBuilder addAlias(String index, String alias) {
request.addAlias(alias, index);
request.addAliasAction(AliasActions.add().index(index).alias(alias));
return this;
}
@ -54,7 +54,7 @@ public class IndicesAliasesRequestBuilder extends AcknowledgedRequestBuilder<Ind
* @param alias The alias
*/
public IndicesAliasesRequestBuilder addAlias(String[] indices, String alias) {
request.addAlias(alias, indices);
request.addAliasAction(AliasActions.add().indices(indices).alias(alias));
return this;
}
@ -66,8 +66,7 @@ public class IndicesAliasesRequestBuilder extends AcknowledgedRequestBuilder<Ind
* @param filter The filter
*/
public IndicesAliasesRequestBuilder addAlias(String index, String alias, String filter) {
AliasActions action = new AliasActions(AliasAction.Type.ADD, index, alias).filter(filter);
request.addAliasAction(action);
request.addAliasAction(AliasActions.add().index(index).alias(alias).filter(filter));
return this;
}
@ -79,8 +78,7 @@ public class IndicesAliasesRequestBuilder extends AcknowledgedRequestBuilder<Ind
* @param filter The filter
*/
public IndicesAliasesRequestBuilder addAlias(String indices[], String alias, String filter) {
AliasActions action = new AliasActions(AliasAction.Type.ADD, indices, alias).filter(filter);
request.addAliasAction(action);
request.addAliasAction(AliasActions.add().indices(indices).alias(alias).filter(filter));
return this;
}
@ -92,7 +90,7 @@ public class IndicesAliasesRequestBuilder extends AcknowledgedRequestBuilder<Ind
* @param filter The filter
*/
public IndicesAliasesRequestBuilder addAlias(String[] indices, String alias, Map<String, Object> filter) {
request.addAlias(alias, filter, indices);
request.addAliasAction(AliasActions.add().indices(indices).alias(alias).filter(filter));
return this;
}
@ -104,7 +102,7 @@ public class IndicesAliasesRequestBuilder extends AcknowledgedRequestBuilder<Ind
* @param filter The filter
*/
public IndicesAliasesRequestBuilder addAlias(String index, String alias, Map<String, Object> filter) {
request.addAlias(alias, filter, index);
request.addAliasAction(AliasActions.add().index(index).alias(alias).filter(filter));
return this;
}
@ -116,7 +114,7 @@ public class IndicesAliasesRequestBuilder extends AcknowledgedRequestBuilder<Ind
* @param filterBuilder The filter
*/
public IndicesAliasesRequestBuilder addAlias(String indices[], String alias, QueryBuilder filterBuilder) {
request.addAlias(alias, filterBuilder, indices);
request.addAliasAction(AliasActions.add().indices(indices).alias(alias).filter(filterBuilder));
return this;
}
@ -128,7 +126,7 @@ public class IndicesAliasesRequestBuilder extends AcknowledgedRequestBuilder<Ind
* @param filterBuilder The filter
*/
public IndicesAliasesRequestBuilder addAlias(String index, String alias, QueryBuilder filterBuilder) {
request.addAlias(alias, filterBuilder, index);
request.addAliasAction(AliasActions.add().index(index).alias(alias).filter(filterBuilder));
return this;
}
@ -139,7 +137,7 @@ public class IndicesAliasesRequestBuilder extends AcknowledgedRequestBuilder<Ind
* @param alias The alias
*/
public IndicesAliasesRequestBuilder removeAlias(String index, String alias) {
request.removeAlias(index, alias);
request.addAliasAction(AliasActions.remove().index(index).alias(alias));
return this;
}
@ -150,7 +148,7 @@ public class IndicesAliasesRequestBuilder extends AcknowledgedRequestBuilder<Ind
* @param aliases The aliases
*/
public IndicesAliasesRequestBuilder removeAlias(String[] indices, String... aliases) {
request.removeAlias(indices, aliases);
request.addAliasAction(AliasActions.remove().indices(indices).aliases(aliases));
return this;
}
@ -161,17 +159,12 @@ public class IndicesAliasesRequestBuilder extends AcknowledgedRequestBuilder<Ind
* @param aliases The aliases
*/
public IndicesAliasesRequestBuilder removeAlias(String index, String[] aliases) {
request.removeAlias(index, aliases);
request.addAliasAction(AliasActions.remove().index(index).aliases(aliases));
return this;
}
/**
* Adds an alias action to the request.
*
* @param aliasAction The alias action
*/
public IndicesAliasesRequestBuilder addAliasAction(AliasAction aliasAction) {
request.addAliasAction(aliasAction);
public IndicesAliasesRequestBuilder removeIndex(String index) {
request.addAliasAction(AliasActions.removeIndex().index(index));
return this;
}

View File

@ -43,6 +43,8 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static java.util.Collections.unmodifiableList;
/**
* Add/remove aliases action
*/
@ -86,31 +88,38 @@ public class TransportIndicesAliasesAction extends TransportMasterNodeAction<Ind
//Expand the indices names
List<AliasActions> actions = request.aliasActions();
List<AliasAction> finalActions = new ArrayList<>();
boolean hasOnlyDeletesButNoneCanBeDone = true;
// Resolve all the AliasActions into AliasAction instances and gather all the aliases
Set<String> aliases = new HashSet<>();
for (AliasActions action : actions) {
//expand indices
String[] concreteIndices = indexNameExpressionResolver.concreteIndexNames(state, request.indicesOptions(), action.indices());
//collect the aliases
Collections.addAll(aliases, action.aliases());
for (String index : concreteIndices) {
for (String alias : action.concreteAliases(state.metaData(), index)) {
AliasAction finalAction = new AliasAction(action.aliasAction());
finalAction.index(index);
finalAction.alias(alias);
finalActions.add(finalAction);
//if there is only delete requests, none will be added if the types do not map to any existing type
hasOnlyDeletesButNoneCanBeDone = false;
switch (action.actionType()) {
case ADD:
for (String alias : action.concreteAliases(state.metaData(), index)) {
finalActions.add(new AliasAction.Add(index, alias, action.filter(), action.indexRouting(), action.searchRouting()));
}
break;
case REMOVE:
for (String alias : action.concreteAliases(state.metaData(), index)) {
finalActions.add(new AliasAction.Remove(index, alias));
}
break;
case REMOVE_INDEX:
finalActions.add(new AliasAction.RemoveIndex(index));
break;
default:
throw new IllegalArgumentException("Unsupported action [" + action.actionType() + "]");
}
}
}
if (hasOnlyDeletesButNoneCanBeDone && actions.size() != 0) {
if (finalActions.isEmpty() && false == actions.isEmpty()) {
throw new AliasesNotFoundException(aliases.toArray(new String[aliases.size()]));
}
request.aliasActions().clear();
IndicesAliasesClusterStateUpdateRequest updateRequest = new IndicesAliasesClusterStateUpdateRequest()
.ackTimeout(request.timeout()).masterNodeTimeout(request.masterNodeTimeout())
.actions(finalActions.toArray(new AliasAction[finalActions.size()]));
IndicesAliasesClusterStateUpdateRequest updateRequest = new IndicesAliasesClusterStateUpdateRequest(unmodifiableList(finalActions))
.ackTimeout(request.timeout()).masterNodeTimeout(request.masterNodeTimeout());
indexAliasesService.indicesAliases(updateRequest, new ActionListener<ClusterStateUpdateResponse>() {
@Override

View File

@ -47,11 +47,15 @@ import org.elasticsearch.index.shard.DocsStats;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static java.util.Collections.unmodifiableList;
/**
* Main class to swap the index pointed to by an alias, given some conditions
*/
@ -156,13 +160,12 @@ public class TransportRolloverAction extends TransportMasterNodeAction<RolloverR
static IndicesAliasesClusterStateUpdateRequest prepareRolloverAliasesUpdateRequest(String oldIndex, String newIndex,
RolloverRequest request) {
final IndicesAliasesClusterStateUpdateRequest updateRequest = new IndicesAliasesClusterStateUpdateRequest()
List<AliasAction> actions = unmodifiableList(Arrays.asList(
new AliasAction.Add(newIndex, request.getAlias(), null, null, null),
new AliasAction.Remove(oldIndex, request.getAlias())));
final IndicesAliasesClusterStateUpdateRequest updateRequest = new IndicesAliasesClusterStateUpdateRequest(actions)
.ackTimeout(request.ackTimeout())
.masterNodeTimeout(request.masterNodeTimeout());
AliasAction[] actions = new AliasAction[2];
actions[0] = new AliasAction(AliasAction.Type.ADD, newIndex, request.getAlias());
actions[1] = new AliasAction(AliasAction.Type.REMOVE, oldIndex, request.getAlias());
updateRequest.actions(actions);
return updateRequest;
}

View File

@ -19,213 +19,167 @@
package org.elasticsearch.cluster.metadata;
import org.elasticsearch.ElasticsearchGenerationException;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Streamable;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.QueryBuilder;
import java.io.IOException;
import java.util.Map;
import org.elasticsearch.common.Strings;
/**
*
* Individual operation to perform on the cluster state as part of an {@link IndicesAliasesRequest}.
*/
public class AliasAction implements Streamable {
public abstract class AliasAction {
private final String index;
public static enum Type {
ADD((byte) 0),
REMOVE((byte) 1);
private final byte value;
Type(byte value) {
this.value = value;
private AliasAction(String index) {
if (false == Strings.hasText(index)) {
throw new IllegalArgumentException("[index] is required");
}
public byte value() {
return value;
}
public static Type fromValue(byte value) {
if (value == 0) {
return ADD;
} else if (value == 1) {
return REMOVE;
} else {
throw new IllegalArgumentException("No type for action [" + value + "]");
}
}
}
private Type actionType;
private String index;
private String alias;
@Nullable
private String filter;
@Nullable
private String indexRouting;
@Nullable
private String searchRouting;
private AliasAction() {
}
public AliasAction(AliasAction other) {
this.actionType = other.actionType;
this.index = other.index;
this.alias = other.alias;
this.filter = other.filter;
this.indexRouting = other.indexRouting;
this.searchRouting = other.searchRouting;
}
public AliasAction(Type actionType) {
this.actionType = actionType;
}
public AliasAction(Type actionType, String index, String alias) {
this.actionType = actionType;
this.index = index;
this.alias = alias;
}
public AliasAction(Type actionType, String index, String alias, String filter) {
this.actionType = actionType;
this.index = index;
this.alias = alias;
this.filter = filter;
}
public Type actionType() {
return actionType;
}
public AliasAction index(String index) {
this.index = index;
return this;
}
public String index() {
/**
* Get the index on which the operation should act.
*/
public String getIndex() {
return index;
}
public AliasAction alias(String alias) {
this.alias = alias;
return this;
/**
* Should this action remove the index? Actions that return true from this will never execute
* {@link #apply(NewAliasValidator, MetaData.Builder, IndexMetaData)}.
*/
abstract boolean removeIndex();
/**
* Apply the action.
*
* @param aliasValidator call to validate a new alias before adding it to the builder
* @param metadata metadata builder for the changes made by all actions as part of this request
* @param index metadata for the index being changed
* @return did this action make any changes?
*/
abstract boolean apply(NewAliasValidator aliasValidator, MetaData.Builder metadata, IndexMetaData index);
/**
* Validate a new alias.
*/
@FunctionalInterface
public interface NewAliasValidator {
void validate(String alias, @Nullable String indexRouting, @Nullable String filter);
}
public String alias() {
return alias;
}
/**
* Operation to add an alias to an index.
*/
public static class Add extends AliasAction {
private final String alias;
public String filter() {
return filter;
}
@Nullable
private final String filter;
public AliasAction filter(String filter) {
this.filter = filter;
return this;
}
@Nullable
private final String indexRouting;
public AliasAction filter(Map<String, Object> filter) {
if (filter == null || filter.isEmpty()) {
this.filter = null;
return this;
@Nullable
private final String searchRouting;
/**
* Build the operation.
*/
public Add(String index, String alias, @Nullable String filter, @Nullable String indexRouting, @Nullable String searchRouting) {
super(index);
if (false == Strings.hasText(alias)) {
throw new IllegalArgumentException("[alias] is required");
}
this.alias = alias;
this.filter = filter;
this.indexRouting = indexRouting;
this.searchRouting = searchRouting;
}
try {
XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
builder.map(filter);
this.filter = builder.string();
return this;
} catch (IOException e) {
throw new ElasticsearchGenerationException("Failed to generate [" + filter + "]", e);
/**
* Alias to add to the index.
*/
public String getAlias() {
return alias;
}
@Override
boolean removeIndex() {
return false;
}
@Override
boolean apply(NewAliasValidator aliasValidator, MetaData.Builder metadata, IndexMetaData index) {
aliasValidator.validate(alias, indexRouting, filter);
AliasMetaData newAliasMd = AliasMetaData.newAliasMetaDataBuilder(alias).filter(filter).indexRouting(indexRouting)
.searchRouting(searchRouting).build();
// Check if this alias already exists
AliasMetaData currentAliasMd = index.getAliases().get(alias);
if (currentAliasMd != null && currentAliasMd.equals(newAliasMd)) {
// It already exists, ignore it
return false;
}
metadata.put(IndexMetaData.builder(index).putAlias(newAliasMd));
return true;
}
}
public AliasAction filter(QueryBuilder queryBuilder) {
if (queryBuilder == null) {
this.filter = null;
return this;
/**
* Operation to remove an alias from an index.
*/
public static class Remove extends AliasAction {
private final String alias;
/**
* Build the operation.
*/
public Remove(String index, String alias) {
super(index);
if (false == Strings.hasText(alias)) {
throw new IllegalArgumentException("[alias] is required");
}
this.alias = alias;
}
try {
XContentBuilder builder = XContentFactory.jsonBuilder();
queryBuilder.toXContent(builder, ToXContent.EMPTY_PARAMS);
builder.close();
this.filter = builder.string();
return this;
} catch (IOException e) {
throw new ElasticsearchGenerationException("Failed to build json for alias request", e);
/**
* Alias to remove from the index.
*/
public String getAlias() {
return alias;
}
@Override
boolean removeIndex() {
return false;
}
@Override
boolean apply(NewAliasValidator aliasValidator, MetaData.Builder metadata, IndexMetaData index) {
if (false == index.getAliases().containsKey(alias)) {
return false;
}
metadata.put(IndexMetaData.builder(index).removeAlias(alias));
return true;
}
}
public AliasAction routing(String routing) {
this.indexRouting = routing;
this.searchRouting = routing;
return this;
}
/**
* Operation to remove an index. This is an "alias action" because it allows us to remove an index at the same time as we remove add an
* alias to replace it.
*/
public static class RemoveIndex extends AliasAction {
public RemoveIndex(String index) {
super(index);
}
public String indexRouting() {
return indexRouting;
}
@Override
boolean removeIndex() {
return true;
}
public AliasAction indexRouting(String indexRouting) {
this.indexRouting = indexRouting;
return this;
@Override
boolean apply(NewAliasValidator aliasValidator, MetaData.Builder metadata, IndexMetaData index) {
throw new UnsupportedOperationException();
}
}
public String searchRouting() {
return searchRouting;
}
public AliasAction searchRouting(String searchRouting) {
this.searchRouting = searchRouting;
return this;
}
public static AliasAction readAliasAction(StreamInput in) throws IOException {
AliasAction aliasAction = new AliasAction();
aliasAction.readFrom(in);
return aliasAction;
}
@Override
public void readFrom(StreamInput in) throws IOException {
actionType = Type.fromValue(in.readByte());
index = in.readOptionalString();
alias = in.readOptionalString();
filter = in.readOptionalString();
indexRouting = in.readOptionalString();
searchRouting = in.readOptionalString();
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeByte(actionType.value());
out.writeOptionalString(index);
out.writeOptionalString(alias);
out.writeOptionalString(filter);
out.writeOptionalString(indexRouting);
out.writeOptionalString(searchRouting);
}
public static AliasAction newAddAliasAction(String index, String alias) {
return new AliasAction(Type.ADD, index, alias);
}
public static AliasAction newRemoveAliasAction(String index, String alias) {
return new AliasAction(Type.REMOVE, index, alias);
}
}
}

View File

@ -20,6 +20,7 @@
package org.elasticsearch.cluster.metadata;
import org.elasticsearch.action.admin.indices.alias.Alias;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject;
@ -33,6 +34,7 @@ import org.elasticsearch.indices.InvalidAliasNameException;
import java.io.IOException;
import java.util.Optional;
import java.util.function.Function;
/**
* Validator for an alias, to be used before adding an alias to the index metadata
@ -45,22 +47,13 @@ public class AliasValidator extends AbstractComponent {
super(settings);
}
/**
* Allows to validate an {@link org.elasticsearch.cluster.metadata.AliasAction} and make sure
* it's valid before it gets added to the index metadata. Doesn't validate the alias filter.
* @throws IllegalArgumentException if the alias is not valid
*/
public void validateAliasAction(AliasAction aliasAction, MetaData metaData) {
validateAlias(aliasAction.alias(), aliasAction.index(), aliasAction.indexRouting(), metaData);
}
/**
* Allows to validate an {@link org.elasticsearch.action.admin.indices.alias.Alias} and make sure
* it's valid before it gets added to the index metadata. Doesn't validate the alias filter.
* @throws IllegalArgumentException if the alias is not valid
*/
public void validateAlias(Alias alias, String index, MetaData metaData) {
validateAlias(alias.name(), index, alias.indexRouting(), metaData);
validateAlias(alias.name(), index, alias.indexRouting(), name -> metaData.index(name));
}
/**
@ -69,7 +62,7 @@ public class AliasValidator extends AbstractComponent {
* @throws IllegalArgumentException if the alias is not valid
*/
public void validateAliasMetaData(AliasMetaData aliasMetaData, String index, MetaData metaData) {
validateAlias(aliasMetaData.alias(), index, aliasMetaData.indexRouting(), metaData);
validateAlias(aliasMetaData.alias(), index, aliasMetaData.indexRouting(), name -> metaData.index(name));
}
/**
@ -90,16 +83,19 @@ public class AliasValidator extends AbstractComponent {
}
}
private void validateAlias(String alias, String index, String indexRouting, MetaData metaData) {
/**
* Validate a proposed alias.
*/
public void validateAlias(String alias, String index, @Nullable String indexRouting, Function<String, IndexMetaData> indexLookup) {
validateAliasStandalone(alias, indexRouting);
if (!Strings.hasText(index)) {
throw new IllegalArgumentException("index name is required");
}
assert metaData != null;
if (metaData.hasIndex(alias)) {
throw new InvalidAliasNameException(metaData.index(alias).getIndex(), alias, "an index exists with the same name as the alias");
IndexMetaData indexNamedSameAsAlias = indexLookup.apply(alias);
if (indexNamedSameAsAlias != null) {
throw new InvalidAliasNameException(indexNamedSameAsAlias.getIndex(), alias, "an index exists with the same name as the alias");
}
}

View File

@ -37,11 +37,11 @@ import org.elasticsearch.index.Index;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;
@ -219,7 +219,7 @@ public final class IndexGraveyard implements MetaData.Custom {
/**
* Add a set of deleted indexes to the list of tombstones in the cluster state.
*/
public Builder addTombstones(final Index[] indices) {
public Builder addTombstones(final Collection<Index> indices) {
for (Index index : indices) {
addTombstone(index);
}

View File

@ -37,11 +37,13 @@ import org.elasticsearch.index.Index;
import org.elasticsearch.snapshots.SnapshotsService;
import java.util.Arrays;
import java.util.Collection;
import java.util.Set;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.toSet;
/**
*
* Deletes indices.
*/
public class MetaDataDeleteIndexService extends AbstractComponent {
@ -56,7 +58,8 @@ public class MetaDataDeleteIndexService extends AbstractComponent {
this.allocationService = allocationService;
}
public void deleteIndices(final DeleteIndexClusterStateUpdateRequest request, final ActionListener<ClusterStateUpdateResponse> listener) {
public void deleteIndices(final DeleteIndexClusterStateUpdateRequest request,
final ActionListener<ClusterStateUpdateResponse> listener) {
if (request.indices() == null || request.indices().length == 0) {
throw new IllegalArgumentException("Index name is required");
}
@ -71,37 +74,43 @@ public class MetaDataDeleteIndexService extends AbstractComponent {
@Override
public ClusterState execute(final ClusterState currentState) {
final MetaData meta = currentState.metaData();
final Index[] indices = request.indices();
final Set<IndexMetaData> metaDatas = Arrays.asList(indices).stream().map(i -> meta.getIndexSafe(i)).collect(Collectors.toSet());
// Check if index deletion conflicts with any running snapshots
SnapshotsService.checkIndexDeletion(currentState, metaDatas);
RoutingTable.Builder routingTableBuilder = RoutingTable.builder(currentState.routingTable());
MetaData.Builder metaDataBuilder = MetaData.builder(meta);
ClusterBlocks.Builder clusterBlocksBuilder = ClusterBlocks.builder().blocks(currentState.blocks());
final IndexGraveyard.Builder graveyardBuilder = IndexGraveyard.builder(metaDataBuilder.indexGraveyard());
final int previousGraveyardSize = graveyardBuilder.tombstones().size();
for (final Index index : indices) {
String indexName = index.getName();
logger.debug("[{}] deleting index", index);
routingTableBuilder.remove(indexName);
clusterBlocksBuilder.removeIndexBlocks(indexName);
metaDataBuilder.remove(indexName);
}
// add tombstones to the cluster state for each deleted index
final IndexGraveyard currentGraveyard = graveyardBuilder.addTombstones(indices).build(settings);
metaDataBuilder.indexGraveyard(currentGraveyard); // the new graveyard set on the metadata
logger.trace("{} tombstones purged from the cluster state. Previous tombstone size: {}. Current tombstone size: {}.",
graveyardBuilder.getNumPurged(), previousGraveyardSize, currentGraveyard.getTombstones().size());
MetaData newMetaData = metaDataBuilder.build();
ClusterBlocks blocks = clusterBlocksBuilder.build();
RoutingAllocation.Result routingResult = allocationService.reroute(
ClusterState.builder(currentState).routingTable(routingTableBuilder.build()).metaData(newMetaData).build(),
"deleted indices [" + indices + "]");
return ClusterState.builder(currentState).routingResult(routingResult).metaData(newMetaData).blocks(blocks).build();
return deleteIndices(currentState, Arrays.asList(request.indices()));
}
});
}
/**
* Delete some indices from the cluster state.
*/
public ClusterState deleteIndices(ClusterState currentState, Collection<Index> indices) {
final MetaData meta = currentState.metaData();
final Set<IndexMetaData> metaDatas = indices.stream().map(i -> meta.getIndexSafe(i)).collect(toSet());
// Check if index deletion conflicts with any running snapshots
SnapshotsService.checkIndexDeletion(currentState, metaDatas);
RoutingTable.Builder routingTableBuilder = RoutingTable.builder(currentState.routingTable());
MetaData.Builder metaDataBuilder = MetaData.builder(meta);
ClusterBlocks.Builder clusterBlocksBuilder = ClusterBlocks.builder().blocks(currentState.blocks());
final IndexGraveyard.Builder graveyardBuilder = IndexGraveyard.builder(metaDataBuilder.indexGraveyard());
final int previousGraveyardSize = graveyardBuilder.tombstones().size();
for (final Index index : indices) {
String indexName = index.getName();
logger.debug("[{}] deleting index", index);
routingTableBuilder.remove(indexName);
clusterBlocksBuilder.removeIndexBlocks(indexName);
metaDataBuilder.remove(indexName);
}
// add tombstones to the cluster state for each deleted index
final IndexGraveyard currentGraveyard = graveyardBuilder.addTombstones(indices).build(settings);
metaDataBuilder.indexGraveyard(currentGraveyard); // the new graveyard set on the metadata
logger.trace("{} tombstones purged from the cluster state. Previous tombstone size: {}. Current tombstone size: {}.",
graveyardBuilder.getNumPurged(), previousGraveyardSize, currentGraveyard.getTombstones().size());
MetaData newMetaData = metaDataBuilder.build();
ClusterBlocks blocks = clusterBlocksBuilder.build();
RoutingAllocation.Result routingResult = allocationService.reroute(
ClusterState.builder(currentState).routingTable(routingTableBuilder.build()).metaData(newMetaData).build(),
"deleted indices [" + indices + "]");
return ClusterState.builder(currentState).routingResult(routingResult).metaData(newMetaData).blocks(blocks).build();
}
}

View File

@ -20,11 +20,14 @@
package org.elasticsearch.cluster.metadata;
import com.carrotsearch.hppc.cursors.ObjectCursor;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesClusterStateUpdateRequest;
import org.elasticsearch.cluster.AckedClusterStateUpdateTask;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.ack.ClusterStateUpdateResponse;
import org.elasticsearch.cluster.metadata.AliasAction.NewAliasValidator;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.Priority;
import org.elasticsearch.common.Strings;
@ -38,11 +41,16 @@ import org.elasticsearch.index.NodeServicesProvider;
import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.indices.IndicesService;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import static java.util.Collections.emptyList;
/**
* Service responsible for submitting add and remove aliases requests
@ -56,109 +64,114 @@ public class MetaDataIndexAliasesService extends AbstractComponent {
private final AliasValidator aliasValidator;
private final NodeServicesProvider nodeServicesProvider;
private final MetaDataDeleteIndexService deleteIndexService;
@Inject
public MetaDataIndexAliasesService(Settings settings, ClusterService clusterService, IndicesService indicesService, AliasValidator aliasValidator, NodeServicesProvider nodeServicesProvider) {
public MetaDataIndexAliasesService(Settings settings, ClusterService clusterService, IndicesService indicesService,
AliasValidator aliasValidator, NodeServicesProvider nodeServicesProvider, MetaDataDeleteIndexService deleteIndexService) {
super(settings);
this.clusterService = clusterService;
this.indicesService = indicesService;
this.aliasValidator = aliasValidator;
this.nodeServicesProvider = nodeServicesProvider;
this.deleteIndexService = deleteIndexService;
}
public void indicesAliases(final IndicesAliasesClusterStateUpdateRequest request, final ActionListener<ClusterStateUpdateResponse> listener) {
clusterService.submitStateUpdateTask("index-aliases", new AckedClusterStateUpdateTask<ClusterStateUpdateResponse>(Priority.URGENT, request, listener) {
public void indicesAliases(final IndicesAliasesClusterStateUpdateRequest request,
final ActionListener<ClusterStateUpdateResponse> listener) {
clusterService.submitStateUpdateTask("index-aliases",
new AckedClusterStateUpdateTask<ClusterStateUpdateResponse>(Priority.URGENT, request, listener) {
@Override
protected ClusterStateUpdateResponse newResponse(boolean acknowledged) {
return new ClusterStateUpdateResponse(acknowledged);
}
@Override
public ClusterState execute(final ClusterState currentState) {
List<Index> indicesToClose = new ArrayList<>();
Map<String, IndexService> indices = new HashMap<>();
try {
for (AliasAction aliasAction : request.actions()) {
aliasValidator.validateAliasAction(aliasAction, currentState.metaData());
if (!currentState.metaData().hasIndex(aliasAction.index())) {
throw new IndexNotFoundException(aliasAction.index());
}
}
boolean changed = false;
MetaData.Builder builder = MetaData.builder(currentState.metaData());
for (AliasAction aliasAction : request.actions()) {
IndexMetaData indexMetaData = builder.get(aliasAction.index());
if (indexMetaData == null) {
throw new IndexNotFoundException(aliasAction.index());
}
// TODO: not copy (putAll)
IndexMetaData.Builder indexMetaDataBuilder = IndexMetaData.builder(indexMetaData);
if (aliasAction.actionType() == AliasAction.Type.ADD) {
String filter = aliasAction.filter();
if (Strings.hasLength(filter)) {
// parse the filter, in order to validate it
IndexService indexService = indices.get(indexMetaData.getIndex());
if (indexService == null) {
indexService = indicesService.indexService(indexMetaData.getIndex());
if (indexService == null) {
// temporarily create the index and add mappings so we can parse the filter
try {
indexService = indicesService.createIndex(nodeServicesProvider, indexMetaData, Collections.emptyList());
for (ObjectCursor<MappingMetaData> cursor : indexMetaData.getMappings().values()) {
MappingMetaData mappingMetaData = cursor.value;
indexService.mapperService().merge(mappingMetaData.type(), mappingMetaData.source(), MapperService.MergeReason.MAPPING_RECOVERY, false);
}
} catch (Exception e) {
logger.warn("[{}] failed to temporary create in order to apply alias action", e, indexMetaData.getIndex());
continue;
}
indicesToClose.add(indexMetaData.getIndex());
}
indices.put(indexMetaData.getIndex().getName(), indexService);
}
aliasValidator.validateAliasFilter(aliasAction.alias(), filter, indexService.newQueryShardContext());
}
AliasMetaData newAliasMd = AliasMetaData.newAliasMetaDataBuilder(
aliasAction.alias())
.filter(filter)
.indexRouting(aliasAction.indexRouting())
.searchRouting(aliasAction.searchRouting())
.build();
// Check if this alias already exists
AliasMetaData aliasMd = indexMetaData.getAliases().get(aliasAction.alias());
if (aliasMd != null && aliasMd.equals(newAliasMd)) {
// It's the same alias - ignore it
continue;
}
indexMetaDataBuilder.putAlias(newAliasMd);
} else if (aliasAction.actionType() == AliasAction.Type.REMOVE) {
if (!indexMetaData.getAliases().containsKey(aliasAction.alias())) {
// This alias doesn't exist - ignore
continue;
}
indexMetaDataBuilder.removeAlias(aliasAction.alias());
}
changed = true;
builder.put(indexMetaDataBuilder);
}
if (changed) {
ClusterState updatedState = ClusterState.builder(currentState).metaData(builder).build();
// even though changes happened, they resulted in 0 actual changes to metadata
// i.e. remove and add the same alias to the same index
if (!updatedState.metaData().equalsAliases(currentState.metaData())) {
return updatedState;
}
}
return currentState;
} finally {
for (Index index : indicesToClose) {
indicesService.removeIndex(index, "created for alias processing");
}
}
public ClusterState execute(ClusterState currentState) {
return innerExecute(currentState, request.actions());
}
});
}
ClusterState innerExecute(ClusterState currentState, Iterable<AliasAction> actions) {
List<Index> indicesToClose = new ArrayList<>();
Map<String, IndexService> indices = new HashMap<>();
try {
boolean changed = false;
// Gather all the indexes that must be removed first so:
// 1. We don't cause error when attempting to replace an index with a alias of the same name.
// 2. We don't allow removal of aliases from indexes that we're just going to delete anyway. That'd be silly.
Set<Index> indicesToDelete = new HashSet<>();
for (AliasAction action : actions) {
if (action.removeIndex()) {
IndexMetaData index = currentState.metaData().getIndices().get(action.getIndex());
if (index == null) {
throw new IndexNotFoundException(action.getIndex());
}
indicesToDelete.add(index.getIndex());
changed = true;
}
}
// Remove the indexes if there are any to remove
if (changed) {
currentState = deleteIndexService.deleteIndices(currentState, indicesToDelete);
}
MetaData.Builder metadata = MetaData.builder(currentState.metaData());
// Run the remaining alias actions
for (AliasAction action : actions) {
if (action.removeIndex()) {
// Handled above
continue;
}
IndexMetaData index = metadata.get(action.getIndex());
if (index == null) {
throw new IndexNotFoundException(action.getIndex());
}
NewAliasValidator newAliasValidator = (alias, indexRouting, filter) -> {
/* It is important that we look up the index using the metadata builder we are modifying so we can remove an
* index and replace it with an alias. */
Function<String, IndexMetaData> indexLookup = name -> metadata.get(name);
aliasValidator.validateAlias(alias, action.getIndex(), indexRouting, indexLookup);
if (Strings.hasLength(filter)) {
IndexService indexService = indices.get(index.getIndex());
if (indexService == null) {
indexService = indicesService.indexService(index.getIndex());
if (indexService == null) {
// temporarily create the index and add mappings so we can parse the filter
try {
indexService = indicesService.createIndex(nodeServicesProvider, index, emptyList());
} catch (IOException e) {
throw new ElasticsearchException("Failed to create temporary index for parsing the alias", e);
}
for (ObjectCursor<MappingMetaData> cursor : index.getMappings().values()) {
MappingMetaData mappingMetaData = cursor.value;
indexService.mapperService().merge(mappingMetaData.type(), mappingMetaData.source(),
MapperService.MergeReason.MAPPING_RECOVERY, false);
}
indicesToClose.add(index.getIndex());
}
indices.put(action.getIndex(), indexService);
}
aliasValidator.validateAliasFilter(alias, filter, indexService.newQueryShardContext());
}
};
changed |= action.apply(newAliasValidator, metadata, index);
}
if (changed) {
ClusterState updatedState = ClusterState.builder(currentState).metaData(metadata).build();
// even though changes happened, they resulted in 0 actual changes to metadata
// i.e. remove and add the same alias to the same index
if (!updatedState.metaData().equalsAliases(currentState.metaData())) {
return updatedState;
}
}
return currentState;
} finally {
for (Index index : indicesToClose) {
indicesService.removeIndex(index, "created for alias processing");
}
}
}
}

View File

@ -20,6 +20,7 @@ package org.elasticsearch.rest.action.admin.indices;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesResponse;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.inject.Inject;
@ -49,7 +50,7 @@ public class RestIndexDeleteAliasesAction extends BaseRestHandler {
final String[] aliases = Strings.splitStringByCommaToArray(request.param("name"));
IndicesAliasesRequest indicesAliasesRequest = new IndicesAliasesRequest();
indicesAliasesRequest.timeout(request.paramAsTime("timeout", indicesAliasesRequest.timeout()));
indicesAliasesRequest.removeAlias(indices, aliases);
indicesAliasesRequest.addAliasAction(AliasActions.remove().indices(indices).aliases(aliases));
indicesAliasesRequest.masterNodeTimeout(request.paramAsTime("master_timeout", indicesAliasesRequest.masterNodeTimeout()));
client.admin().indices().aliases(indicesAliasesRequest, new AcknowledgedRestListener<IndicesAliasesResponse>(channel));

View File

@ -20,9 +20,7 @@ package org.elasticsearch.rest.action.admin.indices;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesResponse;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.cluster.metadata.AliasAction;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
@ -103,12 +101,9 @@ public class RestIndexPutAliasAction extends BaseRestHandler {
IndicesAliasesRequest indicesAliasesRequest = new IndicesAliasesRequest();
indicesAliasesRequest.timeout(request.paramAsTime("timeout", indicesAliasesRequest.timeout()));
String[] aliases = new String[]{alias};
IndicesAliasesRequest.AliasActions aliasAction = new AliasActions(AliasAction.Type.ADD, indices, aliases);
indicesAliasesRequest.addAliasAction(aliasAction);
indicesAliasesRequest.masterNodeTimeout(request.paramAsTime("master_timeout", indicesAliasesRequest.masterNodeTimeout()));
IndicesAliasesRequest.AliasActions aliasAction = AliasActions.add().indices(indices).alias(alias);
if (routing != null) {
aliasAction.routing(routing);
}
@ -121,6 +116,7 @@ public class RestIndexPutAliasAction extends BaseRestHandler {
if (filter != null) {
aliasAction.filter(filter);
}
client.admin().indices().aliases(indicesAliasesRequest, new AcknowledgedRestListener<IndicesAliasesResponse>(channel));
indicesAliasesRequest.addAliasAction(aliasAction);
client.admin().indices().aliases(indicesAliasesRequest, new AcknowledgedRestListener<>(channel));
}
}

View File

@ -23,9 +23,12 @@ import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesResponse;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.cluster.metadata.AliasAction;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.ParseFieldMatcherSupplier;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.rest.BaseRestHandler;
@ -34,13 +37,17 @@ import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.action.AcknowledgedRestListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static org.elasticsearch.rest.RestRequest.Method.POST;
public class RestIndicesAliasesAction extends BaseRestHandler {
static final ObjectParser<IndicesAliasesRequest, ParseFieldMatcherSupplier> PARSER = new ObjectParser<>("aliases");
static {
PARSER.declareObjectArray((request, actions) -> {
for (AliasActions action: actions) {
request.addAliasAction(action);
}
}, AliasActions.PARSER, new ParseField("actions"));
}
@Inject
public RestIndicesAliasesAction(Settings settings, RestController controller) {
@ -52,104 +59,12 @@ public class RestIndicesAliasesAction extends BaseRestHandler {
public void handleRequest(final RestRequest request, final RestChannel channel, final NodeClient client) throws Exception {
IndicesAliasesRequest indicesAliasesRequest = new IndicesAliasesRequest();
indicesAliasesRequest.masterNodeTimeout(request.paramAsTime("master_timeout", indicesAliasesRequest.masterNodeTimeout()));
indicesAliasesRequest.timeout(request.paramAsTime("timeout", indicesAliasesRequest.timeout()));
try (XContentParser parser = XContentFactory.xContent(request.content()).createParser(request.content())) {
// {
// actions : [
// { add : { index : "test1", alias : "alias1", filter : {"user" : "kimchy"} } }
// { remove : { index : "test1", alias : "alias1" } }
// ]
// }
indicesAliasesRequest.timeout(request.paramAsTime("timeout", indicesAliasesRequest.timeout()));
XContentParser.Token token = parser.nextToken();
if (token == null) {
throw new IllegalArgumentException("No action is specified");
}
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.START_ARRAY) {
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
if (token == XContentParser.Token.FIELD_NAME) {
String action = parser.currentName();
AliasAction.Type type;
if ("add".equals(action)) {
type = AliasAction.Type.ADD;
} else if ("remove".equals(action)) {
type = AliasAction.Type.REMOVE;
} else {
throw new IllegalArgumentException("Alias action [" + action + "] not supported");
}
String[] indices = null;
String[] aliases = null;
Map<String, Object> filter = null;
String routing = null;
boolean routingSet = false;
String indexRouting = null;
boolean indexRoutingSet = false;
String searchRouting = null;
boolean searchRoutingSet = false;
String currentFieldName = null;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else if (token.isValue()) {
if ("index".equals(currentFieldName)) {
indices = new String[] { parser.text() };
} else if ("alias".equals(currentFieldName)) {
aliases = new String[] { parser.text() };
} else if ("routing".equals(currentFieldName)) {
routing = parser.textOrNull();
routingSet = true;
} else if ("indexRouting".equals(currentFieldName)
|| "index-routing".equals(currentFieldName) || "index_routing".equals(currentFieldName)) {
indexRouting = parser.textOrNull();
indexRoutingSet = true;
} else if ("searchRouting".equals(currentFieldName)
|| "search-routing".equals(currentFieldName) || "search_routing".equals(currentFieldName)) {
searchRouting = parser.textOrNull();
searchRoutingSet = true;
}
} else if (token == XContentParser.Token.START_ARRAY) {
if ("indices".equals(currentFieldName)) {
List<String> indexNames = new ArrayList<>();
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
String index = parser.text();
indexNames.add(index);
}
indices = indexNames.toArray(new String[indexNames.size()]);
}
if ("aliases".equals(currentFieldName)) {
List<String> aliasNames = new ArrayList<>();
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
String alias = parser.text();
aliasNames.add(alias);
}
aliases = aliasNames.toArray(new String[aliasNames.size()]);
}
} else if (token == XContentParser.Token.START_OBJECT) {
if ("filter".equals(currentFieldName)) {
filter = parser.mapOrdered();
}
}
}
if (type == AliasAction.Type.ADD) {
AliasActions aliasActions = new AliasActions(type, indices, aliases).filter(filter);
if (routingSet) {
aliasActions.routing(routing);
}
if (indexRoutingSet) {
aliasActions.indexRouting(indexRouting);
}
if (searchRoutingSet) {
aliasActions.searchRouting(searchRouting);
}
indicesAliasesRequest.addAliasAction(aliasActions);
} else if (type == AliasAction.Type.REMOVE) {
indicesAliasesRequest.removeAlias(indices, aliases);
}
}
}
}
}
PARSER.parse(parser, indicesAliasesRequest, () -> ParseFieldMatcher.STRICT);
}
if (indicesAliasesRequest.getAliasActions().isEmpty()) {
throw new IllegalArgumentException("No action specified");
}
client.admin().indices().aliases(indicesAliasesRequest, new AcknowledgedRestListener<IndicesAliasesResponse>(channel));
}

View File

@ -0,0 +1,350 @@
/*
* 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.admin.indices.alias;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.test.ESTestCase;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import static org.hamcrest.Matchers.arrayContaining;
import static org.hamcrest.Matchers.arrayWithSize;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
public class AliasActionsTests extends ESTestCase {
public void testValidate() {
AliasActions.Type type = randomFrom(AliasActions.Type.values());
if (type == AliasActions.Type.REMOVE_INDEX) {
Exception e = expectThrows(IllegalArgumentException.class, () -> new AliasActions(type).validate());
assertEquals("One of [index] or [indices] is required", e.getMessage());
} else {
Exception e = expectThrows(IllegalArgumentException.class,
() -> new AliasActions(type).alias(randomAsciiOfLength(5)).validate());
assertEquals("One of [index] or [indices] is required", e.getMessage());
e = expectThrows(IllegalArgumentException.class, () -> new AliasActions(type).index(randomAsciiOfLength(5)).validate());
assertEquals("One of [alias] or [aliases] is required", e.getMessage());
}
}
public void testEmptyIndex() {
Exception e = expectThrows(IllegalArgumentException.class,
() -> new AliasActions(randomFrom(AliasActions.Type.values())).index(null));
assertEquals("[index] can't be empty string", e.getMessage());
e = expectThrows(IllegalArgumentException.class,
() -> new AliasActions(randomFrom(AliasActions.Type.values())).index(""));
assertEquals("[index] can't be empty string", e.getMessage());
e = expectThrows(IllegalArgumentException.class,
() -> new AliasActions(randomFrom(AliasActions.Type.values())).indices((String[]) null));
assertEquals("[indices] can't be empty", e.getMessage());
e = expectThrows(IllegalArgumentException.class,
() -> new AliasActions(randomFrom(AliasActions.Type.values())).indices(new String[0]));
assertEquals("[indices] can't be empty", e.getMessage());
e = expectThrows(IllegalArgumentException.class,
() -> new AliasActions(randomFrom(AliasActions.Type.values())).indices("test", null));
assertEquals("[indices] can't contain empty string", e.getMessage());
e = expectThrows(IllegalArgumentException.class,
() -> new AliasActions(randomFrom(AliasActions.Type.values())).indices("test", ""));
assertEquals("[indices] can't contain empty string", e.getMessage());
}
public void testEmptyAlias() {
AliasActions.Type type = randomValueOtherThan(AliasActions.Type.REMOVE_INDEX, () -> randomFrom(AliasActions.Type.values()));
Exception e = expectThrows(IllegalArgumentException.class, () -> new AliasActions(type).alias(null));
assertEquals("[alias] can't be empty string", e.getMessage());
e = expectThrows(IllegalArgumentException.class, () -> new AliasActions(type).alias(""));
assertEquals("[alias] can't be empty string", e.getMessage());
e = expectThrows(IllegalArgumentException.class, () -> new AliasActions(type).aliases((String[]) null));
assertEquals("[aliases] can't be empty", e.getMessage());
e = expectThrows(IllegalArgumentException.class, () -> new AliasActions(type).aliases(new String[0]));
assertEquals("[aliases] can't be empty", e.getMessage());
e = expectThrows(IllegalArgumentException.class, () -> new AliasActions(type).aliases("test", null));
assertEquals("[aliases] can't contain empty string", e.getMessage());
e = expectThrows(IllegalArgumentException.class, () -> new AliasActions(type).aliases("test", ""));
assertEquals("[aliases] can't contain empty string", e.getMessage());
}
public void testBadOptionsInNonIndex() {
AliasActions action = randomBoolean() ? AliasActions.remove() : AliasActions.removeIndex();
Exception e = expectThrows(IllegalArgumentException.class, () -> action.routing("test"));
assertEquals("[routing] is unsupported for [" + action.actionType() + "]", e.getMessage());
e = expectThrows(IllegalArgumentException.class, () -> action.searchRouting("test"));
assertEquals("[search_routing] is unsupported for [" + action.actionType() + "]", e.getMessage());
e = expectThrows(IllegalArgumentException.class, () -> action.indexRouting("test"));
assertEquals("[index_routing] is unsupported for [" + action.actionType() + "]", e.getMessage());
e = expectThrows(IllegalArgumentException.class, () -> action.filter("test"));
assertEquals("[filter] is unsupported for [" + action.actionType() + "]", e.getMessage());
}
public void testParseAdd() throws IOException {
String[] indices = generateRandomStringArray(10, 5, false, false);
String[] aliases = generateRandomStringArray(10, 5, false, false);
Map<String, Object> filter = randomBoolean() ? randomMap(5) : null;
Object searchRouting = randomBoolean() ? randomRouting() : null;
Object indexRouting = randomBoolean() ? randomBoolean() ? searchRouting : randomRouting() : null;
XContentBuilder b = XContentBuilder.builder(randomFrom(XContentType.values()).xContent());
b.startObject(); {
b.startObject("add"); {
if (indices.length > 1 || randomBoolean()) {
b.field("indices", indices);
} else {
b.field("index", indices[0]);
}
if (aliases.length > 1 || randomBoolean()) {
b.field("aliases", aliases);
} else {
b.field("alias", aliases[0]);
}
if (filter != null) {
b.field("filter", filter);
}
if (searchRouting != null) {
if (searchRouting.equals(indexRouting)) {
b.field("routing", searchRouting);
} else {
b.field("search_routing", searchRouting);
}
}
if (indexRouting != null && false == indexRouting.equals(searchRouting)) {
b.field("index_routing", indexRouting);
}
}
b.endObject();
}
b.endObject();
b = shuffleXContent(b, "filter");
try (XContentParser parser = XContentHelper.createParser(b.bytes())) {
AliasActions action = AliasActions.PARSER.apply(parser, () -> ParseFieldMatcher.STRICT);
assertEquals(AliasActions.Type.ADD, action.actionType());
assertThat(action.indices(), equalTo(indices));
assertThat(action.aliases(), equalTo(aliases));
if (filter == null || filter.isEmpty()) {
assertNull(action.filter());
} else {
assertEquals(XContentFactory.contentBuilder(XContentType.JSON).map(filter).string(), action.filter());
}
assertEquals(Objects.toString(searchRouting, null), action.searchRouting());
assertEquals(Objects.toString(indexRouting, null), action.indexRouting());
}
}
public void testParseAddDefaultRouting() throws IOException {
String index = randomAsciiOfLength(5);
String alias = randomAsciiOfLength(5);
Object searchRouting = randomRouting();
Object indexRouting = randomRouting();
XContentBuilder b = XContentBuilder.builder(randomFrom(XContentType.values()).xContent());
b.startObject(); {
b.startObject("add"); {
b.field("index", index);
b.field("alias", alias);
if (randomBoolean()) {
b.field("routing", searchRouting);
b.field("index_routing", indexRouting);
} else {
b.field("search_routing", searchRouting);
b.field("routing", indexRouting);
}
}
b.endObject();
}
b.endObject();
b = shuffleXContent(b);
try (XContentParser parser = XContentHelper.createParser(b.bytes())) {
AliasActions action = AliasActions.PARSER.apply(parser, () -> ParseFieldMatcher.STRICT);
assertEquals(AliasActions.Type.ADD, action.actionType());
assertThat(action.indices(), arrayContaining(index));
assertThat(action.aliases(), arrayContaining(alias));
assertEquals(searchRouting.toString(), action.searchRouting());
assertEquals(indexRouting.toString(), action.indexRouting());
}
}
public void testParseRemove() throws IOException {
String[] indices = generateRandomStringArray(10, 5, false, false);
String[] aliases = generateRandomStringArray(10, 5, false, false);
XContentBuilder b = XContentBuilder.builder(randomFrom(XContentType.values()).xContent());
b.startObject(); {
b.startObject("remove"); {
if (indices.length > 1 || randomBoolean()) {
b.field("indices", indices);
} else {
b.field("index", indices[0]);
}
if (aliases.length > 1 || randomBoolean()) {
b.field("aliases", aliases);
} else {
b.field("alias", aliases[0]);
}
}
b.endObject();
}
b.endObject();
b = shuffleXContent(b);
try (XContentParser parser = XContentHelper.createParser(b.bytes())) {
AliasActions action = AliasActions.PARSER.apply(parser, () -> ParseFieldMatcher.STRICT);
assertEquals(AliasActions.Type.REMOVE, action.actionType());
assertThat(action.indices(), equalTo(indices));
assertThat(action.aliases(), equalTo(aliases));
}
}
public void testParseRemoveIndex() throws IOException {
String[] indices = randomBoolean() ? new String[] {randomAsciiOfLength(5)} : generateRandomStringArray(10, 5, false, false);
XContentBuilder b = XContentBuilder.builder(randomFrom(XContentType.values()).xContent());
b.startObject(); {
b.startObject("remove_index"); {
if (indices.length > 1 || randomBoolean()) {
b.field("indices", indices);
} else {
b.field("index", indices[0]);
}
}
b.endObject();
}
b.endObject();
b = shuffleXContent(b);
try (XContentParser parser = XContentHelper.createParser(b.bytes())) {
AliasActions action = AliasActions.PARSER.apply(parser, () -> ParseFieldMatcher.STRICT);
assertEquals(AliasActions.Type.REMOVE_INDEX, action.actionType());
assertArrayEquals(indices, action.indices());
assertThat(action.aliases(), arrayWithSize(0));
}
}
public void testParseIndexAndIndicesThrowsError() throws IOException {
XContentBuilder b = XContentBuilder.builder(randomFrom(XContentType.values()).xContent());
b.startObject(); {
b.startObject(randomFrom("add", "remove")); {
b.field("index", randomAsciiOfLength(5));
b.field("indices", generateRandomStringArray(10, 5, false, false));
b.field("alias", randomAsciiOfLength(5));
}
b.endObject();
}
b.endObject();
try (XContentParser parser = XContentHelper.createParser(b.bytes())) {
Exception e = expectThrows(ParsingException.class, () -> AliasActions.PARSER.apply(parser, () -> ParseFieldMatcher.STRICT));
assertThat(e.getCause().getCause(), instanceOf(IllegalArgumentException.class));
assertEquals("Only one of [index] and [indices] is supported", e.getCause().getCause().getMessage());
}
}
public void testParseAliasAndAliasesThrowsError() throws IOException {
XContentBuilder b = XContentBuilder.builder(randomFrom(XContentType.values()).xContent());
b.startObject(); {
b.startObject(randomFrom("add", "remove")); {
b.field("index", randomAsciiOfLength(5));
b.field("alias", randomAsciiOfLength(5));
b.field("aliases", generateRandomStringArray(10, 5, false, false));
}
b.endObject();
}
b.endObject();
try (XContentParser parser = XContentHelper.createParser(b.bytes())) {
Exception e = expectThrows(ParsingException.class, () -> AliasActions.PARSER.apply(parser, () -> ParseFieldMatcher.STRICT));
assertThat(e.getCause().getCause(), instanceOf(IllegalArgumentException.class));
assertEquals("Only one of [alias] and [aliases] is supported", e.getCause().getCause().getMessage());
}
}
public void testRoundTrip() throws IOException {
AliasActions action = new AliasActions(randomFrom(AliasActions.Type.values()));
if (randomBoolean()) {
action.index(randomAsciiOfLength(5));
} else {
action.indices(generateRandomStringArray(5, 5, false, false));
}
if (action.actionType() != AliasActions.Type.REMOVE_INDEX) {
if (randomBoolean()) {
action.alias(randomAsciiOfLength(5));
} else {
action.aliases(generateRandomStringArray(5, 5, false, false));
}
}
if (action.actionType() == AliasActions.Type.ADD) {
if (randomBoolean()) {
action.filter(randomAsciiOfLength(10));
}
if (randomBoolean()) {
if (randomBoolean()) {
action.routing(randomAsciiOfLength(5));
} else {
action.searchRouting(randomAsciiOfLength(5));
action.indexRouting(randomAsciiOfLength(5));
}
}
}
try (BytesStreamOutput out = new BytesStreamOutput()) {
action.writeTo(out);
try (StreamInput in = out.bytes().streamInput()) {
AliasActions read = new AliasActions(in);
assertEquals(action, read);
}
}
}
private Map<String, Object> randomMap(int maxDepth) {
int members = between(0, 5);
Map<String, Object> result = new HashMap<>(members);
for (int i = 0; i < members; i++) {
Object value;
switch (between(0, 3)) {
case 0:
if (maxDepth > 0) {
value = randomMap(maxDepth - 1);
} else {
value = randomAsciiOfLength(5);
}
break;
case 1:
value = randomAsciiOfLength(5);
break;
case 2:
value = randomBoolean();
break;
case 3:
value = randomLong();
break;
default:
throw new UnsupportedOperationException();
}
result.put(randomAsciiOfLength(5), value);
}
return result;
}
private Object randomRouting() {
return randomBoolean() ? randomAsciiOfLength(5) : randomInt();
}
}

View File

@ -35,12 +35,13 @@ import org.elasticsearch.index.shard.DocsStats;
import org.elasticsearch.test.ESTestCase;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import static org.elasticsearch.action.admin.indices.rollover.TransportRolloverAction.evaluateConditions;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.hasSize;
public class TransportRolloverActionTests extends ESTestCase {
@ -97,19 +98,19 @@ public class TransportRolloverActionTests extends ESTestCase {
final IndicesAliasesClusterStateUpdateRequest updateRequest =
TransportRolloverAction.prepareRolloverAliasesUpdateRequest(sourceIndex, targetIndex, rolloverRequest);
final AliasAction[] actions = updateRequest.actions();
assertThat(actions.length, equalTo(2));
List<AliasAction> actions = updateRequest.actions();
assertThat(actions, hasSize(2));
boolean foundAdd = false;
boolean foundRemove = false;
for (AliasAction action : actions) {
if (action.actionType() == AliasAction.Type.ADD) {
if (action.getIndex().equals(targetIndex)) {
assertEquals(sourceAlias, ((AliasAction.Add) action).getAlias());
foundAdd = true;
assertThat(action.index(), equalTo(targetIndex));
assertThat(action.alias(), equalTo(sourceAlias));
} else if (action.actionType() == AliasAction.Type.REMOVE) {
} else if (action.getIndex().equals(sourceIndex)) {
assertEquals(sourceAlias, ((AliasAction.Remove) action).getAlias());
foundRemove = true;
assertThat(action.index(), equalTo(sourceIndex));
assertThat(action.alias(), equalTo(sourceAlias));
} else {
throw new AssertionError("Unknow index [" + action.getIndex() + "]");
}
}
assertTrue(foundAdd);

View File

@ -20,10 +20,8 @@
package org.elasticsearch.aliases;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.admin.indices.alias.Alias;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequestBuilder;
import org.elasticsearch.action.admin.indices.alias.exists.AliasesExistResponse;
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesResponse;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
@ -31,7 +29,6 @@ import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.WriteRequest.RefreshPolicy;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.AliasAction;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.elasticsearch.cluster.metadata.AliasOrIndex;
import org.elasticsearch.cluster.metadata.IndexMetaData;
@ -52,14 +49,13 @@ import org.elasticsearch.test.ESIntegTestCase;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import static org.elasticsearch.client.Requests.createIndexRequest;
import static org.elasticsearch.client.Requests.indexRequest;
import static org.elasticsearch.cluster.metadata.AliasAction.Type.ADD;
import static org.elasticsearch.cluster.metadata.AliasAction.Type.REMOVE;
import static org.elasticsearch.cluster.metadata.IndexMetaData.INDEX_METADATA_BLOCK;
import static org.elasticsearch.cluster.metadata.IndexMetaData.INDEX_READ_ONLY_BLOCK;
import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_METADATA;
@ -114,25 +110,15 @@ public class IndexAliasesIT extends ESIntegTestCase {
logger.info("--> creating index [test]");
createIndex("test");
ensureGreen();
//invalid filter, invalid json
IndicesAliasesRequestBuilder indicesAliasesRequestBuilder = admin().indices().prepareAliases().addAlias("test", "alias1", "abcde");
try {
indicesAliasesRequestBuilder.get();
fail("put alias should have been failed due to invalid filter");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("failed to parse filter for alias [alias1]"));
}
Exception e = expectThrows(IllegalArgumentException.class,
() -> admin().indices().prepareAliases().addAlias("test", "alias1", "abcde").get());
assertThat(e.getMessage(), equalTo("failed to parse filter for alias [alias1]"));
//valid json , invalid filter
indicesAliasesRequestBuilder = admin().indices().prepareAliases().addAlias("test", "alias1", "{ \"test\": {} }");
try {
indicesAliasesRequestBuilder.get();
fail("put alias should have been failed due to invalid filter");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("failed to parse filter for alias [alias1]"));
}
// valid json , invalid filter
e = expectThrows(IllegalArgumentException.class,
() -> admin().indices().prepareAliases().addAlias("test", "alias1", "{ \"test\": {} }").get());
assertThat(e.getMessage(), equalTo("failed to parse filter for alias [alias1]"));
}
public void testFilteringAliases() throws Exception {
@ -598,7 +584,7 @@ public class IndexAliasesIT extends ESIntegTestCase {
.addAlias("foobar", "foo"));
assertAcked(admin().indices().prepareAliases()
.addAliasAction(new AliasAction(ADD, "foobar", "bac").routing("bla")));
.addAliasAction(AliasActions.add().index("foobar").alias("bac").routing("bla")));
logger.info("--> getting bar and baz for index bazbar");
getResponse = admin().indices().prepareGetAliases("bar", "bac").addIndices("bazbar").get();
@ -729,224 +715,6 @@ public class IndexAliasesIT extends ESIntegTestCase {
assertThat(existsResponse.exists(), equalTo(false));
}
public void testAddAliasNullWithoutExistingIndices() {
try {
assertAcked(admin().indices().prepareAliases().addAliasAction(AliasAction.newAddAliasAction(null, "alias1")));
fail("create alias should have failed due to null index");
} catch (IllegalArgumentException e) {
assertThat("Exception text does not contain \"Alias action [add]: [index/indices] may not be empty string\"",
e.getMessage(), containsString("Alias action [add]: [index/indices] may not be empty string"));
}
}
public void testAddAliasNullWithExistingIndices() throws Exception {
logger.info("--> creating index [test]");
createIndex("test");
ensureGreen();
logger.info("--> aliasing index [null] with [empty-alias]");
try {
assertAcked(admin().indices().prepareAliases().addAlias((String) null, "empty-alias"));
fail("create alias should have failed due to null index");
} catch (IllegalArgumentException e) {
assertThat("Exception text does not contain \"Alias action [add]: [index/indices] may not be empty string\"",
e.getMessage(), containsString("Alias action [add]: [index/indices] may not be empty string"));
}
}
public void testAddAliasEmptyIndex() {
try {
admin().indices().prepareAliases().addAliasAction(AliasAction.newAddAliasAction("", "alias1")).get();
fail("Expected ActionRequestValidationException");
} catch (ActionRequestValidationException e) {
assertThat(e.getMessage(), containsString("[index/indices] may not be empty string"));
}
try {
admin().indices().prepareAliases().addAliasAction(new AliasActions(ADD, "", "alias1")).get();
fail("Expected ActionRequestValidationException");
} catch (ActionRequestValidationException e) {
assertThat(e.getMessage(), containsString("[index/indices] may not be empty string"));
}
}
public void testAddAliasNullAlias() {
try {
admin().indices().prepareAliases().addAliasAction(AliasAction.newAddAliasAction("index1", null)).get();
fail("Expected ActionRequestValidationException");
} catch (ActionRequestValidationException e) {
assertThat(e.getMessage(), containsString("[alias/aliases] may not be empty string"));
}
try {
admin().indices().prepareAliases().addAliasAction(new AliasActions(ADD, "index1", (String)null)).get();
fail("Expected ActionRequestValidationException");
} catch (ActionRequestValidationException e) {
assertThat(e.getMessage(), containsString("[alias/aliases] may not be empty string"));
}
try {
admin().indices().prepareAliases().addAliasAction(new AliasActions(ADD, "index1", (String[])null)).get();
fail("Expected ActionRequestValidationException");
} catch (ActionRequestValidationException e) {
assertThat(e.getMessage(), containsString("[alias/aliases] is either missing or null"));
}
}
public void testAddAliasEmptyAlias() {
try {
admin().indices().prepareAliases().addAliasAction(AliasAction.newAddAliasAction("index1", "")).get();
fail("Expected ActionRequestValidationException");
} catch (ActionRequestValidationException e) {
assertThat(e.getMessage(), containsString("[alias/aliases] may not be empty string"));
}
try {
admin().indices().prepareAliases().addAliasAction(new AliasActions(ADD, "index1", "")).get();
fail("Expected ActionRequestValidationException");
} catch (ActionRequestValidationException e) {
assertThat(e.getMessage(), containsString("[alias/aliases] may not be empty string"));
}
}
public void testAddAliasNullAliasNullIndex() {
try {
admin().indices().prepareAliases().addAliasAction(AliasAction.newAddAliasAction(null, null)).get();
fail("Should throw " + ActionRequestValidationException.class.getSimpleName());
} catch (ActionRequestValidationException e) {
assertThat(e.validationErrors(), notNullValue());
assertThat(e.validationErrors().size(), equalTo(2));
}
try {
admin().indices().prepareAliases().addAliasAction(new AliasActions(ADD, null, (String)null)).get();
fail("Should throw " + ActionRequestValidationException.class.getSimpleName());
} catch (ActionRequestValidationException e) {
assertThat(e.validationErrors(), notNullValue());
assertThat(e.validationErrors().size(), equalTo(2));
}
}
public void testAddAliasEmptyAliasEmptyIndex() {
try {
admin().indices().prepareAliases().addAliasAction(AliasAction.newAddAliasAction("", "")).get();
fail("Should throw " + ActionRequestValidationException.class.getSimpleName());
} catch (ActionRequestValidationException e) {
assertThat(e.validationErrors(), notNullValue());
assertThat(e.validationErrors().size(), equalTo(2));
}
try {
admin().indices().prepareAliases().addAliasAction(new AliasActions(ADD, "", "")).get();
fail("Should throw " + ActionRequestValidationException.class.getSimpleName());
} catch (ActionRequestValidationException e) {
assertThat(e.validationErrors(), notNullValue());
assertThat(e.validationErrors().size(), equalTo(2));
}
}
public void testRemoveAliasNullIndex() {
try {
admin().indices().prepareAliases().addAliasAction(AliasAction.newRemoveAliasAction(null, "alias1")).get();
fail("Expected ActionRequestValidationException");
} catch (ActionRequestValidationException e) {
assertThat(e.getMessage(), containsString("[index/indices] may not be empty string"));
}
try {
admin().indices().prepareAliases().addAliasAction(new AliasActions(REMOVE, null, "alias1")).get();
fail("Expected ActionRequestValidationException");
} catch (ActionRequestValidationException e) {
assertThat(e.getMessage(), containsString("[index/indices] may not be empty string"));
}
}
public void testRemoveAliasEmptyIndex() {
try {
admin().indices().prepareAliases().addAliasAction(AliasAction.newRemoveAliasAction("", "alias1")).get();
fail("Expected ActionRequestValidationException");
} catch (ActionRequestValidationException e) {
assertThat(e.getMessage(), containsString("[index/indices] may not be empty string"));
}
try {
admin().indices().prepareAliases().addAliasAction(new AliasActions(REMOVE, "", "alias1")).get();
fail("Expected ActionRequestValidationException");
} catch (ActionRequestValidationException e) {
assertThat(e.getMessage(), containsString("[index/indices] may not be empty string"));
}
}
public void testRemoveAliasNullAlias() {
try {
admin().indices().prepareAliases().addAliasAction(AliasAction.newRemoveAliasAction("index1", null)).get();
fail("Expected ActionRequestValidationException");
} catch (ActionRequestValidationException e) {
assertThat(e.getMessage(), containsString("[alias/aliases] may not be empty string"));
}
try {
admin().indices().prepareAliases().addAliasAction(new AliasActions(REMOVE, "index1", (String)null)).get();
fail("Expected ActionRequestValidationException");
} catch (ActionRequestValidationException e) {
assertThat(e.getMessage(), containsString("[alias/aliases] may not be empty string"));
}
try {
admin().indices().prepareAliases().addAliasAction(new AliasActions(REMOVE, "index1", (String[])null)).get();
fail("Expected ActionRequestValidationException");
} catch (ActionRequestValidationException e) {
assertThat(e.getMessage(), containsString("[alias/aliases] is either missing or null"));
}
}
public void testRemoveAliasEmptyAlias() {
try {
admin().indices().prepareAliases().addAliasAction(AliasAction.newRemoveAliasAction("index1", "")).get();
fail("Expected ActionRequestValidationException");
} catch (ActionRequestValidationException e) {
assertThat(e.getMessage(), containsString("[alias/aliases] may not be empty string"));
}
try {
admin().indices().prepareAliases().addAliasAction(new AliasActions(REMOVE, "index1", "")).get();
fail("Expected ActionRequestValidationException");
} catch (ActionRequestValidationException e) {
assertThat(e.getMessage(), containsString("[alias/aliases] may not be empty string"));
}
}
public void testRemoveAliasNullAliasNullIndex() {
try {
admin().indices().prepareAliases().addAliasAction(AliasAction.newRemoveAliasAction(null, null)).get();
fail("Should throw " + ActionRequestValidationException.class.getSimpleName());
} catch (ActionRequestValidationException e) {
assertThat(e.validationErrors(), notNullValue());
assertThat(e.validationErrors().size(), equalTo(2));
}
try {
admin().indices().prepareAliases().addAliasAction(new AliasActions(REMOVE, null, (String)null)).get();
fail("Should throw " + ActionRequestValidationException.class.getSimpleName());
} catch (ActionRequestValidationException e) {
assertThat(e.validationErrors(), notNullValue());
assertThat(e.validationErrors().size(), equalTo(2));
}
try {
admin().indices().prepareAliases().addAliasAction(new AliasActions(REMOVE, (String[])null, (String[])null)).get();
fail("Should throw " + ActionRequestValidationException.class.getSimpleName());
} catch (ActionRequestValidationException e) {
assertThat(e.validationErrors(), notNullValue());
assertThat(e.validationErrors().size(), equalTo(2));
}
}
public void testRemoveAliasEmptyAliasEmptyIndex() {
try {
admin().indices().prepareAliases().addAliasAction(AliasAction.newAddAliasAction("", "")).get();
fail("Should throw " + ActionRequestValidationException.class.getSimpleName());
} catch (ActionRequestValidationException e) {
assertThat(e.validationErrors(), notNullValue());
assertThat(e.validationErrors().size(), equalTo(2));
}
try {
admin().indices().prepareAliases().addAliasAction(new AliasActions(REMOVE, "", "")).get();
fail("Should throw " + ActionRequestValidationException.class.getSimpleName());
} catch (ActionRequestValidationException e) {
assertThat(e.validationErrors(), notNullValue());
assertThat(e.validationErrors().size(), equalTo(2));
}
}
public void testGetAllAliasesWorks() {
createIndex("index1");
createIndex("index2");
@ -1113,6 +881,13 @@ public class IndexAliasesIT extends ESIntegTestCase {
}
}
public void testRemoveIndexAndReplaceWithAlias() throws InterruptedException, ExecutionException {
assertAcked(client().admin().indices().prepareCreate("test"));
indexRandom(true, client().prepareIndex("test_2", "test", "test").setSource("test", "test"));
assertAcked(client().admin().indices().prepareAliases().addAlias("test_2", "test").removeIndex("test"));
assertHitCount(client().prepareSearch("test").get(), 1);
}
private void checkAliases() {
GetAliasesResponse getAliasesResponse = admin().indices().prepareGetAliases("alias1").get();
assertThat(getAliasesResponse.getAliases().get("test").size(), equalTo(1));

View File

@ -0,0 +1,106 @@
/*
* 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.cluster.ClusterName;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.SnapshotsInProgress;
import org.elasticsearch.cluster.block.ClusterBlocks;
import org.elasticsearch.cluster.routing.RoutingTable;
import org.elasticsearch.cluster.routing.allocation.AllocationService;
import org.elasticsearch.cluster.routing.allocation.RoutingAllocation;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.repositories.IndexId;
import org.elasticsearch.snapshots.Snapshot;
import org.elasticsearch.snapshots.SnapshotId;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.VersionUtils;
import static java.util.Collections.singleton;
import static java.util.Collections.singletonList;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class MetaDataDeleteIndexServiceTests extends ESTestCase {
private final AllocationService allocationService = mock(AllocationService.class);
private final MetaDataDeleteIndexService service = new MetaDataDeleteIndexService(Settings.EMPTY, null, allocationService);
public void testDeleteMissing() {
Index index = new Index("missing", "doesn't matter");
ClusterState state = ClusterState.builder(ClusterName.DEFAULT).build();
IndexNotFoundException e = expectThrows(IndexNotFoundException.class, () -> service.deleteIndices(state, singleton(index)));
assertEquals(index, e.getIndex());
}
public void testDeleteSnapshotting() {
String index = randomAsciiOfLength(5);
Snapshot snapshot = new Snapshot("doesn't matter", new SnapshotId("snapshot name", "snapshot uuid"));
SnapshotsInProgress snaps = new SnapshotsInProgress(new SnapshotsInProgress.Entry(snapshot, true, false,
SnapshotsInProgress.State.INIT, singletonList(new IndexId(index, "doesn't matter")),
System.currentTimeMillis(), ImmutableOpenMap.of()));
ClusterState state = ClusterState.builder(clusterState(index))
.putCustom(SnapshotsInProgress.TYPE, snaps)
.build();
Exception e = expectThrows(IllegalArgumentException.class,
() -> service.deleteIndices(state, singleton(state.metaData().getIndices().get(index).getIndex())));
assertEquals("Cannot delete indices that are being snapshotted: [[" + index + "]]. Try again after snapshot finishes "
+ "or cancel the currently running snapshot.", e.getMessage());
}
public void testDeleteUnassigned() {
// Create an unassigned index
String index = randomAsciiOfLength(5);
ClusterState before = clusterState(index);
// Mock the built reroute
when(allocationService.reroute(any(ClusterState.class), any(String.class))).then(
i -> RoutingAllocation.Result.unchanged((ClusterState) i.getArguments()[0]));
// Remove it
ClusterState after = service.deleteIndices(before, singleton(before.metaData().getIndices().get(index).getIndex()));
// It is gone
assertNull(after.metaData().getIndices().get(index));
assertNull(after.routingTable().index(index));
assertNull(after.blocks().indices().get(index));
// Make sure we actually attempted to reroute
verify(allocationService).reroute(any(ClusterState.class), any(String.class));
}
private ClusterState clusterState(String index) {
IndexMetaData indexMetaData = IndexMetaData.builder(index)
.settings(Settings.builder().put("index.version.created", VersionUtils.randomVersion(random())))
.numberOfShards(1)
.numberOfReplicas(1)
.build();
return ClusterState.builder(ClusterName.DEFAULT)
.metaData(MetaData.builder().put(indexMetaData, false))
.routingTable(RoutingTable.builder().addAsNew(indexMetaData).build())
.blocks(ClusterBlocks.builder().addBlocks(indexMetaData))
.build();
}
}

View File

@ -0,0 +1,138 @@
/*
* 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.cluster.ClusterName;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.VersionUtils;
import java.util.Arrays;
import java.util.Collection;
import static java.util.Collections.singletonList;
import static org.hamcrest.Matchers.contains;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyCollectionOf;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class MetaDataIndexAliasesServiceTests extends ESTestCase {
private final AliasValidator aliasValidator = new AliasValidator(Settings.EMPTY);
private final MetaDataDeleteIndexService deleteIndexService = mock(MetaDataDeleteIndexService.class);
private final MetaDataIndexAliasesService service = new MetaDataIndexAliasesService(Settings.EMPTY, null, null, aliasValidator,
null, deleteIndexService);
public MetaDataIndexAliasesServiceTests() {
// Mock any deletes so we don't need to worry about how MetaDataDeleteIndexService does its job
when(deleteIndexService.deleteIndices(any(ClusterState.class), anyCollectionOf(Index.class))).then(i -> {
ClusterState state = (ClusterState) i.getArguments()[0];
@SuppressWarnings("unchecked")
Collection<Index> indices = (Collection<Index>) i.getArguments()[1];
MetaData.Builder meta = MetaData.builder(state.metaData());
for (Index index : indices) {
assertTrue("index now found", state.metaData().hasConcreteIndex(index.getName()));
meta.remove(index.getName()); // We only think about metadata for this test. Not routing or any other fun stuff.
}
return ClusterState.builder(state).metaData(meta).build();
});
}
public void testAddAndRemove() {
// Create a state with a single index
String index = randomAsciiOfLength(5);
ClusterState before = createIndex(ClusterState.builder(ClusterName.DEFAULT).build(), index);
// Add an alias to it
ClusterState after = service.innerExecute(before, singletonList(new AliasAction.Add(index, "test", null, null, null)));
AliasOrIndex alias = after.metaData().getAliasAndIndexLookup().get("test");
assertNotNull(alias);
assertTrue(alias.isAlias());
assertThat(alias.getIndices(), contains(after.metaData().index(index)));
// Remove the alias from it while adding another one
before = after;
after = service.innerExecute(before, Arrays.asList(
new AliasAction.Remove(index, "test"),
new AliasAction.Add(index, "test_2", null, null, null)));
assertNull(after.metaData().getAliasAndIndexLookup().get("test"));
alias = after.metaData().getAliasAndIndexLookup().get("test_2");
assertNotNull(alias);
assertTrue(alias.isAlias());
assertThat(alias.getIndices(), contains(after.metaData().index(index)));
// Now just remove on its own
before = after;
after = service.innerExecute(before, singletonList(new AliasAction.Remove(index, "test_2")));
assertNull(after.metaData().getAliasAndIndexLookup().get("test"));
assertNull(after.metaData().getAliasAndIndexLookup().get("test_2"));
}
public void testSwapIndexWithAlias() {
// Create "test" and "test_2"
ClusterState before = createIndex(ClusterState.builder(ClusterName.DEFAULT).build(), "test");
before = createIndex(before, "test_2");
// Now remove "test" and add an alias to "test" to "test_2" in one go
ClusterState after = service.innerExecute(before, Arrays.asList(
new AliasAction.Add("test_2", "test", null, null, null),
new AliasAction.RemoveIndex("test")));
AliasOrIndex alias = after.metaData().getAliasAndIndexLookup().get("test");
assertNotNull(alias);
assertTrue(alias.isAlias());
assertThat(alias.getIndices(), contains(after.metaData().index("test_2")));
}
public void testAddAliasToRemovedIndex() {
// Create "test"
ClusterState before = createIndex(ClusterState.builder(ClusterName.DEFAULT).build(), "test");
// Attempt to add an alias to "test" at the same time as we remove it
IndexNotFoundException e = expectThrows(IndexNotFoundException.class, () -> service.innerExecute(before, Arrays.asList(
new AliasAction.Add("test", "alias", null, null, null),
new AliasAction.RemoveIndex("test"))));
assertEquals("test", e.getIndex().getName());
}
public void testRemoveIndexTwice() {
// Create "test"
ClusterState before = createIndex(ClusterState.builder(ClusterName.DEFAULT).build(), "test");
// Try to remove an index twice. This should just remove the index once....
ClusterState after = service.innerExecute(before, Arrays.asList(
new AliasAction.RemoveIndex("test"),
new AliasAction.RemoveIndex("test")));
assertNull(after.metaData().getAliasAndIndexLookup().get("test"));
}
private ClusterState createIndex(ClusterState state, String index) {
IndexMetaData indexMetaData = IndexMetaData.builder(index)
.settings(Settings.builder().put("index.version.created", VersionUtils.randomVersion(random())))
.numberOfShards(1)
.numberOfReplicas(1)
.build();
return ClusterState.builder(state)
.metaData(MetaData.builder(state.metaData()).put(indexMetaData, false))
.build();
}
}

View File

@ -20,7 +20,7 @@ package org.elasticsearch.cluster.shards;
import org.elasticsearch.action.admin.cluster.shards.ClusterSearchShardsGroup;
import org.elasticsearch.action.admin.cluster.shards.ClusterSearchShardsResponse;
import org.elasticsearch.cluster.metadata.AliasAction;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
import org.elasticsearch.common.Priority;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.test.ESIntegTestCase;
@ -99,9 +99,9 @@ public class ClusterSearchShardsIT extends ESIntegTestCase {
client().admin().indices().prepareCreate("test2").setSettings(Settings.builder()
.put("index.number_of_shards", "4").put("index.number_of_replicas", 1)).execute().actionGet();
client().admin().indices().prepareAliases()
.addAliasAction(AliasAction.newAddAliasAction("test1", "routing_alias").routing("ABC"))
.addAliasAction(AliasAction.newAddAliasAction("test2", "routing_alias").routing("EFG"))
.execute().actionGet();
.addAliasAction(AliasActions.add().index("test1").alias("routing_alias").routing("ABC"))
.addAliasAction(AliasActions.add().index("test2").alias("routing_alias").routing("EFG"))
.get();
client().admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).setWaitForGreenStatus().execute().actionGet();
ClusterSearchShardsResponse response = client().admin().cluster().prepareSearchShards("routing_alias").execute().actionGet();

View File

@ -18,12 +18,9 @@
*/
package org.elasticsearch.indices;
import org.apache.lucene.store.LockObtainFailedException;
import org.elasticsearch.Version;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.AliasAction;
import org.elasticsearch.cluster.metadata.IndexGraveyard;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.MetaData;
@ -250,14 +247,11 @@ public class IndicesServiceTests extends ESSingleNodeTestCase {
public void testDanglingIndicesWithAliasConflict() throws Exception {
final String indexName = "test-idx1";
final String alias = "test-alias";
final IndicesService indicesService = getIndicesService();
final ClusterService clusterService = getInstanceFromNode(ClusterService.class);
final IndexService test = createIndex(indexName);
createIndex(indexName);
// create the alias for the index
AliasAction action = new AliasAction(AliasAction.Type.ADD, indexName, alias);
IndicesAliasesRequest request = new IndicesAliasesRequest().addAliasAction(action);
client().admin().indices().aliases(request).actionGet();
client().admin().indices().prepareAliases().addAlias(indexName, alias).get();
final ClusterState originalState = clusterService.state();
// try to import a dangling index with the same name as the alias, it should fail
@ -276,9 +270,7 @@ public class IndicesServiceTests extends ESSingleNodeTestCase {
assertThat(clusterService.state(), equalTo(originalState));
// remove the alias
action = new AliasAction(AliasAction.Type.REMOVE, indexName, alias);
request = new IndicesAliasesRequest().addAliasAction(action);
client().admin().indices().aliases(request).actionGet();
client().admin().indices().prepareAliases().removeAlias(indexName, alias).get();
// now try importing a dangling index with the same name as the alias, it should succeed.
listener = new DanglingListener();

View File

@ -19,6 +19,7 @@
package org.elasticsearch.routing;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
@ -30,7 +31,6 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import static org.elasticsearch.cluster.metadata.AliasAction.newAddAliasAction;
import static org.elasticsearch.common.util.set.Sets.newHashSet;
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
@ -62,14 +62,15 @@ public class AliasResolveRoutingIT extends ESIntegTestCase {
createIndex("test2");
client().admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).setWaitForGreenStatus().execute().actionGet();
client().admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test1", "alias")).execute().actionGet();
client().admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test1", "alias10").routing("0")).execute().actionGet();
client().admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test1", "alias110").searchRouting("1,0")).execute().actionGet();
client().admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test1", "alias12").routing("2")).execute().actionGet();
client().admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test2", "alias20").routing("0")).execute().actionGet();
client().admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test2", "alias21").routing("1")).execute().actionGet();
client().admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test1", "alias0").routing("0")).execute().actionGet();
client().admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test2", "alias0").routing("0")).execute().actionGet();
client().admin().indices().prepareAliases()
.addAliasAction(AliasActions.add().index("test1").alias("alias"))
.addAliasAction(AliasActions.add().index("test1").alias("alias10").routing("0"))
.addAliasAction(AliasActions.add().index("test1").alias("alias110").searchRouting("1,0"))
.addAliasAction(AliasActions.add().index("test1").alias("alias12").routing("2"))
.addAliasAction(AliasActions.add().index("test2").alias("alias20").routing("0"))
.addAliasAction(AliasActions.add().index("test2").alias("alias21").routing("1"))
.addAliasAction(AliasActions.add().index("test1").alias("alias0").routing("0"))
.addAliasAction(AliasActions.add().index("test2").alias("alias0").routing("0")).get();
assertThat(clusterService().state().metaData().resolveIndexRouting(null, null, "test1"), nullValue());
assertThat(clusterService().state().metaData().resolveIndexRouting(null, null, "alias"), nullValue());
@ -103,12 +104,13 @@ public class AliasResolveRoutingIT extends ESIntegTestCase {
createIndex("test2");
client().admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).setWaitForGreenStatus().execute().actionGet();
client().admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test1", "alias")).execute().actionGet();
client().admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test1", "alias10").routing("0")).execute().actionGet();
client().admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test2", "alias20").routing("0")).execute().actionGet();
client().admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test2", "alias21").routing("1")).execute().actionGet();
client().admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test1", "alias0").routing("0")).execute().actionGet();
client().admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test2", "alias0").routing("0")).execute().actionGet();
client().admin().indices().prepareAliases()
.addAliasAction(AliasActions.add().index("test1").alias("alias"))
.addAliasAction(AliasActions.add().index("test1").alias("alias10").routing("0"))
.addAliasAction(AliasActions.add().index("test2").alias("alias20").routing("0"))
.addAliasAction(AliasActions.add().index("test2").alias("alias21").routing("1"))
.addAliasAction(AliasActions.add().index("test1").alias("alias0").routing("0"))
.addAliasAction(AliasActions.add().index("test2").alias("alias0").routing("0")).get();
ClusterState state = clusterService().state();
IndexNameExpressionResolver indexNameExpressionResolver = internalCluster().getInstance(IndexNameExpressionResolver.class);

View File

@ -19,6 +19,7 @@
package org.elasticsearch.routing;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.action.support.WriteRequest.RefreshPolicy;
@ -26,12 +27,11 @@ import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.test.ESIntegTestCase;
import static org.elasticsearch.cluster.metadata.AliasAction.newAddAliasAction;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
import static org.hamcrest.Matchers.equalTo;
/**
*
* Test aliases with routing.
*/
public class AliasRoutingIT extends ESIntegTestCase {
@ -43,7 +43,7 @@ public class AliasRoutingIT extends ESIntegTestCase {
public void testAliasCrudRouting() throws Exception {
createIndex("test");
ensureGreen();
assertAcked(admin().indices().prepareAliases().addAliasAction(newAddAliasAction("test", "alias0").routing("0")));
assertAcked(admin().indices().prepareAliases().addAliasAction(AliasActions.add().index("test").alias("alias0").routing("0")));
logger.info("--> indexing with id [1], and routing [0] using alias");
client().prepareIndex("alias0", "type1", "1").setSource("field", "value1").setRefreshPolicy(RefreshPolicy.IMMEDIATE).get();
@ -105,10 +105,10 @@ public class AliasRoutingIT extends ESIntegTestCase {
createIndex("test");
ensureGreen();
assertAcked(admin().indices().prepareAliases()
.addAliasAction(newAddAliasAction("test", "alias"))
.addAliasAction(newAddAliasAction("test", "alias0").routing("0"))
.addAliasAction(newAddAliasAction("test", "alias1").routing("1"))
.addAliasAction(newAddAliasAction("test", "alias01").searchRouting("0,1")));
.addAliasAction(AliasActions.add().index("test").alias("alias"))
.addAliasAction(AliasActions.add().index("test").alias("alias0").routing("0"))
.addAliasAction(AliasActions.add().index("test").alias("alias1").routing("1"))
.addAliasAction(AliasActions.add().index("test").alias("alias01").searchRouting("0,1")));
logger.info("--> indexing with id [1], and routing [0] using alias");
client().prepareIndex("alias0", "type1", "1").setSource("field", "value1").setRefreshPolicy(RefreshPolicy.IMMEDIATE).get();
@ -200,12 +200,12 @@ public class AliasRoutingIT extends ESIntegTestCase {
createIndex("test-b");
ensureGreen();
assertAcked(admin().indices().prepareAliases()
.addAliasAction(newAddAliasAction("test-a", "alias-a0").routing("0"))
.addAliasAction(newAddAliasAction("test-a", "alias-a1").routing("1"))
.addAliasAction(newAddAliasAction("test-b", "alias-b0").routing("0"))
.addAliasAction(newAddAliasAction("test-b", "alias-b1").routing("1"))
.addAliasAction(newAddAliasAction("test-a", "alias-ab").searchRouting("0"))
.addAliasAction(newAddAliasAction("test-b", "alias-ab").searchRouting("1")));
.addAliasAction(AliasActions.add().index("test-a").alias("alias-a0").routing("0"))
.addAliasAction(AliasActions.add().index("test-a").alias("alias-a1").routing("1"))
.addAliasAction(AliasActions.add().index("test-b").alias("alias-b0").routing("0"))
.addAliasAction(AliasActions.add().index("test-b").alias("alias-b1").routing("1"))
.addAliasAction(AliasActions.add().index("test-a").alias("alias-ab").searchRouting("0"))
.addAliasAction(AliasActions.add().index("test-b").alias("alias-ab").searchRouting("1")));
ensureGreen(); // wait for events again to make sure we got the aliases on all nodes
logger.info("--> indexing with id [1], and routing [0] using alias to test-a");
client().prepareIndex("alias-a0", "type1", "1").setSource("field", "value1").setRefreshPolicy(RefreshPolicy.IMMEDIATE).get();
@ -259,7 +259,7 @@ public class AliasRoutingIT extends ESIntegTestCase {
createIndex("index", "index_2");
ensureGreen();
assertAcked(admin().indices().prepareAliases()
.addAliasAction(newAddAliasAction("index", "index_1").routing("1")));
.addAliasAction(AliasActions.add().index("index").alias("index_1").routing("1")));
logger.info("--> indexing on index_1 which is an alias for index with routing [1]");
client().prepareIndex("index_1", "type1", "1").setSource("field", "value1").setRefreshPolicy(RefreshPolicy.IMMEDIATE).get();
@ -284,7 +284,7 @@ public class AliasRoutingIT extends ESIntegTestCase {
createIndex("index", "index_2");
ensureGreen();
assertAcked(admin().indices().prepareAliases()
.addAliasAction(newAddAliasAction("index", "index_1").routing("1")));
.addAliasAction(AliasActions.add().index("index").alias("index_1").routing("1")));
logger.info("--> indexing on index_1 which is an alias for index with routing [1]");
client().prepareIndex("index_1", "type1", "1").setSource("field", "value1").setRefreshPolicy(RefreshPolicy.IMMEDIATE).get();
@ -305,7 +305,7 @@ public class AliasRoutingIT extends ESIntegTestCase {
ensureGreen();
logger.info("--> creating alias with routing [3]");
assertAcked(admin().indices().prepareAliases()
.addAliasAction(newAddAliasAction("test", "alias").routing("3")));
.addAliasAction(AliasActions.add().index("test").alias("alias").routing("3")));
logger.info("--> indexing with id [0], and routing [3]");
client().prepareIndex("alias", "type1", "0").setSource("field", "value1").setRefreshPolicy(RefreshPolicy.IMMEDIATE).get();
@ -320,7 +320,7 @@ public class AliasRoutingIT extends ESIntegTestCase {
logger.info("--> creating alias with routing [4]");
assertAcked(admin().indices().prepareAliases()
.addAliasAction(newAddAliasAction("test", "alias").routing("4")));
.addAliasAction(AliasActions.add().index("test").alias("alias").routing("4")));
logger.info("--> verifying search with wrong routing should not find");
for (int i = 0; i < 5; i++) {
@ -330,7 +330,7 @@ public class AliasRoutingIT extends ESIntegTestCase {
logger.info("--> creating alias with search routing [3,4] and index routing 4");
assertAcked(client().admin().indices().prepareAliases()
.addAliasAction(newAddAliasAction("test", "alias").searchRouting("3,4").indexRouting("4")));
.addAliasAction(AliasActions.add().index("test").alias("alias").searchRouting("3,4").indexRouting("4")));
logger.info("--> indexing with id [1], and routing [4]");
client().prepareIndex("alias", "type1", "1").setSource("field", "value2").setRefreshPolicy(RefreshPolicy.IMMEDIATE).get();

View File

@ -38,7 +38,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import static org.elasticsearch.client.Requests.indexAliasesRequest;
import static org.elasticsearch.client.Requests.indexRequest;
import static org.elasticsearch.client.Requests.refreshRequest;
import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF_REPLICAS;
@ -108,8 +107,9 @@ public class MoreLikeThisIT extends ESIntegTestCase {
.startObject("text").field("type", "text").endObject()
.endObject().endObject().endObject()));
logger.info("Creating aliases alias release");
client().admin().indices().aliases(indexAliasesRequest().addAlias("release", termQuery("text", "release"), "test")).actionGet();
client().admin().indices().aliases(indexAliasesRequest().addAlias("beta", termQuery("text", "beta"), "test")).actionGet();
client().admin().indices().prepareAliases()
.addAlias("test", "release", termQuery("text", "release"))
.addAlias("test", "beta", termQuery("text", "beta")).get();
logger.info("Running Cluster Health");
assertThat(ensureGreen(), equalTo(ClusterHealthStatus.GREEN));
@ -155,8 +155,8 @@ public class MoreLikeThisIT extends ESIntegTestCase {
.startObject("properties")
.endObject()
.endObject().endObject().string();
client().admin().indices().prepareCreate(indexName).addMapping(typeName, mapping).execute().actionGet();
client().admin().indices().aliases(indexAliasesRequest().addAlias(aliasName, indexName)).actionGet();
client().admin().indices().prepareCreate(indexName).addMapping(typeName, mapping).get();
client().admin().indices().prepareAliases().addAlias(indexName, aliasName).get();
assertThat(ensureGreen(), equalTo(ClusterHealthStatus.GREEN));

View File

@ -24,7 +24,7 @@ POST /_aliases
// CONSOLE
// TEST[s/^/PUT test1\nPUT test2\n/]
An alias can also be removed, for example:
And here is removing that same alias:
[source,js]
--------------------------------------------------
@ -109,6 +109,25 @@ indices that match this pattern are added/removed.
It is an error to index to an alias which points to more than one index.
It is also possible to swap an index with an alias in one operation:
[source,js]
--------------------------------------------------
PUT test <1>
PUT test_2 <2>
POST /_aliases
{
"actions" : [
{ "add": { "index": "test_2", "alias": "test" } },
{ "remove_index": { "index": "test" } } <3>
]
}
--------------------------------------------------
// CONSOLE
<1> An index we've added by mistake
<2> The index we should have added
<3> `remove_index` is just like <<indices-delete-index>>
[float]
[[filtered]]
=== Filtered Aliases

View File

@ -57,3 +57,10 @@ that the multi-index matching and expansion is not confused. It was previously
possible (but a really bad idea) to create indices starting with a hyphen or
plus sign. Any index already existing with these preceding characters will
continue to work normally.
==== Aliases API
The `/_aliases` API no longer supports `indexRouting` and `index-routing`, only
`index_routing`. It also no longer support `searchRouting` and `search-routing`,
only `search_routing`. These were removed because they were untested and we
prefer there to be only one (obvious) way to do things like this.

View File

@ -82,3 +82,87 @@
- match: {test_index.aliases.test_alias1: {'index_routing': 'routing_value', 'search_routing': 'routing_value'}}
- match: {test_index.aliases.test_alias2: {'index_routing': 'routing_value', 'search_routing': 'routing_value'}}
---
"Remove alias":
- do:
indices.create:
index: test_index
- do:
indices.exists_alias:
name: test_alias1
- is_false: ''
- do:
indices.exists_alias:
name: test_alias2
- is_false: ''
- do:
indices.exists_alias:
name: test_alias3
- is_false: ''
- do:
indices.update_aliases:
body:
actions:
- add:
index: test_index
aliases: [test_alias1, test_alias2]
- do:
indices.exists_alias:
name: test_alias1
- is_true: ''
- do:
indices.exists_alias:
name: test_alias2
- is_true: ''
- do:
indices.exists_alias:
name: test_alias3
- is_false: ''
- do:
indices.update_aliases:
body:
actions:
- remove:
index: test_index
alias: test_alias1
- add:
index: test_index
alias: test_alias3
- do:
indices.exists_alias:
name: test_alias1
- is_false: ''
- do:
indices.exists_alias:
name: test_alias2
- is_true: ''
- do:
indices.exists_alias:
name: test_alias3
- is_true: ''
- do:
indices.update_aliases:
body:
actions:
- remove:
index: test_index
alias: test_alias2
- remove:
index: test_index
alias: test_alias3
- do:
indices.exists_alias:
name: test_alias1
- is_false: ''
- do:
indices.exists_alias:
name: test_alias2
- is_false: ''
- do:
indices.exists_alias:
name: test_alias3
- is_false: ''

View File

@ -0,0 +1,34 @@
---
"Remove and index and replace it with an alias":
- do:
indices.create:
index: test
- do:
indices.create:
index: test_2
- do:
indices.update_aliases:
body:
actions:
- add:
index: test_2
aliases: [test, test_write]
- remove_index:
index: test
- do:
indices.exists_alias:
name: test
- is_true: ''
- do:
indices.exists_alias:
name: test_write
- is_true: ''
- do:
indices.get_mapping:
index: test
- is_true: test_2 # the name of the index that the alias points to, would be `test` if the index were still there