Do not allow modify aliases on followers (#43017)
Now that aliases are replicated by a follower from its leader, this commit prevents directly modifying aliases on follower indices.
This commit is contained in:
parent
0ebcb21d2c
commit
63bad28005
|
@ -85,6 +85,7 @@ import org.elasticsearch.action.admin.cluster.storedscripts.TransportPutStoredSc
|
|||
import org.elasticsearch.action.admin.cluster.tasks.PendingClusterTasksAction;
|
||||
import org.elasticsearch.action.admin.cluster.tasks.TransportPendingClusterTasksAction;
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesAction;
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||
import org.elasticsearch.action.admin.indices.alias.TransportIndicesAliasesAction;
|
||||
import org.elasticsearch.action.admin.indices.alias.exists.AliasesExistAction;
|
||||
import org.elasticsearch.action.admin.indices.alias.exists.TransportAliasesExistAction;
|
||||
|
@ -362,6 +363,7 @@ public class ActionModule extends AbstractModule {
|
|||
private final DestructiveOperations destructiveOperations;
|
||||
private final RestController restController;
|
||||
private final RequestValidators<PutMappingRequest> mappingRequestValidators;
|
||||
private final RequestValidators<IndicesAliasesRequest> indicesAliasesRequestRequestValidators;
|
||||
|
||||
public ActionModule(boolean transportClient, Settings settings, IndexNameExpressionResolver indexNameExpressionResolver,
|
||||
IndexScopedSettings indexScopedSettings, ClusterSettings clusterSettings, SettingsFilter settingsFilter,
|
||||
|
@ -395,6 +397,8 @@ public class ActionModule extends AbstractModule {
|
|||
}
|
||||
mappingRequestValidators = new RequestValidators<>(
|
||||
actionPlugins.stream().flatMap(p -> p.mappingRequestValidators().stream()).collect(Collectors.toList()));
|
||||
indicesAliasesRequestRequestValidators = new RequestValidators<>(
|
||||
actionPlugins.stream().flatMap(p -> p.indicesAliasesRequestValidators().stream()).collect(Collectors.toList()));
|
||||
|
||||
if (transportClient) {
|
||||
restController = null;
|
||||
|
@ -692,6 +696,7 @@ public class ActionModule extends AbstractModule {
|
|||
bind(ActionFilters.class).toInstance(actionFilters);
|
||||
bind(DestructiveOperations.class).toInstance(destructiveOperations);
|
||||
bind(new TypeLiteral<RequestValidators<PutMappingRequest>>() {}).toInstance(mappingRequestValidators);
|
||||
bind(new TypeLiteral<RequestValidators<IndicesAliasesRequest>>() {}).toInstance(indicesAliasesRequestRequestValidators);
|
||||
|
||||
if (false == transportClient) {
|
||||
// Supporting classes only used when not a transport client
|
||||
|
|
|
@ -63,6 +63,7 @@ import static org.elasticsearch.common.xcontent.ObjectParser.fromList;
|
|||
public class IndicesAliasesRequest extends AcknowledgedRequest<IndicesAliasesRequest> implements ToXContentObject {
|
||||
|
||||
private List<AliasActions> allAliasActions = new ArrayList<>();
|
||||
private String origin = "";
|
||||
|
||||
// indices options that require every specified index to exist, expand wildcards only to open
|
||||
// indices, don't allow that no indices are resolved from wildcard expressions and resolve the
|
||||
|
@ -535,6 +536,15 @@ public class IndicesAliasesRequest extends AcknowledgedRequest<IndicesAliasesReq
|
|||
}
|
||||
}
|
||||
|
||||
public String origin() {
|
||||
return origin;
|
||||
}
|
||||
|
||||
public IndicesAliasesRequest origin(final String origin) {
|
||||
this.origin = Objects.requireNonNull(origin);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the action to this request and validate it.
|
||||
*/
|
||||
|
@ -565,12 +575,23 @@ public class IndicesAliasesRequest extends AcknowledgedRequest<IndicesAliasesReq
|
|||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
allAliasActions = in.readList(AliasActions::new);
|
||||
if (in.getVersion().onOrAfter(Version.V_7_3_0)) {
|
||||
origin = in.readOptionalString();
|
||||
} else {
|
||||
origin = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
out.writeList(allAliasActions);
|
||||
// noinspection StatementWithEmptyBody
|
||||
if (out.getVersion().onOrAfter(Version.V_7_3_0)) {
|
||||
out.writeOptionalString(origin);
|
||||
} else {
|
||||
// nothing to do here, here for symmetry with IndicesAliasesRequest#readFrom
|
||||
}
|
||||
}
|
||||
|
||||
public IndicesOptions indicesOptions() {
|
||||
|
|
|
@ -21,6 +21,7 @@ package org.elasticsearch.action.admin.indices.alias;
|
|||
|
||||
import com.carrotsearch.hppc.cursors.ObjectCursor;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.RequestValidators;
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
|
||||
import org.elasticsearch.action.support.ActionFilters;
|
||||
import org.elasticsearch.action.support.master.AcknowledgedResponse;
|
||||
|
@ -37,6 +38,7 @@ import org.elasticsearch.cluster.metadata.MetaDataIndexAliasesService;
|
|||
import org.elasticsearch.cluster.service.ClusterService;
|
||||
import org.elasticsearch.common.collect.ImmutableOpenMap;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.index.Index;
|
||||
import org.elasticsearch.rest.action.admin.indices.AliasesNotFoundException;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
|
@ -45,6 +47,8 @@ import java.util.ArrayList;
|
|||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.Collections.unmodifiableList;
|
||||
|
@ -55,14 +59,21 @@ import static java.util.Collections.unmodifiableList;
|
|||
public class TransportIndicesAliasesAction extends TransportMasterNodeAction<IndicesAliasesRequest, AcknowledgedResponse> {
|
||||
|
||||
private final MetaDataIndexAliasesService indexAliasesService;
|
||||
private final RequestValidators<IndicesAliasesRequest> requestValidators;
|
||||
|
||||
@Inject
|
||||
public TransportIndicesAliasesAction(TransportService transportService, ClusterService clusterService,
|
||||
ThreadPool threadPool, MetaDataIndexAliasesService indexAliasesService,
|
||||
ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver) {
|
||||
public TransportIndicesAliasesAction(
|
||||
final TransportService transportService,
|
||||
final ClusterService clusterService,
|
||||
final ThreadPool threadPool,
|
||||
final MetaDataIndexAliasesService indexAliasesService,
|
||||
final ActionFilters actionFilters,
|
||||
final IndexNameExpressionResolver indexNameExpressionResolver,
|
||||
final RequestValidators<IndicesAliasesRequest> requestValidators) {
|
||||
super(IndicesAliasesAction.NAME, transportService, clusterService, threadPool, actionFilters, indexNameExpressionResolver,
|
||||
IndicesAliasesRequest::new);
|
||||
this.indexAliasesService = indexAliasesService;
|
||||
this.requestValidators = Objects.requireNonNull(requestValidators);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -96,23 +107,28 @@ public class TransportIndicesAliasesAction extends TransportMasterNodeAction<Ind
|
|||
// Resolve all the AliasActions into AliasAction instances and gather all the aliases
|
||||
Set<String> aliases = new HashSet<>();
|
||||
for (AliasActions action : actions) {
|
||||
String[] concreteIndices = indexNameExpressionResolver.concreteIndexNames(state, request.indicesOptions(), action.indices());
|
||||
final Index[] concreteIndices = indexNameExpressionResolver.concreteIndices(state, request.indicesOptions(), action.indices());
|
||||
final Optional<Exception> maybeException = requestValidators.validateRequest(request, state, concreteIndices);
|
||||
if (maybeException.isPresent()) {
|
||||
listener.onFailure(maybeException.get());
|
||||
return;
|
||||
}
|
||||
Collections.addAll(aliases, action.getOriginalAliases());
|
||||
for (String index : concreteIndices) {
|
||||
for (final Index index : concreteIndices) {
|
||||
switch (action.actionType()) {
|
||||
case ADD:
|
||||
for (String alias : concreteAliases(action, state.metaData(), index)) {
|
||||
finalActions.add(new AliasAction.Add(index, alias, action.filter(), action.indexRouting(),
|
||||
for (String alias : concreteAliases(action, state.metaData(), index.getName())) {
|
||||
finalActions.add(new AliasAction.Add(index.getName(), alias, action.filter(), action.indexRouting(),
|
||||
action.searchRouting(), action.writeIndex()));
|
||||
}
|
||||
break;
|
||||
case REMOVE:
|
||||
for (String alias : concreteAliases(action, state.metaData(), index)) {
|
||||
finalActions.add(new AliasAction.Remove(index, alias));
|
||||
for (String alias : concreteAliases(action, state.metaData(), index.getName())) {
|
||||
finalActions.add(new AliasAction.Remove(index.getName(), alias));
|
||||
}
|
||||
break;
|
||||
case REMOVE_INDEX:
|
||||
finalActions.add(new AliasAction.RemoveIndex(index));
|
||||
finalActions.add(new AliasAction.RemoveIndex(index.getName()));
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported action [" + action.actionType() + "]");
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
package org.elasticsearch.action.admin.indices.mapping.put;
|
||||
|
||||
import com.carrotsearch.hppc.ObjectHashSet;
|
||||
|
||||
import org.elasticsearch.ElasticsearchGenerationException;
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.action.ActionRequestValidationException;
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.elasticsearch.action.Action;
|
|||
import org.elasticsearch.action.ActionRequest;
|
||||
import org.elasticsearch.action.ActionResponse;
|
||||
import org.elasticsearch.action.RequestValidators;
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
|
||||
import org.elasticsearch.action.support.ActionFilter;
|
||||
import org.elasticsearch.action.support.TransportAction;
|
||||
|
@ -190,4 +191,8 @@ public interface ActionPlugin {
|
|||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
default Collection<RequestValidators.RequestValidator<IndicesAliasesRequest>> indicesAliasesRequestValidators() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* 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.RequestValidators;
|
||||
import org.elasticsearch.action.admin.indices.alias.exists.AliasesExistResponse;
|
||||
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
|
||||
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesResponse;
|
||||
import org.elasticsearch.cluster.metadata.AliasMetaData;
|
||||
import org.elasticsearch.common.settings.Setting;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.index.Index;
|
||||
import org.elasticsearch.plugins.ActionPlugin;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.test.ESSingleNodeTestCase;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.hasToString;
|
||||
|
||||
public class ValidateIndiesAliasesRequestIT extends ESSingleNodeTestCase {
|
||||
|
||||
public static class IndicesAliasesPlugin extends Plugin implements ActionPlugin {
|
||||
|
||||
static final Setting<List<String>> ALLOWED_ORIGINS_SETTING = Setting.listSetting(
|
||||
"index.aliases.allowed_origins",
|
||||
Collections.emptyList(),
|
||||
Function.identity(),
|
||||
Setting.Property.IndexScope,
|
||||
Setting.Property.Dynamic);
|
||||
|
||||
@Override
|
||||
public List<Setting<?>> getSettings() {
|
||||
return Collections.singletonList(ALLOWED_ORIGINS_SETTING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<RequestValidators.RequestValidator<IndicesAliasesRequest>> indicesAliasesRequestValidators() {
|
||||
return Collections.singletonList((request, state, indices) -> {
|
||||
for (final Index index : indices) {
|
||||
final List<String> allowedOrigins = ALLOWED_ORIGINS_SETTING.get(state.metaData().index(index).getSettings());
|
||||
if (allowedOrigins.contains(request.origin()) == false) {
|
||||
final String message = String.format(
|
||||
Locale.ROOT,
|
||||
"origin [%s] not allowed for index [%s]",
|
||||
request.origin(),
|
||||
index.getName());
|
||||
return Optional.of(new IllegalStateException(message));
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<Class<? extends Plugin>> getPlugins() {
|
||||
return Collections.singletonList(IndicesAliasesPlugin.class);
|
||||
}
|
||||
|
||||
public void testAllowed() {
|
||||
final Settings settings = Settings.builder()
|
||||
.putList(IndicesAliasesPlugin.ALLOWED_ORIGINS_SETTING.getKey(), Collections.singletonList("allowed"))
|
||||
.build();
|
||||
createIndex("index", settings);
|
||||
final IndicesAliasesRequest request = new IndicesAliasesRequest().origin("allowed");
|
||||
request.addAliasAction(IndicesAliasesRequest.AliasActions.add().index("index").alias("alias"));
|
||||
assertAcked(client().admin().indices().aliases(request).actionGet());
|
||||
final GetAliasesResponse response = client().admin().indices().getAliases(new GetAliasesRequest("alias")).actionGet();
|
||||
assertThat(response.getAliases().keys().size(), equalTo(1));
|
||||
assertThat(response.getAliases().keys().iterator().next().value, equalTo("index"));
|
||||
final List<AliasMetaData> aliasMetaData = response.getAliases().get("index");
|
||||
assertThat(aliasMetaData, hasSize(1));
|
||||
assertThat(aliasMetaData.get(0).alias(), equalTo("alias"));
|
||||
}
|
||||
|
||||
public void testNotAllowed() {
|
||||
final Settings settings = Settings.builder()
|
||||
.putList(IndicesAliasesPlugin.ALLOWED_ORIGINS_SETTING.getKey(), Collections.singletonList("allowed"))
|
||||
.build();
|
||||
createIndex("index", settings);
|
||||
final String origin = randomFrom("", "not-allowed");
|
||||
final IndicesAliasesRequest request = new IndicesAliasesRequest().origin(origin);
|
||||
request.addAliasAction(IndicesAliasesRequest.AliasActions.add().index("index").alias("alias"));
|
||||
final Exception e = expectThrows(IllegalStateException.class, () -> client().admin().indices().aliases(request).actionGet());
|
||||
assertThat(e, hasToString(containsString("origin [" + origin + "] not allowed for index [index]")));
|
||||
}
|
||||
|
||||
public void testSomeAllowed() {
|
||||
final Settings fooIndexSettings = Settings.builder()
|
||||
.putList(IndicesAliasesPlugin.ALLOWED_ORIGINS_SETTING.getKey(), Collections.singletonList("foo_allowed"))
|
||||
.build();
|
||||
createIndex("foo", fooIndexSettings);
|
||||
final Settings barIndexSettings = Settings.builder()
|
||||
.putList(IndicesAliasesPlugin.ALLOWED_ORIGINS_SETTING.getKey(), Collections.singletonList("bar_allowed"))
|
||||
.build();
|
||||
createIndex("bar", barIndexSettings);
|
||||
final String origin = randomFrom("foo_allowed", "bar_allowed");
|
||||
final IndicesAliasesRequest request = new IndicesAliasesRequest().origin(origin);
|
||||
request.addAliasAction(IndicesAliasesRequest.AliasActions.add().index("foo").alias("alias"));
|
||||
request.addAliasAction(IndicesAliasesRequest.AliasActions.add().index("bar").alias("alias"));
|
||||
final Exception e = expectThrows(IllegalStateException.class, () -> client().admin().indices().aliases(request).actionGet());
|
||||
final String index = "foo_allowed".equals(origin) ? "bar" : "foo";
|
||||
assertThat(e, hasToString(containsString("origin [" + origin + "] not allowed for index [" + index + "]")));
|
||||
final AliasesExistResponse response = client().admin().indices().aliasesExist(new GetAliasesRequest("alias")).actionGet();
|
||||
assertFalse(response.exists());
|
||||
}
|
||||
|
||||
}
|
|
@ -10,6 +10,7 @@ import org.apache.lucene.util.SetOnce;
|
|||
import org.elasticsearch.action.ActionRequest;
|
||||
import org.elasticsearch.action.ActionResponse;
|
||||
import org.elasticsearch.action.RequestValidators;
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
||||
|
@ -355,4 +356,8 @@ public class Ccr extends Plugin implements ActionPlugin, PersistentTaskPlugin, E
|
|||
return Collections.singletonList(CcrRequests.CCR_PUT_MAPPING_REQUEST_VALIDATOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<RequestValidators.RequestValidator<IndicesAliasesRequest>> indicesAliasesRequestValidators() {
|
||||
return Collections.singletonList(CcrRequests.CCR_INDICES_ALIASES_REQUEST_VALIDATOR);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.elasticsearch.ElasticsearchStatusException;
|
|||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.RequestValidators;
|
||||
import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest;
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||
|
@ -105,4 +106,23 @@ public final class CcrRequests {
|
|||
return Optional.empty();
|
||||
};
|
||||
|
||||
public static final RequestValidators.RequestValidator<IndicesAliasesRequest> CCR_INDICES_ALIASES_REQUEST_VALIDATOR =
|
||||
(request, state, indices) -> {
|
||||
if (request.origin() == null) {
|
||||
return Optional.empty(); // an indices aliases request on old versions does not have origin
|
||||
}
|
||||
final List<Index> followingIndices = Arrays.stream(indices)
|
||||
.filter(index -> {
|
||||
final IndexMetaData indexMetaData = state.metaData().index(index);
|
||||
return indexMetaData != null && CcrSettings.CCR_FOLLOWING_INDEX_SETTING.get(indexMetaData.getSettings());
|
||||
}).collect(Collectors.toList());
|
||||
if (followingIndices.isEmpty() == false && "ccr".equals(request.origin()) == false) {
|
||||
final String errorMessage = "can't modify aliases on indices "
|
||||
+ "[" + followingIndices.stream().map(Index::getName).collect(Collectors.joining(", ")) + "]; "
|
||||
+ "aliases of following indices are self-replicated from their leader indices";
|
||||
return Optional.of(new ElasticsearchStatusException(errorMessage, RestStatus.FORBIDDEN));
|
||||
}
|
||||
return Optional.empty();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -305,10 +305,11 @@ public class ShardFollowTasksExecutor extends PersistentTasksExecutor<ShardFollo
|
|||
aliasActions.add(IndicesAliasesRequest.AliasActions.remove().index(followerIndex.getName()).alias(aliasName));
|
||||
}
|
||||
|
||||
final IndicesAliasesRequest request = new IndicesAliasesRequest();
|
||||
if (aliasActions.isEmpty()) {
|
||||
handler.accept(leaderIndexMetaData.getAliasesVersion());
|
||||
} else {
|
||||
final IndicesAliasesRequest request = new IndicesAliasesRequest();
|
||||
request.origin("ccr");
|
||||
aliasActions.forEach(request::addAliasAction);
|
||||
followerClient.admin().indices().aliases(
|
||||
request,
|
||||
|
|
|
@ -17,6 +17,9 @@ import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksRequest;
|
|||
import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksResponse;
|
||||
import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest;
|
||||
import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse;
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
|
||||
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesResponse;
|
||||
import org.elasticsearch.action.admin.indices.close.CloseIndexRequest;
|
||||
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
|
||||
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsRequest;
|
||||
|
@ -43,6 +46,7 @@ import org.elasticsearch.client.Requests;
|
|||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.health.ClusterIndexHealth;
|
||||
import org.elasticsearch.cluster.health.ClusterShardHealth;
|
||||
import org.elasticsearch.cluster.metadata.AliasMetaData;
|
||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||
import org.elasticsearch.cluster.metadata.MappingMetaData;
|
||||
import org.elasticsearch.cluster.routing.IndexShardRoutingTable;
|
||||
|
@ -103,11 +107,13 @@ import static java.util.Collections.singletonMap;
|
|||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
||||
import static org.elasticsearch.xpack.ccr.CcrRetentionLeases.retentionLeaseId;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.hasToString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
|
@ -412,6 +418,43 @@ public class IndexFollowingIT extends CcrIntegTestCase {
|
|||
assertAcked(followerClient().admin().indices().putMapping(putMappingRequest).actionGet());
|
||||
}
|
||||
|
||||
public void testDoNotAllowAddAliasToFollower() throws Exception {
|
||||
final String leaderIndexSettings =
|
||||
getIndexSettings(between(1, 2), between(0, 1), singletonMap(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), "true"));
|
||||
assertAcked(leaderClient().admin().indices().prepareCreate("leader").setSource(leaderIndexSettings, XContentType.JSON));
|
||||
followerClient().execute(PutFollowAction.INSTANCE, putFollow("leader", "follower")).get();
|
||||
final IndicesAliasesRequest request = new IndicesAliasesRequest()
|
||||
.addAliasAction(IndicesAliasesRequest.AliasActions.add().index("follower").alias("follower_alias"));
|
||||
final ElasticsearchStatusException e =
|
||||
expectThrows(ElasticsearchStatusException.class, () -> followerClient().admin().indices().aliases(request).actionGet());
|
||||
assertThat(
|
||||
e,
|
||||
hasToString(containsString("can't modify aliases on indices [follower]; "
|
||||
+ "aliases of following indices are self-replicated from their leader indices")));
|
||||
assertThat(e.status(), equalTo(RestStatus.FORBIDDEN));
|
||||
}
|
||||
|
||||
public void testAddAliasAfterUnfollow() throws Exception {
|
||||
final String leaderIndexSettings =
|
||||
getIndexSettings(between(1, 2), between(0, 1), singletonMap(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), "true"));
|
||||
assertAcked(leaderClient().admin().indices().prepareCreate("leader").setSource(leaderIndexSettings, XContentType.JSON));
|
||||
followerClient().execute(PutFollowAction.INSTANCE, putFollow("leader", "follower")).get();
|
||||
pauseFollow("follower");
|
||||
followerClient().admin().indices().close(new CloseIndexRequest("follower")).actionGet();
|
||||
assertAcked(followerClient().execute(UnfollowAction.INSTANCE, new UnfollowAction.Request("follower")).actionGet());
|
||||
followerClient().admin().indices().open(new OpenIndexRequest("follower")).actionGet();
|
||||
final IndicesAliasesRequest request = new IndicesAliasesRequest()
|
||||
.addAliasAction(IndicesAliasesRequest.AliasActions.add().index("follower").alias("follower_alias"));
|
||||
assertAcked(followerClient().admin().indices().aliases(request).actionGet());
|
||||
final GetAliasesResponse response =
|
||||
followerClient().admin().indices().getAliases(new GetAliasesRequest("follower_alias")).actionGet();
|
||||
assertThat(response.getAliases().keys().size(), equalTo(1));
|
||||
assertThat(response.getAliases().keys().iterator().next().value, equalTo("follower"));
|
||||
final List<AliasMetaData> aliasMetaData = response.getAliases().get("follower");
|
||||
assertThat(aliasMetaData, hasSize(1));
|
||||
assertThat(aliasMetaData.get(0).alias(), equalTo("follower_alias"));
|
||||
}
|
||||
|
||||
public void testFollowIndex_backlog() throws Exception {
|
||||
int numberOfShards = between(1, 5);
|
||||
String leaderIndexSettings = getIndexSettings(numberOfShards, between(0, 1),
|
||||
|
|
|
@ -8,6 +8,7 @@ package org.elasticsearch.xpack.core;
|
|||
import org.elasticsearch.action.ActionRequest;
|
||||
import org.elasticsearch.action.ActionResponse;
|
||||
import org.elasticsearch.action.RequestValidators;
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
|
||||
import org.elasticsearch.action.support.ActionFilter;
|
||||
import org.elasticsearch.bootstrap.BootstrapCheck;
|
||||
|
@ -439,8 +440,17 @@ public class LocalStateCompositeXPackPlugin extends XPackPlugin implements Scrip
|
|||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<RequestValidators.RequestValidator<IndicesAliasesRequest>> indicesAliasesRequestValidators() {
|
||||
return filterPlugins(ActionPlugin.class)
|
||||
.stream()
|
||||
.flatMap(p -> p.indicesAliasesRequestValidators().stream())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private <T> List<T> filterPlugins(Class<T> type) {
|
||||
return plugins.stream().filter(x -> type.isAssignableFrom(x.getClass())).map(p -> ((T)p))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue