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:
parent
70f4d718f8
commit
df73292256
|
@ -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" />
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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: ''
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue