Refactor put mapping request validation for reuse (#43005)

This commit refactors put mapping request validation for reuse. The
concrete case that we are after here is the ability to apply effectively
the same framework to indices aliases requests. This commit refactors
the put mapping request validation framework to allow for that.
This commit is contained in:
Jason Tedor 2019-06-09 10:06:14 -04:00
parent 0a982fc57f
commit 915d2f2daa
No known key found for this signature in database
GPG Key ID: FA89F05560F16BC5
13 changed files with 315 additions and 201 deletions

View File

@ -118,6 +118,7 @@ import org.elasticsearch.action.admin.indices.mapping.get.TransportGetFieldMappi
import org.elasticsearch.action.admin.indices.mapping.get.TransportGetFieldMappingsIndexAction; import org.elasticsearch.action.admin.indices.mapping.get.TransportGetFieldMappingsIndexAction;
import org.elasticsearch.action.admin.indices.mapping.get.TransportGetMappingsAction; import org.elasticsearch.action.admin.indices.mapping.get.TransportGetMappingsAction;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingAction; import org.elasticsearch.action.admin.indices.mapping.put.PutMappingAction;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
import org.elasticsearch.action.admin.indices.mapping.put.TransportPutMappingAction; import org.elasticsearch.action.admin.indices.mapping.put.TransportPutMappingAction;
import org.elasticsearch.action.admin.indices.open.OpenIndexAction; import org.elasticsearch.action.admin.indices.open.OpenIndexAction;
import org.elasticsearch.action.admin.indices.open.TransportOpenIndexAction; import org.elasticsearch.action.admin.indices.open.TransportOpenIndexAction;
@ -204,6 +205,7 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.node.DiscoveryNodes;
import org.elasticsearch.common.NamedRegistry; import org.elasticsearch.common.NamedRegistry;
import org.elasticsearch.common.inject.AbstractModule; import org.elasticsearch.common.inject.AbstractModule;
import org.elasticsearch.common.inject.TypeLiteral;
import org.elasticsearch.common.inject.multibindings.MapBinder; import org.elasticsearch.common.inject.multibindings.MapBinder;
import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.ClusterSettings;
import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.IndexScopedSettings;
@ -359,7 +361,7 @@ public class ActionModule extends AbstractModule {
private final AutoCreateIndex autoCreateIndex; private final AutoCreateIndex autoCreateIndex;
private final DestructiveOperations destructiveOperations; private final DestructiveOperations destructiveOperations;
private final RestController restController; private final RestController restController;
private final TransportPutMappingAction.RequestValidators mappingRequestValidators; private final RequestValidators<PutMappingRequest> mappingRequestValidators;
public ActionModule(boolean transportClient, Settings settings, IndexNameExpressionResolver indexNameExpressionResolver, public ActionModule(boolean transportClient, Settings settings, IndexNameExpressionResolver indexNameExpressionResolver,
IndexScopedSettings indexScopedSettings, ClusterSettings clusterSettings, SettingsFilter settingsFilter, IndexScopedSettings indexScopedSettings, ClusterSettings clusterSettings, SettingsFilter settingsFilter,
@ -391,9 +393,8 @@ public class ActionModule extends AbstractModule {
restWrapper = newRestWrapper; restWrapper = newRestWrapper;
} }
} }
mappingRequestValidators = new TransportPutMappingAction.RequestValidators( mappingRequestValidators = new RequestValidators<>(
actionPlugins.stream().flatMap(p -> p.mappingRequestValidators().stream()).collect(Collectors.toList()) actionPlugins.stream().flatMap(p -> p.mappingRequestValidators().stream()).collect(Collectors.toList()));
);
if (transportClient) { if (transportClient) {
restController = null; restController = null;
@ -690,7 +691,7 @@ public class ActionModule extends AbstractModule {
protected void configure() { protected void configure() {
bind(ActionFilters.class).toInstance(actionFilters); bind(ActionFilters.class).toInstance(actionFilters);
bind(DestructiveOperations.class).toInstance(destructiveOperations); bind(DestructiveOperations.class).toInstance(destructiveOperations);
bind(TransportPutMappingAction.RequestValidators.class).toInstance(mappingRequestValidators); bind(new TypeLiteral<RequestValidators<PutMappingRequest>>() {}).toInstance(mappingRequestValidators);
if (false == transportClient) { if (false == transportClient) {
// Supporting classes only used when not a transport client // Supporting classes only used when not a transport client

View File

@ -0,0 +1,67 @@
/*
* 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;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.index.Index;
import java.util.Collection;
import java.util.Optional;
public class RequestValidators<T extends ActionRequest> {
private final Collection<RequestValidator<T>> validators;
public RequestValidators(Collection<RequestValidator<T>> validators) {
this.validators = validators;
}
public Optional<Exception> validateRequest(final T request, final ClusterState state, final Index[] indices) {
Exception exception = null;
for (final RequestValidator<T> validator : validators) {
final Optional<Exception> maybeException = validator.validateRequest(request, state, indices);
if (maybeException.isPresent() == false) continue;
if (exception == null) {
exception = maybeException.get();
} else {
exception.addSuppressed(maybeException.get());
}
}
return Optional.ofNullable(exception);
}
/**
* A validator that validates an request associated with indices before executing it.
*/
public interface RequestValidator<T extends ActionRequest> {
/**
* Validates a given request with its associated concrete indices and the current state.
*
* @param request the request to validate
* @param state the current cluster state
* @param indices the concrete indices that associated with the given request
* @return an optional exception indicates a reason that the given request should be aborted, otherwise empty
*/
Optional<Exception> validateRequest(T request, ClusterState state, Index[] indices);
}
}

View File

@ -1,40 +0,0 @@
/*
* 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.mapping.put;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.index.Index;
/**
* A validator that validates a {@link PutMappingRequest} before executing it.
* @see TransportPutMappingAction.RequestValidators
*/
public interface MappingRequestValidator {
/**
* Validates a given put mapping request with its associated concrete indices and the current state.
*
* @param request the request to validate
* @param state the current cluster state
* @param indices the concrete indices that associated with the given put mapping request
* @return a non-null exception indicates a reason that the given request should be aborted; otherwise returns null.
*/
Exception validateRequest(PutMappingRequest request, ClusterState state, Index[] indices);
}

View File

@ -21,6 +21,7 @@ package org.elasticsearch.action.admin.indices.mapping.put;
import org.apache.logging.log4j.message.ParameterizedMessage; import org.apache.logging.log4j.message.ParameterizedMessage;
import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.RequestValidators;
import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.master.AcknowledgedResponse; import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.support.master.TransportMasterNodeAction; import org.elasticsearch.action.support.master.TransportMasterNodeAction;
@ -37,7 +38,8 @@ import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService; import org.elasticsearch.transport.TransportService;
import java.util.Collection; import java.util.Objects;
import java.util.Optional;
/** /**
* Put mapping action. * Put mapping action.
@ -45,17 +47,21 @@ import java.util.Collection;
public class TransportPutMappingAction extends TransportMasterNodeAction<PutMappingRequest, AcknowledgedResponse> { public class TransportPutMappingAction extends TransportMasterNodeAction<PutMappingRequest, AcknowledgedResponse> {
private final MetaDataMappingService metaDataMappingService; private final MetaDataMappingService metaDataMappingService;
private final RequestValidators requestValidators; private final RequestValidators<PutMappingRequest> requestValidators;
@Inject @Inject
public TransportPutMappingAction(TransportService transportService, ClusterService clusterService, public TransportPutMappingAction(
ThreadPool threadPool, MetaDataMappingService metaDataMappingService, final TransportService transportService,
ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver, final ClusterService clusterService,
RequestValidators requestValidators) { final ThreadPool threadPool,
final MetaDataMappingService metaDataMappingService,
final ActionFilters actionFilters,
final IndexNameExpressionResolver indexNameExpressionResolver,
final RequestValidators<PutMappingRequest> requestValidators) {
super(PutMappingAction.NAME, transportService, clusterService, threadPool, actionFilters, indexNameExpressionResolver, super(PutMappingAction.NAME, transportService, clusterService, threadPool, actionFilters, indexNameExpressionResolver,
PutMappingRequest::new); PutMappingRequest::new);
this.metaDataMappingService = metaDataMappingService; this.metaDataMappingService = metaDataMappingService;
this.requestValidators = requestValidators; this.requestValidators = Objects.requireNonNull(requestValidators);
} }
@Override @Override
@ -87,9 +93,9 @@ public class TransportPutMappingAction extends TransportMasterNodeAction<PutMapp
final Index[] concreteIndices = request.getConcreteIndex() == null ? final Index[] concreteIndices = request.getConcreteIndex() == null ?
indexNameExpressionResolver.concreteIndices(state, request) indexNameExpressionResolver.concreteIndices(state, request)
: new Index[] {request.getConcreteIndex()}; : new Index[] {request.getConcreteIndex()};
final Exception validationException = requestValidators.validateRequest(request, state, concreteIndices); final Optional<Exception> maybeValidationException = requestValidators.validateRequest(request, state, concreteIndices);
if (validationException != null) { if (maybeValidationException.isPresent()) {
listener.onFailure(validationException); listener.onFailure(maybeValidationException.get());
return; return;
} }
PutMappingClusterStateUpdateRequest updateRequest = new PutMappingClusterStateUpdateRequest() PutMappingClusterStateUpdateRequest updateRequest = new PutMappingClusterStateUpdateRequest()
@ -118,26 +124,4 @@ public class TransportPutMappingAction extends TransportMasterNodeAction<PutMapp
} }
} }
public static class RequestValidators {
private final Collection<MappingRequestValidator> validators;
public RequestValidators(Collection<MappingRequestValidator> validators) {
this.validators = validators;
}
Exception validateRequest(PutMappingRequest request, ClusterState state, Index[] indices) {
Exception firstException = null;
for (MappingRequestValidator validator : validators) {
final Exception e = validator.validateRequest(request, state, indices);
if (e == null) continue;
if (firstException == null) {
firstException = e;
} else {
firstException.addSuppressed(e);
}
}
return firstException;
}
}
} }

View File

@ -22,8 +22,8 @@ package org.elasticsearch.plugins;
import org.elasticsearch.action.Action; import org.elasticsearch.action.Action;
import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionResponse; import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.action.admin.indices.mapping.put.MappingRequestValidator; import org.elasticsearch.action.RequestValidators;
import org.elasticsearch.action.admin.indices.mapping.put.TransportPutMappingAction; import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
import org.elasticsearch.action.support.ActionFilter; import org.elasticsearch.action.support.ActionFilter;
import org.elasticsearch.action.support.TransportAction; import org.elasticsearch.action.support.TransportAction;
import org.elasticsearch.action.support.TransportActions; import org.elasticsearch.action.support.TransportActions;
@ -183,10 +183,11 @@ public interface ActionPlugin {
} }
/** /**
* Returns a collection of validators that are used by {@link TransportPutMappingAction.RequestValidators} to * Returns a collection of validators that are used by {@link RequestValidators} to validate a
* validate a {@link org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest} before the executing it. * {@link org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest} before the executing it.
*/ */
default Collection<MappingRequestValidator> mappingRequestValidators() { default Collection<RequestValidators.RequestValidator<PutMappingRequest>> mappingRequestValidators() {
return Collections.emptyList(); return Collections.emptyList();
} }
} }

View File

@ -0,0 +1,97 @@
/*
* 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;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
import org.elasticsearch.common.Randomness;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.hamcrest.OptionalMatchers;
import org.hamcrest.Matchers;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
public class RequestValidatorsTests extends ESTestCase {
private final RequestValidators.RequestValidator<PutMappingRequest> EMPTY = (request, state, indices) -> Optional.empty();
private final RequestValidators.RequestValidator<PutMappingRequest> FAIL =
(request, state, indices) -> Optional.of(new Exception("failure"));
public void testValidates() {
final int numberOfValidations = randomIntBetween(0, 8);
final List<RequestValidators.RequestValidator<PutMappingRequest>> validators = new ArrayList<>(numberOfValidations);
for (int i = 0; i < numberOfValidations; i++) {
validators.add(EMPTY);
}
final RequestValidators<PutMappingRequest> requestValidators = new RequestValidators<>(validators);
assertThat(requestValidators.validateRequest(null, null, null), OptionalMatchers.isEmpty());
}
public void testFailure() {
final RequestValidators<PutMappingRequest> validators = new RequestValidators<>(Collections.singletonList(FAIL));
assertThat(validators.validateRequest(null, null, null), OptionalMatchers.isPresent());
}
public void testValidatesAfterFailure() {
final RequestValidators<PutMappingRequest> validators =
new RequestValidators<>(Collections.unmodifiableList(Arrays.asList(FAIL, EMPTY)));
assertThat(validators.validateRequest(null, null, null), OptionalMatchers.isPresent());
}
public void testMultipleFailures() {
final int numberOfFailures = randomIntBetween(2, 8);
final List<RequestValidators.RequestValidator<PutMappingRequest>> validators = new ArrayList<>(numberOfFailures);
for (int i = 0; i < numberOfFailures; i++) {
validators.add(FAIL);
}
final RequestValidators<PutMappingRequest> requestValidators = new RequestValidators<>(validators);
final Optional<Exception> e = requestValidators.validateRequest(null, null, null);
assertThat(e, OptionalMatchers.isPresent());
// noinspection OptionalGetWithoutIsPresent
assertThat(e.get().getSuppressed(), Matchers.arrayWithSize(numberOfFailures - 1));
}
public void testRandom() {
final int numberOfValidations = randomIntBetween(0, 8);
final int numberOfFailures = randomIntBetween(0, 8);
final List<RequestValidators.RequestValidator<PutMappingRequest>> validators =
new ArrayList<>(numberOfValidations + numberOfFailures);
for (int i = 0; i < numberOfValidations; i++) {
validators.add(EMPTY);
}
for (int i = 0; i < numberOfFailures; i++) {
validators.add(FAIL);
}
Randomness.shuffle(validators);
final RequestValidators<PutMappingRequest> requestValidators = new RequestValidators<>(validators);
final Optional<Exception> e = requestValidators.validateRequest(null, null, null);
if (numberOfFailures == 0) {
assertThat(e, OptionalMatchers.isEmpty());
} else {
assertThat(e, OptionalMatchers.isPresent());
// noinspection OptionalGetWithoutIsPresent
assertThat(e.get().getSuppressed(), Matchers.arrayWithSize(numberOfFailures - 1));
}
}
}

View File

@ -1,91 +0,0 @@
/*
* 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.mapping.put;
import org.elasticsearch.common.Randomness;
import org.elasticsearch.test.ESTestCase;
import org.hamcrest.Matchers;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class TransportPutMappingRequestValidatorsTests extends ESTestCase {
private final MappingRequestValidator EMPTY = (request, state, indices) -> null;
private final MappingRequestValidator FAIL = (request, state, indices) -> new Exception("failure");
public void testValidates() {
final int numberOfValidations = randomIntBetween(0, 8);
final List<MappingRequestValidator> validators = new ArrayList<>(numberOfValidations);
for (int i = 0; i < numberOfValidations; i++) {
validators.add(EMPTY);
}
final TransportPutMappingAction.RequestValidators requestValidators = new TransportPutMappingAction.RequestValidators(validators);
assertNull(requestValidators.validateRequest(null, null, null));
}
public void testFailure() {
final TransportPutMappingAction.RequestValidators validators =
new TransportPutMappingAction.RequestValidators(Collections.singletonList(FAIL));
assertNotNull(validators.validateRequest(null, null, null));
}
public void testValidatesAfterFailure() {
final TransportPutMappingAction.RequestValidators validators =
new TransportPutMappingAction.RequestValidators(Collections.unmodifiableList(Arrays.asList(FAIL, EMPTY)));
assertNotNull(validators.validateRequest(null, null, null));
}
public void testMultipleFailures() {
final int numberOfFailures = randomIntBetween(2, 8);
final List<MappingRequestValidator> validators = new ArrayList<>(numberOfFailures);
for (int i = 0; i < numberOfFailures; i++) {
validators.add(FAIL);
}
final TransportPutMappingAction.RequestValidators requestValidators = new TransportPutMappingAction.RequestValidators(validators);
final Exception e = requestValidators.validateRequest(null, null, null);
assertNotNull(e);
assertThat(e.getSuppressed(), Matchers.arrayWithSize(numberOfFailures - 1));
}
public void testRandom() {
final int numberOfValidations = randomIntBetween(0, 8);
final int numberOfFailures = randomIntBetween(0, 8);
final List<MappingRequestValidator> validators = new ArrayList<>(numberOfValidations + numberOfFailures);
for (int i = 0; i < numberOfValidations; i++) {
validators.add(EMPTY);
}
for (int i = 0; i < numberOfFailures; i++) {
validators.add(FAIL);
}
Randomness.shuffle(validators);
final TransportPutMappingAction.RequestValidators requestValidators = new TransportPutMappingAction.RequestValidators(validators);
final Exception e = requestValidators.validateRequest(null, null, null);
if (numberOfFailures == 0) {
assertNull(e);
} else {
assertNotNull(e);
assertThat(e.getSuppressed(), Matchers.arrayWithSize(numberOfFailures - 1));
}
}
}

View File

@ -19,6 +19,7 @@
package org.elasticsearch.action.admin.indices.mapping.put; package org.elasticsearch.action.admin.indices.mapping.put;
import org.elasticsearch.action.RequestValidators;
import org.elasticsearch.common.util.concurrent.ConcurrentCollections; import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
import org.elasticsearch.index.Index; import org.elasticsearch.index.Index;
import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.ActionPlugin;
@ -29,6 +30,7 @@ import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
@ -38,14 +40,15 @@ public class ValidateMappingRequestPluginIT extends ESSingleNodeTestCase {
static final Map<String, Collection<String>> allowedOrigins = ConcurrentCollections.newConcurrentMap(); static final Map<String, Collection<String>> allowedOrigins = ConcurrentCollections.newConcurrentMap();
public static class TestPlugin extends Plugin implements ActionPlugin { public static class TestPlugin extends Plugin implements ActionPlugin {
@Override @Override
public Collection<MappingRequestValidator> mappingRequestValidators() { public Collection<RequestValidators.RequestValidator<PutMappingRequest>> mappingRequestValidators() {
return Collections.singletonList((request, state, indices) -> { return Collections.singletonList((request, state, indices) -> {
for (Index index : indices) { for (Index index : indices) {
if (allowedOrigins.getOrDefault(index.getName(), Collections.emptySet()).contains(request.origin()) == false) { if (allowedOrigins.getOrDefault(index.getName(), Collections.emptySet()).contains(request.origin()) == false) {
return new IllegalStateException("not allowed: index[" + index.getName() + "] origin[" + request.origin() + "]"); return Optional.of(
new IllegalStateException("not allowed: index[" + index.getName() + "] origin[" + request.origin() + "]"));
} }
} }
return null; return Optional.empty();
}); });
} }
} }

View File

@ -24,6 +24,7 @@ import org.apache.logging.log4j.Logger;
import org.elasticsearch.Version; import org.elasticsearch.Version;
import org.elasticsearch.action.Action; import org.elasticsearch.action.Action;
import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.RequestValidators;
import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryAction; import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryAction;
import org.elasticsearch.action.admin.cluster.repositories.put.TransportPutRepositoryAction; import org.elasticsearch.action.admin.cluster.repositories.put.TransportPutRepositoryAction;
import org.elasticsearch.action.admin.cluster.reroute.ClusterRerouteAction; import org.elasticsearch.action.admin.cluster.reroute.ClusterRerouteAction;
@ -1154,7 +1155,7 @@ public class SnapshotResiliencyTests extends ESTestCase {
); );
actions.put(PutMappingAction.INSTANCE, actions.put(PutMappingAction.INSTANCE,
new TransportPutMappingAction(transportService, clusterService, threadPool, metaDataMappingService, new TransportPutMappingAction(transportService, clusterService, threadPool, metaDataMappingService,
actionFilters, indexNameExpressionResolver, new TransportPutMappingAction.RequestValidators(Collections.emptyList()))); actionFilters, indexNameExpressionResolver, new RequestValidators<>(Collections.emptyList())));
final ResponseCollectorService responseCollectorService = new ResponseCollectorService(clusterService); final ResponseCollectorService responseCollectorService = new ResponseCollectorService(clusterService);
final SearchTransportService searchTransportService = new SearchTransportService(transportService, final SearchTransportService searchTransportService = new SearchTransportService(transportService,
SearchExecutionStatsCollector.makeWrapper(responseCollectorService)); SearchExecutionStatsCollector.makeWrapper(responseCollectorService));

View File

@ -0,0 +1,84 @@
/*
* 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.test.hamcrest;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
import java.util.Optional;
public class OptionalMatchers {
private static class IsEmptyMatcher extends TypeSafeMatcher<Optional<?>> {
@Override
protected boolean matchesSafely(final Optional<?> item) {
// noinspection OptionalAssignedToNull
return item != null && item.isPresent() == false;
}
@Override
public void describeTo(final Description description) {
description.appendText("expected empty optional");
}
@Override
protected void describeMismatchSafely(final Optional<?> item, final Description mismatchDescription) {
if (item == null) {
mismatchDescription.appendText("was null");
} else {
mismatchDescription.appendText("was ").appendText(item.toString());
}
}
}
public static IsEmptyMatcher isEmpty() {
return new IsEmptyMatcher();
}
private static class IsPresentMatcher extends TypeSafeMatcher<Optional<?>> {
@Override
protected boolean matchesSafely(final Optional<?> item) {
return item != null && item.isPresent();
}
@Override
public void describeTo(final Description description) {
description.appendText("expected non-empty optional");
}
@Override
protected void describeMismatchSafely(final Optional<?> item, final Description mismatchDescription) {
if (item == null) {
mismatchDescription.appendText("was null");
} else {
mismatchDescription.appendText("was empty");
}
}
}
public static IsPresentMatcher isPresent() {
return new IsPresentMatcher();
}
}

View File

@ -9,7 +9,8 @@ package org.elasticsearch.xpack.ccr;
import org.apache.lucene.util.SetOnce; import org.apache.lucene.util.SetOnce;
import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionResponse; import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.action.admin.indices.mapping.put.MappingRequestValidator; import org.elasticsearch.action.RequestValidators;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
import org.elasticsearch.client.Client; import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.metadata.MetaData;
@ -350,7 +351,7 @@ public class Ccr extends Plugin implements ActionPlugin, PersistentTaskPlugin, E
protected XPackLicenseState getLicenseState() { return XPackPlugin.getSharedLicenseState(); } protected XPackLicenseState getLicenseState() { return XPackPlugin.getSharedLicenseState(); }
@Override @Override
public Collection<MappingRequestValidator> mappingRequestValidators() { public Collection<RequestValidators.RequestValidator<PutMappingRequest>> mappingRequestValidators() {
return Collections.singletonList(CcrRequests.CCR_PUT_MAPPING_REQUEST_VALIDATOR); return Collections.singletonList(CcrRequests.CCR_PUT_MAPPING_REQUEST_VALIDATOR);
} }

View File

@ -7,9 +7,9 @@ package org.elasticsearch.xpack.ccr.action;
import org.elasticsearch.ElasticsearchStatusException; import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.RequestValidators;
import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest; import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest; import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
import org.elasticsearch.action.admin.indices.mapping.put.MappingRequestValidator;
import org.elasticsearch.client.Client; import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.MappingMetaData; import org.elasticsearch.cluster.metadata.MappingMetaData;
@ -22,6 +22,7 @@ import org.elasticsearch.xpack.ccr.CcrSettings;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -85,21 +86,23 @@ public final class CcrRequests {
)); ));
} }
public static final MappingRequestValidator CCR_PUT_MAPPING_REQUEST_VALIDATOR = (request, state, indices) -> { public static final RequestValidators.RequestValidator<PutMappingRequest> CCR_PUT_MAPPING_REQUEST_VALIDATOR =
if (request.origin() == null) { (request, state, indices) -> {
return null; // a put-mapping-request on old versions does not have origin. if (request.origin() == null) {
} return Optional.empty(); // a put-mapping-request on old versions does not have origin.
final List<Index> followingIndices = Arrays.stream(indices) }
.filter(index -> { final List<Index> followingIndices = Arrays.stream(indices)
final IndexMetaData indexMetaData = state.metaData().index(index); .filter(index -> {
return indexMetaData != null && CcrSettings.CCR_FOLLOWING_INDEX_SETTING.get(indexMetaData.getSettings()); final IndexMetaData indexMetaData = state.metaData().index(index);
}).collect(Collectors.toList()); return indexMetaData != null && CcrSettings.CCR_FOLLOWING_INDEX_SETTING.get(indexMetaData.getSettings());
if (followingIndices.isEmpty() == false && "ccr".equals(request.origin()) == false) { }).collect(Collectors.toList());
final String errorMessage = "can't put mapping to the following indices " if (followingIndices.isEmpty() == false && "ccr".equals(request.origin()) == false) {
+ "[" + followingIndices.stream().map(Index::getName).collect(Collectors.joining(", ")) + "]; " final String errorMessage = "can't put mapping to the following indices "
+ "the mapping of the following indices are self-replicated from its leader indices"; + "[" + followingIndices.stream().map(Index::getName).collect(Collectors.joining(", ")) + "]; "
return new ElasticsearchStatusException(errorMessage, RestStatus.FORBIDDEN); + "the mapping of the following indices are self-replicated from its leader indices";
} return Optional.of(new ElasticsearchStatusException(errorMessage, RestStatus.FORBIDDEN));
return null; }
}; return Optional.empty();
};
} }

View File

@ -7,7 +7,8 @@ package org.elasticsearch.xpack.core;
import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionResponse; import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.action.admin.indices.mapping.put.MappingRequestValidator; import org.elasticsearch.action.RequestValidators;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
import org.elasticsearch.action.support.ActionFilter; import org.elasticsearch.action.support.ActionFilter;
import org.elasticsearch.bootstrap.BootstrapCheck; import org.elasticsearch.bootstrap.BootstrapCheck;
import org.elasticsearch.client.Client; import org.elasticsearch.client.Client;
@ -431,9 +432,11 @@ public class LocalStateCompositeXPackPlugin extends XPackPlugin implements Scrip
} }
@Override @Override
public Collection<MappingRequestValidator> mappingRequestValidators() { public Collection<RequestValidators.RequestValidator<PutMappingRequest>> mappingRequestValidators() {
return filterPlugins(ActionPlugin.class).stream().flatMap(p -> p.mappingRequestValidators().stream()) return filterPlugins(ActionPlugin.class)
.collect(Collectors.toList()); .stream()
.flatMap(p -> p.mappingRequestValidators().stream())
.collect(Collectors.toList());
} }
private <T> List<T> filterPlugins(Class<T> type) { private <T> List<T> filterPlugins(Class<T> type) {