From 9ec96854f2db5a3d6bb617ec84770361a29fa2b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Fern=C3=A1ndez=20Casta=C3=B1o?= Date: Thu, 6 May 2021 19:26:29 +0200 Subject: [PATCH] Add support for index pattern exclusion in CCR AutoFollow --- .../ccr/GetAutoFollowPatternResponse.java | 23 ++- .../ccr/PutAutoFollowPatternRequest.java | 22 ++- .../java/org/elasticsearch/client/CCRIT.java | 21 ++- .../client/CcrRequestConvertersTests.java | 5 +- .../GetAutoFollowPatternResponseTests.java | 3 + .../ccr/PutAutoFollowPatternRequestTests.java | 6 +- .../documentation/CCRDocumentationIT.java | 7 +- .../ccr/put_auto_follow_pattern.asciidoc | 5 +- .../get-auto-follow-pattern.asciidoc | 1 + .../put-auto-follow-pattern.asciidoc | 9 ++ .../elasticsearch/xpack/ccr/AutoFollowIT.java | 58 ++++++++ .../rest-api-spec/test/ccr/auto_follow.yml | 6 + .../elasticsearch/xpack/ccr/AutoFollowIT.java | 40 ++++++ .../elasticsearch/xpack/ccr/CcrLicenseIT.java | 1 + ...nsportActivateAutoFollowPatternAction.java | 1 + .../TransportPutAutoFollowPatternAction.java | 25 +++- .../xpack/ccr/AutoFollowMetadataTests.java | 2 + .../ccr/CCRInfoTransportActionTests.java | 1 + .../action/AutoFollowCoordinatorTests.java | 131 +++++++++++++++++- .../GetAutoFollowPatternResponseTests.java | 1 + .../PutAutoFollowPatternRequestTests.java | 2 + ...tActivateAutoFollowPatternActionTests.java | 2 + ...ortDeleteAutoFollowPatternActionTests.java | 3 + ...nsportGetAutoFollowPatternActionTests.java | 2 + ...nsportPutAutoFollowPatternActionTests.java | 48 +++++++ .../xpack/core/ccr/AutoFollowMetadata.java | 64 +++++++-- .../action/PutAutoFollowPatternAction.java | 29 +++- 27 files changed, 483 insertions(+), 35 deletions(-) diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/GetAutoFollowPatternResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/GetAutoFollowPatternResponse.java index 2a9a9b4e9bc74..bb293ff19dba0 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/GetAutoFollowPatternResponse.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/GetAutoFollowPatternResponse.java @@ -82,11 +82,19 @@ public static class Pattern extends FollowConfig { @SuppressWarnings("unchecked") private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( - "pattern", true, args -> new Pattern((String) args[0], (List) args[1], (String) args[2])); + "pattern", + true, + args -> new Pattern((String) args[0], + (List) args[1], + args[2] == null ? Collections.emptyList() : (List) args[2], + (String) args[3]) + ); static { PARSER.declareString(ConstructingObjectParser.constructorArg(), PutFollowRequest.REMOTE_CLUSTER_FIELD); PARSER.declareStringArray(ConstructingObjectParser.constructorArg(), PutAutoFollowPatternRequest.LEADER_PATTERNS_FIELD); + PARSER.declareStringArray(ConstructingObjectParser.optionalConstructorArg(), + PutAutoFollowPatternRequest.LEADER_EXCLUSION_PATTERNS_FIELD); PARSER.declareString(ConstructingObjectParser.optionalConstructorArg(), PutAutoFollowPatternRequest.FOLLOW_PATTERN_FIELD); PARSER.declareObject(Pattern::setSettings, (p, c) -> Settings.fromXContent(p), PutAutoFollowPatternRequest.SETTINGS); PARSER.declareInt(Pattern::setMaxReadRequestOperationCount, FollowConfig.MAX_READ_REQUEST_OPERATION_COUNT); @@ -123,11 +131,16 @@ public static class Pattern extends FollowConfig { private final String remoteCluster; private final List leaderIndexPatterns; + private final List leaderIndexExclusionPatterns; private final String followIndexNamePattern; - Pattern(String remoteCluster, List leaderIndexPatterns, String followIndexNamePattern) { + Pattern(String remoteCluster, + List leaderIndexPatterns, + List leaderIndexExclusionPatterns, + String followIndexNamePattern) { this.remoteCluster = remoteCluster; this.leaderIndexPatterns = leaderIndexPatterns; + this.leaderIndexExclusionPatterns = leaderIndexExclusionPatterns; this.followIndexNamePattern = followIndexNamePattern; } @@ -139,6 +152,10 @@ public List getLeaderIndexPatterns() { return leaderIndexPatterns; } + public List getLeaderIndexExclusionPatterns() { + return leaderIndexExclusionPatterns; + } + public String getFollowIndexNamePattern() { return followIndexNamePattern; } @@ -151,6 +168,7 @@ public boolean equals(Object o) { Pattern pattern = (Pattern) o; return Objects.equals(remoteCluster, pattern.remoteCluster) && Objects.equals(leaderIndexPatterns, pattern.leaderIndexPatterns) && + Objects.equals(leaderIndexExclusionPatterns, pattern.leaderIndexExclusionPatterns) && Objects.equals(followIndexNamePattern, pattern.followIndexNamePattern); } @@ -160,6 +178,7 @@ public int hashCode() { super.hashCode(), remoteCluster, leaderIndexPatterns, + leaderIndexExclusionPatterns, followIndexNamePattern ); } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/PutAutoFollowPatternRequest.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/PutAutoFollowPatternRequest.java index ecad888566128..6a27990f7579c 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/PutAutoFollowPatternRequest.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/PutAutoFollowPatternRequest.java @@ -14,23 +14,36 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import java.io.IOException; +import java.util.Collections; import java.util.List; import java.util.Objects; public final class PutAutoFollowPatternRequest extends FollowConfig implements Validatable, ToXContentObject { static final ParseField LEADER_PATTERNS_FIELD = new ParseField("leader_index_patterns"); + static final ParseField LEADER_EXCLUSION_PATTERNS_FIELD = new ParseField("leader_index_exclusion_patterns"); static final ParseField FOLLOW_PATTERN_FIELD = new ParseField("follow_index_pattern"); private final String name; private final String remoteCluster; private final List leaderIndexPatterns; + private final List leaderIndexExclusionPatterns; private String followIndexNamePattern; - public PutAutoFollowPatternRequest(String name, String remoteCluster, List leaderIndexPatterns) { + public PutAutoFollowPatternRequest(String name, + String remoteCluster, + List leaderIndexPatterns) { + this(name, remoteCluster, leaderIndexPatterns, Collections.emptyList()); + } + + public PutAutoFollowPatternRequest(String name, + String remoteCluster, + List leaderIndexPatterns, + List leaderIndexExclusionPatterns) { this.name = Objects.requireNonNull(name); this.remoteCluster = Objects.requireNonNull(remoteCluster); this.leaderIndexPatterns = Objects.requireNonNull(leaderIndexPatterns); + this.leaderIndexExclusionPatterns = Objects.requireNonNull(leaderIndexExclusionPatterns); } public String getName() { @@ -45,6 +58,10 @@ public List getLeaderIndexPatterns() { return leaderIndexPatterns; } + public List getLeaderIndexExclusionPatterns() { + return leaderIndexExclusionPatterns; + } + public String getFollowIndexNamePattern() { return followIndexNamePattern; } @@ -58,6 +75,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.startObject(); builder.field(PutFollowRequest.REMOTE_CLUSTER_FIELD.getPreferredName(), remoteCluster); builder.field(LEADER_PATTERNS_FIELD.getPreferredName(), leaderIndexPatterns); + builder.field(LEADER_EXCLUSION_PATTERNS_FIELD.getPreferredName(), leaderIndexExclusionPatterns); if (followIndexNamePattern != null) { builder.field(FOLLOW_PATTERN_FIELD.getPreferredName(), followIndexNamePattern); } @@ -75,6 +93,7 @@ public boolean equals(Object o) { return Objects.equals(name, that.name) && Objects.equals(remoteCluster, that.remoteCluster) && Objects.equals(leaderIndexPatterns, that.leaderIndexPatterns) && + Objects.equals(leaderIndexExclusionPatterns, that.leaderIndexExclusionPatterns) && Objects.equals(followIndexNamePattern, that.followIndexNamePattern); } @@ -85,6 +104,7 @@ public int hashCode() { name, remoteCluster, leaderIndexPatterns, + leaderIndexExclusionPatterns, followIndexNamePattern ); } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/CCRIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/CCRIT.java index 593712802ff22..06b508758f0ef 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/CCRIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/CCRIT.java @@ -244,8 +244,10 @@ public void testForgetFollower() throws IOException { public void testAutoFollowing() throws Exception { CcrClient ccrClient = highLevelClient().ccr(); - PutAutoFollowPatternRequest putAutoFollowPatternRequest = - new PutAutoFollowPatternRequest("pattern1", "local_cluster", Collections.singletonList("logs-*")); + PutAutoFollowPatternRequest putAutoFollowPatternRequest = new PutAutoFollowPatternRequest("pattern1", + "local_cluster", + Collections.singletonList("logs-*"), + Collections.singletonList("logs-excluded")); putAutoFollowPatternRequest.setFollowIndexNamePattern("copy-{{leader_index}}"); final int followerNumberOfReplicas = randomIntBetween(0, 4); final Settings autoFollowerPatternSettings = @@ -270,6 +272,20 @@ public void testAutoFollowing() throws Exception { getIndexSettingsAsMap("copy-logs-20200101"), hasEntry("index.number_of_replicas", Integer.toString(followerNumberOfReplicas))); + CreateIndexRequest createExcludedIndexRequest = new CreateIndexRequest("logs-excluded"); + CreateIndexResponse createExcludedIndexResponse = + highLevelClient().indices().create(createExcludedIndexRequest, RequestOptions.DEFAULT); + assertThat(createExcludedIndexResponse.isAcknowledged(), is(true)); + + assertBusy(() -> { + CcrStatsRequest ccrStatsRequest = new CcrStatsRequest(); + CcrStatsResponse ccrStatsResponse = execute(ccrStatsRequest, ccrClient::getCcrStats, ccrClient::getCcrStatsAsync); + assertThat(ccrStatsResponse.getAutoFollowStats().getNumberOfSuccessfulFollowIndices(), equalTo(1L)); + assertThat(ccrStatsResponse.getIndicesFollowStats().getShardFollowStats("copy-logs-20200101"), notNullValue()); + }); + + assertThat(indexExists("copy-logs-excluded"), is(false)); + GetAutoFollowPatternRequest getAutoFollowPatternRequest = randomBoolean() ? new GetAutoFollowPatternRequest("pattern1") : new GetAutoFollowPatternRequest(); GetAutoFollowPatternResponse getAutoFollowPatternResponse = @@ -279,6 +295,7 @@ public void testAutoFollowing() throws Exception { assertThat(pattern, notNullValue()); assertThat(pattern.getRemoteCluster(), equalTo(putAutoFollowPatternRequest.getRemoteCluster())); assertThat(pattern.getLeaderIndexPatterns(), equalTo(putAutoFollowPatternRequest.getLeaderIndexPatterns())); + assertThat(pattern.getLeaderIndexExclusionPatterns(), equalTo(putAutoFollowPatternRequest.getLeaderIndexExclusionPatterns())); assertThat(pattern.getFollowIndexNamePattern(), equalTo(putAutoFollowPatternRequest.getFollowIndexNamePattern())); assertThat(pattern.getSettings(), equalTo(autoFollowerPatternSettings)); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/CcrRequestConvertersTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/CcrRequestConvertersTests.java index 970cd4b03b997..f0ea0b58c255e 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/CcrRequestConvertersTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/CcrRequestConvertersTests.java @@ -101,7 +101,10 @@ public void testForgetFollower() throws IOException { public void testPutAutofollowPattern() throws Exception { PutAutoFollowPatternRequest putAutoFollowPatternRequest = new PutAutoFollowPatternRequest(randomAlphaOfLength(4), - randomAlphaOfLength(4), Arrays.asList(generateRandomStringArray(4, 4, false))); + randomAlphaOfLength(4), + Arrays.asList(generateRandomStringArray(4, 4, false)), + Arrays.asList(generateRandomStringArray(4, 4, false)) + ); if (randomBoolean()) { putAutoFollowPatternRequest.setFollowIndexNamePattern(randomAlphaOfLength(4)); } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/GetAutoFollowPatternResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/GetAutoFollowPatternResponseTests.java index 23e52a8c32ed9..6113dc9a0cc3e 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/GetAutoFollowPatternResponseTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/GetAutoFollowPatternResponseTests.java @@ -39,6 +39,7 @@ protected GetAutoFollowPatternAction.Response createServerTestInstance(XContentT for (int i = 0; i < numPatterns; i++) { String remoteCluster = randomAlphaOfLength(4); List leaderIndexPatterns = Collections.singletonList(randomAlphaOfLength(4)); + List leaderIndexExclusionsPatterns = Collections.singletonList(randomAlphaOfLength(4)); String followIndexNamePattern = randomAlphaOfLength(4); final Settings settings = Settings.builder().put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), randomIntBetween(0, 4)).build(); @@ -89,6 +90,7 @@ protected GetAutoFollowPatternAction.Response createServerTestInstance(XContentT new AutoFollowMetadata.AutoFollowPattern( remoteCluster, leaderIndexPatterns, + leaderIndexExclusionsPatterns, followIndexNamePattern, settings, active, @@ -124,6 +126,7 @@ protected void assertInstances(GetAutoFollowPatternAction.Response serverTestIns assertThat(serverPattern.getRemoteCluster(), equalTo(clientPattern.getRemoteCluster())); assertThat(serverPattern.getLeaderIndexPatterns(), equalTo(clientPattern.getLeaderIndexPatterns())); assertThat(serverPattern.getFollowIndexPattern(), equalTo(clientPattern.getFollowIndexNamePattern())); + assertThat(serverPattern.getLeaderIndexExclusionPatterns(), equalTo(clientPattern.getLeaderIndexExclusionPatterns())); assertThat(serverPattern.getSettings(), equalTo(clientPattern.getSettings())); assertThat(serverPattern.getMaxOutstandingReadRequests(), equalTo(clientPattern.getMaxOutstandingReadRequests())); assertThat(serverPattern.getMaxOutstandingWriteRequests(), equalTo(clientPattern.getMaxOutstandingWriteRequests())); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/PutAutoFollowPatternRequestTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/PutAutoFollowPatternRequestTests.java index 466a12e94791e..5f80c95728a11 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/PutAutoFollowPatternRequestTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/PutAutoFollowPatternRequestTests.java @@ -28,7 +28,10 @@ public class PutAutoFollowPatternRequestTests extends AbstractRequestTestCase< protected PutAutoFollowPatternRequest createClientTestInstance() { // Name isn't serialized, because it specified in url path, so no need to randomly generate it here. PutAutoFollowPatternRequest putAutoFollowPatternRequest = new PutAutoFollowPatternRequest("name", - randomAlphaOfLength(4), Arrays.asList(generateRandomStringArray(4, 4, false))); + randomAlphaOfLength(4), + Arrays.asList(generateRandomStringArray(4, 4, false)), + Arrays.asList(generateRandomStringArray(4, 4, false)) + ); if (randomBoolean()) { putAutoFollowPatternRequest.setFollowIndexNamePattern(randomAlphaOfLength(4)); } @@ -75,6 +78,7 @@ protected void assertInstances(PutAutoFollowPatternAction.Request serverInstance assertThat(serverInstance.getName(), equalTo(clientTestInstance.getName())); assertThat(serverInstance.getRemoteCluster(), equalTo(clientTestInstance.getRemoteCluster())); assertThat(serverInstance.getLeaderIndexPatterns(), equalTo(clientTestInstance.getLeaderIndexPatterns())); + assertThat(serverInstance.getLeaderIndexExclusionPatterns(), equalTo(clientTestInstance.getLeaderIndexExclusionPatterns())); assertThat(serverInstance.getFollowIndexNamePattern(), equalTo(clientTestInstance.getFollowIndexNamePattern())); assertFollowConfig(serverInstance.getParameters(), clientTestInstance); } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/CCRDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/CCRDocumentationIT.java index d16f230067876..36d193f1a9608 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/CCRDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/CCRDocumentationIT.java @@ -474,12 +474,13 @@ public void testPutAutoFollowPattern() throws Exception { new PutAutoFollowPatternRequest( "my_pattern", // <1> "local", // <2> - Arrays.asList("logs-*", "metrics-*") // <3> + Arrays.asList("logs-*", "metrics-*"), // <3> + Arrays.asList("logs-excluded", "metrics-excluded") // <4> ); - request.setFollowIndexNamePattern("copy-{{leader_index}}"); // <4> + request.setFollowIndexNamePattern("copy-{{leader_index}}"); // <5> Settings settings = Settings.builder().put("index.number_of_replicas", 0L).build(); - request.setSettings(settings); // <5> + request.setSettings(settings); // <6> // end::ccr-put-auto-follow-pattern-request // tag::ccr-put-auto-follow-pattern-execute diff --git a/docs/java-rest/high-level/ccr/put_auto_follow_pattern.asciidoc b/docs/java-rest/high-level/ccr/put_auto_follow_pattern.asciidoc index a57b26738a4e6..e9ebbe7c86feb 100644 --- a/docs/java-rest/high-level/ccr/put_auto_follow_pattern.asciidoc +++ b/docs/java-rest/high-level/ccr/put_auto_follow_pattern.asciidoc @@ -21,8 +21,9 @@ include-tagged::{doc-tests-file}[{api}-request] <1> The name of the auto follow pattern. <2> The name of the remote cluster. <3> The leader index patterns. -<4> The pattern used to create the follower index -<5> The settings overrides for the follower index +<4> The leader index exclusion patterns. +<5> The pattern used to create the follower index. +<6> The settings overrides for the follower index. [id="{upid}-{api}-response"] ==== Response diff --git a/docs/reference/ccr/apis/auto-follow/get-auto-follow-pattern.asciidoc b/docs/reference/ccr/apis/auto-follow/get-auto-follow-pattern.asciidoc index 5ea23782e1967..2c32c1ccdc3ab 100644 --- a/docs/reference/ccr/apis/auto-follow/get-auto-follow-pattern.asciidoc +++ b/docs/reference/ccr/apis/auto-follow/get-auto-follow-pattern.asciidoc @@ -96,6 +96,7 @@ The API returns the following result: [ "leader_index*" ], + "leader_index_exclusion_patterns": [], "follow_index_pattern" : "{{leader_index}}-follower" } } diff --git a/docs/reference/ccr/apis/auto-follow/put-auto-follow-pattern.asciidoc b/docs/reference/ccr/apis/auto-follow/put-auto-follow-pattern.asciidoc index 1ef668fae9f7f..866d01fbb6083 100644 --- a/docs/reference/ccr/apis/auto-follow/put-auto-follow-pattern.asciidoc +++ b/docs/reference/ccr/apis/auto-follow/put-auto-follow-pattern.asciidoc @@ -20,6 +20,10 @@ PUT /_ccr/auto_follow/ [ "" ], + "leader_index_exclusion_patterns": + [ + "leader_index_exclusion_pattern>" + ], "follow_index_pattern" : "" } -------------------------------------------------- @@ -27,6 +31,7 @@ PUT /_ccr/auto_follow/ // TEST[s//auto_follow_pattern_name/] // TEST[s//remote_cluster/] // TEST[s//leader_index*/] +// TEST[s///] // TEST[s//{{leader_index}}-follower/] ////////////////////////// @@ -72,6 +77,10 @@ indices. (Optional, array) An array of simple index patterns to match against indices in the remote cluster specified by the `remote_cluster` field. +`leader_index_exclusion_patterns`:: + (Optional, array) An array of simple index patterns to avoid following + indices in the remote cluster specified by the `remote_cluster` field. + `follow_index_pattern`:: (Optional, string) The name of follower index. The template `{{leader_index}}` can be used to derive the name of the follower index from the name of the diff --git a/x-pack/plugin/ccr/qa/multi-cluster/src/test/java/org/elasticsearch/xpack/ccr/AutoFollowIT.java b/x-pack/plugin/ccr/qa/multi-cluster/src/test/java/org/elasticsearch/xpack/ccr/AutoFollowIT.java index 35d45cae2e9ea..2237a0e499e19 100644 --- a/x-pack/plugin/ccr/qa/multi-cluster/src/test/java/org/elasticsearch/xpack/ccr/AutoFollowIT.java +++ b/x-pack/plugin/ccr/qa/multi-cluster/src/test/java/org/elasticsearch/xpack/ccr/AutoFollowIT.java @@ -174,6 +174,64 @@ public void testAutoFollowPatterns() throws Exception { } } + public void testAutoFollowExclusionPatterns() throws Exception { + if ("follow".equals(targetCluster) == false) { + logger.info("skipping test, waiting for target cluster [follow]"); + return; + } + + final String autoFollowPatternName = getTestName().toLowerCase(Locale.ROOT); + try { + final String excludedIndex = "metrics-20220102"; + int initialNumberOfSuccessfulFollowedIndices = getNumberOfSuccessfulFollowedIndices(); + Request request = new Request("PUT", "/_ccr/auto_follow/" + autoFollowPatternName); + try (XContentBuilder bodyBuilder = JsonXContent.contentBuilder()) { + bodyBuilder.startObject(); + { + bodyBuilder.startArray("leader_index_patterns"); + { + bodyBuilder.value("metrics-*"); + } + bodyBuilder.endArray(); + bodyBuilder.startArray("leader_index_exclusion_patterns"); + { + bodyBuilder.value(excludedIndex); + } + bodyBuilder.endArray(); + bodyBuilder.field("remote_cluster", "leader_cluster"); + } + bodyBuilder.endObject(); + request.setJsonEntity(Strings.toString(bodyBuilder)); + } + assertOK(client().performRequest(request)); + + try (RestClient leaderClient = buildLeaderClient()) { + request = new Request("PUT", "/metrics-20220101"); + request.setJsonEntity("{\"mappings\": {\"properties\": {\"field\": {\"type\": \"keyword\"}}}}"); + assertOK(leaderClient.performRequest(request)); + } + + assertBusy(() -> { + assertThat(getNumberOfSuccessfulFollowedIndices(), equalTo(initialNumberOfSuccessfulFollowedIndices + 1)); + ensureYellow("metrics-20220101"); + }); + + try (RestClient leaderClient = buildLeaderClient()) { + request = new Request("PUT", "/" + excludedIndex); + request.setJsonEntity("{\"mappings\": {\"properties\": {\"field\": {\"type\": \"keyword\"}}}}"); + assertOK(leaderClient.performRequest(request)); + } + + assertBusy(() -> { + assertThat(getNumberOfSuccessfulFollowedIndices(), equalTo(initialNumberOfSuccessfulFollowedIndices + 1)); + }); + + assertThat(indexExists(excludedIndex), is(false)); + } finally { + cleanUpFollower(List.of("metrics-20220101"), List.of(), List.of(autoFollowPatternName)); + } + } + public void testPutAutoFollowPatternThatOverridesRequiredLeaderSetting() throws IOException { if ("follow".equals(targetCluster) == false) { logger.info("skipping test, waiting for target cluster [follow]"); diff --git a/x-pack/plugin/ccr/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/ccr/auto_follow.yml b/x-pack/plugin/ccr/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/ccr/auto_follow.yml index db54e2c38c7cb..40badf5cc64e0 100644 --- a/x-pack/plugin/ccr/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/ccr/auto_follow.yml +++ b/x-pack/plugin/ccr/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/ccr/auto_follow.yml @@ -25,6 +25,7 @@ body: remote_cluster: local leader_index_patterns: ['logs-*'] + leader_index_exclusion_patterns: ['logs-excluded'] max_outstanding_read_requests: 2 - is_true: acknowledged @@ -34,6 +35,7 @@ - match: { patterns.0.name: 'my_pattern' } - match: { patterns.0.pattern.remote_cluster: 'local' } - match: { patterns.0.pattern.leader_index_patterns: ['logs-*'] } + - match: { patterns.0.pattern.leader_index_exclusion_patterns: ['logs-excluded'] } - match: { patterns.0.pattern.max_outstanding_read_requests: 2 } - do: @@ -41,6 +43,7 @@ - match: { patterns.0.name: 'my_pattern' } - match: { patterns.0.pattern.remote_cluster: 'local' } - match: { patterns.0.pattern.leader_index_patterns: ['logs-*'] } + - match: { patterns.0.pattern.leader_index_exclusion_patterns: ['logs-excluded'] } - match: { patterns.0.pattern.max_outstanding_read_requests: 2 } - do: @@ -84,6 +87,7 @@ body: remote_cluster: local leader_index_patterns: ['logs-*'] + leader_index_exclusion_patterns: ['logs-excluded'] max_outstanding_read_requests: 2 - is_true: acknowledged @@ -112,6 +116,7 @@ - match: { patterns.0.name: 'pattern_test' } - match: { patterns.0.pattern.remote_cluster: 'local' } - match: { patterns.0.pattern.leader_index_patterns: ['logs-*'] } + - match: { patterns.0.pattern.leader_index_exclusion_patterns: ['logs-excluded'] } - match: { patterns.0.pattern.max_outstanding_read_requests: 2 } - match: { patterns.0.pattern.active: false } @@ -131,6 +136,7 @@ - match: { patterns.0.name: 'pattern_test' } - match: { patterns.0.pattern.remote_cluster: 'local' } - match: { patterns.0.pattern.leader_index_patterns: ['logs-*'] } + - match: { patterns.0.pattern.leader_index_exclusion_patterns: ['logs-excluded'] } - match: { patterns.0.pattern.max_outstanding_read_requests: 2 } - match: { patterns.0.pattern.active: true } diff --git a/x-pack/plugin/ccr/src/internalClusterTest/java/org/elasticsearch/xpack/ccr/AutoFollowIT.java b/x-pack/plugin/ccr/src/internalClusterTest/java/org/elasticsearch/xpack/ccr/AutoFollowIT.java index cbdcabfe89908..bf62f6d48311d 100644 --- a/x-pack/plugin/ccr/src/internalClusterTest/java/org/elasticsearch/xpack/ccr/AutoFollowIT.java +++ b/x-pack/plugin/ccr/src/internalClusterTest/java/org/elasticsearch/xpack/ccr/AutoFollowIT.java @@ -514,13 +514,53 @@ public void testPauseAndResumeWithMultipleAutoFollowPatterns() throws Exception assertThat(followerClient().admin().indices().prepareStats("copy-*").get().getIndices().size(), equalTo(leaderIndices.get())); } + public void testAutoFollowExclusion() throws Exception { + Settings leaderIndexSettings = Settings.builder() + .put(IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 1) + .put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 0) + .build(); + + putAutoFollowPatterns("my-pattern1", new String[] {"logs-*"}, Collections.singletonList("logs-2018*")); + + createLeaderIndex("logs-201701", leaderIndexSettings); + assertLongBusy(() -> { + AutoFollowStats autoFollowStats = getAutoFollowStats(); + assertThat(autoFollowStats.getNumberOfSuccessfulFollowIndices(), equalTo(1L)); + assertThat(autoFollowStats.getNumberOfFailedFollowIndices(), equalTo(0L)); + assertThat(autoFollowStats.getNumberOfFailedRemoteClusterStateRequests(), equalTo(0L)); + }); + assertTrue(ESIntegTestCase.indexExists("copy-logs-201701", followerClient())); + + createLeaderIndex("logs-201801", leaderIndexSettings); + assertLongBusy(() -> { + AutoFollowStats autoFollowStats = getAutoFollowStats(); + assertThat(autoFollowStats.getNumberOfSuccessfulFollowIndices(), equalTo(1L)); + assertThat(autoFollowStats.getNumberOfFailedFollowIndices(), greaterThanOrEqualTo(0L)); + assertThat(autoFollowStats.getNumberOfFailedRemoteClusterStateRequests(), equalTo(0L)); + }); + + assertFalse(ESIntegTestCase.indexExists("copy-logs-201801", followerClient())); + } + + public void testUpdatingExclusionPatternsIsNotAllowed() { + putAutoFollowPatterns("my-pattern1", new String[] {"logs-*"}, Collections.singletonList("logs-2018*")); + expectThrows(IllegalArgumentException.class, () -> + putAutoFollowPatterns("my-pattern1", new String[] {"logs-*"}, List.of("logs-2018*", "new-entry"))); + } + private void putAutoFollowPatterns(String name, String[] patterns) { + putAutoFollowPatterns(name, patterns, Collections.emptyList()); + } + + private void putAutoFollowPatterns(String name, String[] patterns, List exclusionPatterns) { PutAutoFollowPatternAction.Request request = new PutAutoFollowPatternAction.Request(); request.setName(name); request.setRemoteCluster("leader_cluster"); request.setLeaderIndexPatterns(Arrays.asList(patterns)); + request.setLeaderIndexExclusionPatterns(exclusionPatterns); // Need to set this, because following an index in the same cluster request.setFollowIndexNamePattern("copy-{{leader_index}}"); + assertTrue(followerClient().execute(PutAutoFollowPatternAction.INSTANCE, request).actionGet().isAcknowledged()); } diff --git a/x-pack/plugin/ccr/src/internalClusterTest/java/org/elasticsearch/xpack/ccr/CcrLicenseIT.java b/x-pack/plugin/ccr/src/internalClusterTest/java/org/elasticsearch/xpack/ccr/CcrLicenseIT.java index 9a7bedfe867e7..6c180ded50091 100644 --- a/x-pack/plugin/ccr/src/internalClusterTest/java/org/elasticsearch/xpack/ccr/CcrLicenseIT.java +++ b/x-pack/plugin/ccr/src/internalClusterTest/java/org/elasticsearch/xpack/ccr/CcrLicenseIT.java @@ -168,6 +168,7 @@ public ClusterState execute(ClusterState currentState) throws Exception { AutoFollowPattern autoFollowPattern = new AutoFollowPattern( "test_alias", Collections.singletonList("logs-*"), + Collections.emptyList(), null, Settings.EMPTY, true, diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportActivateAutoFollowPatternAction.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportActivateAutoFollowPatternAction.java index 0559f79e50030..a79c63250ef42 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportActivateAutoFollowPatternAction.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportActivateAutoFollowPatternAction.java @@ -77,6 +77,7 @@ static ClusterState innerActivate(final Request request, ClusterState currentSta new AutoFollowMetadata.AutoFollowPattern( previousAutoFollowPattern.getRemoteCluster(), previousAutoFollowPattern.getLeaderIndexPatterns(), + previousAutoFollowPattern.getLeaderIndexExclusionPatterns(), previousAutoFollowPattern.getFollowIndexPattern(), previousAutoFollowPattern.getSettings(), request.isActive(), diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportPutAutoFollowPatternAction.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportPutAutoFollowPatternAction.java index 05b4ed2caf1bd..5830498c81eec 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportPutAutoFollowPatternAction.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportPutAutoFollowPatternAction.java @@ -142,10 +142,22 @@ static ClusterState innerPut(PutAutoFollowPatternAction.Request request, followedLeaderIndices.put(request.getName(), followedIndexUUIDs); // Mark existing leader indices as already auto followed: if (previousPattern != null) { - markExistingIndicesAsAutoFollowedForNewPatterns(request.getLeaderIndexPatterns(), remoteClusterState.metadata(), - previousPattern, followedIndexUUIDs); + if (request.getLeaderIndexExclusionPatterns().equals(previousPattern.getLeaderIndexExclusionPatterns()) == false) { + throw new IllegalArgumentException("Updating the leader index exclusion patterns is not allowed"); + } + + markExistingIndicesAsAutoFollowedForNewPatterns(request.getLeaderIndexPatterns(), + request.getLeaderIndexExclusionPatterns(), + remoteClusterState.metadata(), + previousPattern, + followedIndexUUIDs + ); } else { - markExistingIndicesAsAutoFollowed(request.getLeaderIndexPatterns(), remoteClusterState.metadata(), followedIndexUUIDs); + markExistingIndicesAsAutoFollowed(request.getLeaderIndexPatterns(), + request.getLeaderIndexExclusionPatterns(), + remoteClusterState.metadata(), + followedIndexUUIDs + ); } if (filteredHeaders != null) { @@ -155,6 +167,7 @@ static ClusterState innerPut(PutAutoFollowPatternAction.Request request, AutoFollowPattern autoFollowPattern = new AutoFollowPattern( request.getRemoteCluster(), request.getLeaderIndexPatterns(), + request.getLeaderIndexExclusionPatterns(), request.getFollowIndexNamePattern(), request.getSettings(), true, @@ -178,6 +191,7 @@ static ClusterState innerPut(PutAutoFollowPatternAction.Request request, private static void markExistingIndicesAsAutoFollowedForNewPatterns( List leaderIndexPatterns, + List leaderIndexExclusionPatterns, Metadata leaderMetadata, AutoFollowPattern previousPattern, List followedIndexUUIDS) { @@ -186,17 +200,18 @@ private static void markExistingIndicesAsAutoFollowedForNewPatterns( .stream() .filter(p -> previousPattern.getLeaderIndexPatterns().contains(p) == false) .collect(Collectors.toList()); - markExistingIndicesAsAutoFollowed(newPatterns, leaderMetadata, followedIndexUUIDS); + markExistingIndicesAsAutoFollowed(newPatterns, leaderIndexExclusionPatterns, leaderMetadata, followedIndexUUIDS); } private static void markExistingIndicesAsAutoFollowed( List patterns, + List exclusionPatterns, Metadata leaderMetadata, List followedIndexUUIDS) { for (final IndexMetadata indexMetadata : leaderMetadata) { IndexAbstraction indexAbstraction = leaderMetadata.getIndicesLookup().get(indexMetadata.getIndex().getName()); - if (AutoFollowPattern.match(patterns, indexAbstraction)) { + if (AutoFollowPattern.match(patterns, exclusionPatterns, indexAbstraction)) { followedIndexUUIDS.add(indexMetadata.getIndexUUID()); } } diff --git a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/AutoFollowMetadataTests.java b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/AutoFollowMetadataTests.java index 9c057e4e69fba..c60f65875fbe5 100644 --- a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/AutoFollowMetadataTests.java +++ b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/AutoFollowMetadataTests.java @@ -43,9 +43,11 @@ protected AutoFollowMetadata createTestInstance() { Map> headers = new HashMap<>(numEntries); for (int i = 0; i < numEntries; i++) { List leaderPatterns = Arrays.asList(generateRandomStringArray(4, 4, false)); + List leaderExclusionPatterns = Arrays.asList(generateRandomStringArray(4, 4, false)); AutoFollowMetadata.AutoFollowPattern autoFollowPattern = new AutoFollowMetadata.AutoFollowPattern( randomAlphaOfLength(4), leaderPatterns, + leaderExclusionPatterns, randomAlphaOfLength(4), Settings.builder().put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), randomIntBetween(0, 4)).build(), true, diff --git a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/CCRInfoTransportActionTests.java b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/CCRInfoTransportActionTests.java index ecc58df18d42e..2e0e03e267e5b 100644 --- a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/CCRInfoTransportActionTests.java +++ b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/CCRInfoTransportActionTests.java @@ -100,6 +100,7 @@ public void testUsageStats() throws Exception { AutoFollowMetadata.AutoFollowPattern pattern = new AutoFollowMetadata.AutoFollowPattern( "remote_cluser", Collections.singletonList("logs" + i + "*"), + Collections.emptyList(), null, Settings.EMPTY, true, diff --git a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/AutoFollowCoordinatorTests.java b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/AutoFollowCoordinatorTests.java index 92466a38294a0..3f304d4ed5bc4 100644 --- a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/AutoFollowCoordinatorTests.java +++ b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/AutoFollowCoordinatorTests.java @@ -97,6 +97,7 @@ public void testAutoFollower() { AutoFollowPattern autoFollowPattern = new AutoFollowPattern( "remote", Collections.singletonList("logs-*"), + Collections.emptyList(), null, Settings.EMPTY, true, @@ -184,6 +185,7 @@ public void testAutoFollower_dataStream() { AutoFollowPattern autoFollowPattern = new AutoFollowPattern( "remote", Collections.singletonList("logs-*"), + Collections.emptyList(), null, Settings.EMPTY, true, @@ -269,6 +271,7 @@ public void testAutoFollowerClusterStateApiFailure() { AutoFollowPattern autoFollowPattern = new AutoFollowPattern( "remote", Collections.singletonList("logs-*"), + Collections.emptyList(), null, Settings.EMPTY, true, @@ -336,6 +339,7 @@ public void testAutoFollowerUpdateClusterStateFailure() { AutoFollowPattern autoFollowPattern = new AutoFollowPattern( "remote", Collections.singletonList("logs-*"), + Collections.emptyList(), null, Settings.EMPTY, true, @@ -408,6 +412,7 @@ public void testAutoFollowerWithNoActivePatternsDoesNotStart() { autoFollowPatterns.put("pattern_1", new AutoFollowPattern( remoteCluster, List.of("logs-*", "test-*"), + Collections.emptyList(), "copy-", Settings.EMPTY, false, @@ -425,7 +430,9 @@ public void testAutoFollowerWithNoActivePatternsDoesNotStart() { ); autoFollowPatterns.put("pattern_2", new AutoFollowPattern( remoteCluster, - List.of("users-*"), "copy-", + List.of("users-*"), + Collections.emptyList(), + "copy-", Settings.EMPTY, false, null, @@ -660,6 +667,7 @@ public void testAutoFollowerCreateAndFollowApiCallFailure() { AutoFollowPattern autoFollowPattern = new AutoFollowPattern( "remote", Collections.singletonList("logs-*"), + Collections.emptyList(), null, Settings.EMPTY, true, null, @@ -734,6 +742,7 @@ public void testGetLeaderIndicesToFollow() { final AutoFollowPattern autoFollowPattern = new AutoFollowPattern( "remote", Collections.singletonList("metrics-*"), + Collections.emptyList(), null, Settings.EMPTY, true, @@ -804,6 +813,7 @@ public void testGetLeaderIndicesToFollow() { final AutoFollowPattern inactiveAutoFollowPattern = new AutoFollowPattern( "remote", Collections.singletonList("metrics-*"), + Collections.emptyList(), null, Settings.EMPTY, false, @@ -830,6 +840,7 @@ public void testGetLeaderIndicesToFollow_shardsNotStarted() { AutoFollowPattern autoFollowPattern = new AutoFollowPattern( "remote", Collections.singletonList("*"), + Collections.emptyList(), null, Settings.EMPTY, true, @@ -885,6 +896,7 @@ public void testGetLeaderIndicesToFollowWithClosedIndices() { final AutoFollowPattern autoFollowPattern = new AutoFollowPattern( "remote", Collections.singletonList("*"), + Collections.emptyList(), null, Settings.EMPTY, true, @@ -1023,6 +1035,7 @@ public void testGetFollowerIndexName() { AutoFollowPattern autoFollowPattern = new AutoFollowPattern( "remote", Collections.singletonList("metrics-*"), + Collections.emptyList(), null, Settings.EMPTY, true, @@ -1042,6 +1055,7 @@ public void testGetFollowerIndexName() { autoFollowPattern = new AutoFollowPattern( "remote", Collections.singletonList("metrics-*"), + Collections.emptyList(), "eu-metrics-0", Settings.EMPTY, true, @@ -1061,6 +1075,7 @@ public void testGetFollowerIndexName() { autoFollowPattern = new AutoFollowPattern( "remote", Collections.singletonList("metrics-*"), + Collections.emptyList(), "eu-{{leader_index}}", Settings.EMPTY, true, @@ -1158,6 +1173,7 @@ public void testUpdateAutoFollowers() { "pattern1", new AutoFollowPattern( "remote1", Collections.singletonList("logs-*"), + Collections.emptyList(), null, Settings.EMPTY, true, @@ -1178,6 +1194,7 @@ public void testUpdateAutoFollowers() { new AutoFollowPattern( "remote2", Collections.singletonList("logs-*"), + Collections.emptyList(), null, Settings.EMPTY, true, @@ -1198,6 +1215,7 @@ public void testUpdateAutoFollowers() { new AutoFollowPattern( "remote2", Collections.singletonList("metrics-*"), + Collections.emptyList(), null, Settings.EMPTY, true, @@ -1242,6 +1260,7 @@ public void testUpdateAutoFollowers() { new AutoFollowPattern( "remote1", Collections.singletonList("metrics-*"), + Collections.emptyList(), null, Settings.EMPTY, true, @@ -1336,6 +1355,7 @@ public void testUpdateAutoFollowersNoActivePatterns() { new AutoFollowPattern( "remote1", Collections.singletonList("logs-*"), + Collections.emptyList(), null, Settings.EMPTY, true, @@ -1355,6 +1375,7 @@ public void testUpdateAutoFollowersNoActivePatterns() { "pattern2", new AutoFollowPattern("remote2", Collections.singletonList("logs-*"), + Collections.emptyList(), null, Settings.EMPTY, true, @@ -1374,6 +1395,7 @@ public void testUpdateAutoFollowersNoActivePatterns() { "pattern3", new AutoFollowPattern("remote2", Collections.singletonList("metrics-*"), + Collections.emptyList(), null, Settings.EMPTY, true, @@ -1409,6 +1431,7 @@ public void testUpdateAutoFollowersNoActivePatterns() { (name, pattern) -> new AutoFollowPattern( pattern.getRemoteCluster(), pattern.getLeaderIndexPatterns(), + pattern.getLeaderIndexExclusionPatterns(), pattern.getFollowIndexPattern(), Settings.EMPTY, false, @@ -1428,6 +1451,7 @@ public void testUpdateAutoFollowersNoActivePatterns() { "pattern3", (name, pattern) -> new AutoFollowPattern(pattern.getRemoteCluster(), pattern.getLeaderIndexPatterns(), + pattern.getLeaderIndexExclusionPatterns(), pattern.getFollowIndexPattern(), Settings.EMPTY, false, @@ -1459,6 +1483,7 @@ public void testUpdateAutoFollowersNoActivePatterns() { new AutoFollowPattern( "remote1", Collections.singletonList("metrics-*"), + Collections.emptyList(), null, Settings.EMPTY, true, @@ -1478,6 +1503,7 @@ public void testUpdateAutoFollowersNoActivePatterns() { "pattern2", (name, pattern) -> new AutoFollowPattern(pattern.getRemoteCluster(), pattern.getLeaderIndexPatterns(), + pattern.getLeaderIndexExclusionPatterns(), pattern.getFollowIndexPattern(), Settings.EMPTY, false, @@ -1523,6 +1549,7 @@ public void testWaitForMetadataVersion() { AutoFollowPattern autoFollowPattern = new AutoFollowPattern( "remote", Collections.singletonList("logs-*"), + Collections.emptyList(), null, Settings.EMPTY, true, @@ -1601,6 +1628,7 @@ public void testWaitForTimeOut() { AutoFollowPattern autoFollowPattern = new AutoFollowPattern( "remote", Collections.singletonList("logs-*"), + Collections.emptyList(), null, Settings.EMPTY, true, @@ -1674,6 +1702,7 @@ public void testAutoFollowerSoftDeletesDisabled() { AutoFollowPattern autoFollowPattern = new AutoFollowPattern( "remote", Collections.singletonList("logs-*"), + Collections.emptyList(), null, Settings.EMPTY, true, @@ -1755,6 +1784,7 @@ public void testAutoFollowerFollowerIndexAlreadyExists() { AutoFollowPattern autoFollowPattern = new AutoFollowPattern( "remote", Collections.singletonList("logs-*"), + Collections.emptyList(), null, Settings.EMPTY, true, @@ -1853,6 +1883,7 @@ public void testRepeatedFailures() throws InterruptedException { final AutoFollowPattern pattern = new AutoFollowPattern( "remote", List.of("*"), + Collections.emptyList(), "{}", Settings.EMPTY, true, @@ -1934,6 +1965,7 @@ public void testClosedIndicesAreNotAutoFollowed() { new AutoFollowPattern( "remote", List.of("docs-*"), + Collections.emptyList(), null, Settings.EMPTY, true, @@ -2019,6 +2051,103 @@ void cleanFollowedRemoteIndices(ClusterState remoteClusterState, List pa } } + public void testExcludedPatternIndicesAreNotAutoFollowed() { + final Client client = mock(Client.class); + when(client.getRemoteClusterClient(anyString())).thenReturn(client); + + final String pattern = "pattern1"; + final ClusterState localState = ClusterState.builder(new ClusterName("local")) + .metadata(Metadata.builder() + .putCustom(AutoFollowMetadata.TYPE, + new AutoFollowMetadata( + Map.of( + pattern, + new AutoFollowPattern( + "remote", + List.of("docs-*"), + List.of("docs-excluded-*"), + null, + Settings.EMPTY, + true, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null + ) + ), + Map.of(pattern, List.of()), + Map.of(pattern, Map.of())))) + .build(); + + ClusterState remoteState = null; + final int nbLeaderIndices = randomIntBetween(1, 15); + for (int i = 0; i < nbLeaderIndices; i++) { + String indexName = "docs-" + i; + if (remoteState == null) { + remoteState = createRemoteClusterState(indexName, true); + } else { + remoteState = createRemoteClusterState(remoteState, indexName); + } + } + + final int nbLeaderExcludedIndices = randomIntBetween(1, 15); + for (int i = 0; i < nbLeaderExcludedIndices; i++) { + String indexName = "docs-excluded-" + i; + remoteState = createRemoteClusterState(remoteState, indexName); + } + + final ClusterState finalRemoteState = remoteState; + final AtomicReference lastModifiedClusterState = new AtomicReference<>(localState); + final List results = new ArrayList<>(); + final Set followedIndices = ConcurrentCollections.newConcurrentSet(); + final AutoFollower autoFollower = + new AutoFollower("remote", results::addAll, localClusterStateSupplier(localState), () -> 1L, Runnable::run) { + @Override + void getRemoteClusterState(String remoteCluster, + long metadataVersion, + BiConsumer handler) { + assertThat(remoteCluster, equalTo("remote")); + handler.accept(new ClusterStateResponse(new ClusterName("remote"), finalRemoteState, false), null); + } + + @Override + void createAndFollow(Map headers, + PutFollowAction.Request followRequest, + Runnable successHandler, + Consumer failureHandler) { + followedIndices.add(followRequest.getLeaderIndex()); + successHandler.run(); + } + + @Override + void updateAutoFollowMetadata(Function updateFunction, Consumer handler) { + lastModifiedClusterState.updateAndGet(updateFunction::apply); + handler.accept(null); + } + + @Override + void cleanFollowedRemoteIndices(ClusterState remoteClusterState, List patterns) { + // Ignore, to avoid invoking updateAutoFollowMetadata(...) twice + } + }; + autoFollower.start(); + + assertThat(results, notNullValue()); + assertThat(results.size(), equalTo(1)); + + for (ObjectObjectCursor index : remoteState.metadata().indices()) { + boolean followed = index.value.getIndex().getName().startsWith("docs-excluded") == false; + assertThat(results.get(0).autoFollowExecutionResults.containsKey(index.value.getIndex()), is(followed)); + assertThat(followedIndices.contains(index.key), is(followed)); + } + } + private static ClusterState createRemoteClusterState(String indexName, boolean enableSoftDeletes) { return createRemoteClusterState(indexName, enableSoftDeletes, 0L); } diff --git a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/GetAutoFollowPatternResponseTests.java b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/GetAutoFollowPatternResponseTests.java index 030955725e31b..329e33c5584e6 100644 --- a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/GetAutoFollowPatternResponseTests.java +++ b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/GetAutoFollowPatternResponseTests.java @@ -35,6 +35,7 @@ protected GetAutoFollowPatternAction.Response createTestInstance() { AutoFollowPattern autoFollowPattern = new AutoFollowPattern( "remote", Collections.singletonList(randomAlphaOfLength(4)), + Collections.singletonList(randomAlphaOfLength(4)), randomAlphaOfLength(4), Settings.builder().put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), randomIntBetween(0, 4)).build(), true, diff --git a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/PutAutoFollowPatternRequestTests.java b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/PutAutoFollowPatternRequestTests.java index 3572ed53c8c20..192c50f74ac6d 100644 --- a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/PutAutoFollowPatternRequestTests.java +++ b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/PutAutoFollowPatternRequestTests.java @@ -49,6 +49,7 @@ protected PutAutoFollowPatternAction.Request createTestInstance() { if (randomBoolean()) { request.setFollowIndexNamePattern(randomAlphaOfLength(4)); } + request.setLeaderIndexExclusionPatterns(Arrays.asList(generateRandomStringArray(4, 4, false))); ResumeFollowActionRequestTests.generateFollowParameters(request.getParameters()); return request; } @@ -63,6 +64,7 @@ protected PutAutoFollowPatternAction.Request createXContextTestInstance(XContent if (randomBoolean()) { request.setFollowIndexNamePattern(randomAlphaOfLength(4)); } + request.setLeaderIndexExclusionPatterns(Arrays.asList(generateRandomStringArray(4, 4, false))); ResumeFollowActionRequestTests.generateFollowParameters(request.getParameters()); return request; } diff --git a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/TransportActivateAutoFollowPatternActionTests.java b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/TransportActivateAutoFollowPatternActionTests.java index 26209a170bd6b..557f0a8bd4edb 100644 --- a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/TransportActivateAutoFollowPatternActionTests.java +++ b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/TransportActivateAutoFollowPatternActionTests.java @@ -20,6 +20,7 @@ import org.elasticsearch.xpack.core.ccr.action.ActivateAutoFollowPatternAction.Request; import java.util.Arrays; +import java.util.Collections; import static java.util.Collections.singletonMap; import static org.hamcrest.Matchers.equalTo; @@ -83,6 +84,7 @@ public void testInnerActivateToggle() { private static AutoFollowMetadata.AutoFollowPattern randomAutoFollowPattern() { return new AutoFollowMetadata.AutoFollowPattern(randomAlphaOfLength(5), randomSubsetOf(Arrays.asList("test-*", "user-*", "logs-*", "failures-*")), + Collections.emptyList(), randomFrom("{{leader_index}}", "{{leader_index}}-follower", "test"), Settings.builder().put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), randomIntBetween(0, 4)).build(), randomBoolean(), diff --git a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/TransportDeleteAutoFollowPatternActionTests.java b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/TransportDeleteAutoFollowPatternActionTests.java index 3c369eda3e384..3a6c9b467fb4f 100644 --- a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/TransportDeleteAutoFollowPatternActionTests.java +++ b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/TransportDeleteAutoFollowPatternActionTests.java @@ -39,6 +39,7 @@ public void testInnerDelete() { new AutoFollowPattern( "eu_cluster", existingPatterns, + Collections.emptyList(), null, Settings.EMPTY, true, null, @@ -67,6 +68,7 @@ public void testInnerDelete() { new AutoFollowPattern( "asia_cluster", existingPatterns, + Collections.emptyList(), null, Settings.EMPTY, true, @@ -118,6 +120,7 @@ public void testInnerDeleteDoesNotExist() { new AutoFollowPattern( "eu_cluster", existingPatterns, + Collections.emptyList(), null, Settings.EMPTY, true, diff --git a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/TransportGetAutoFollowPatternActionTests.java b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/TransportGetAutoFollowPatternActionTests.java index b8d9ea944b0dd..04d700704fe05 100644 --- a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/TransportGetAutoFollowPatternActionTests.java +++ b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/TransportGetAutoFollowPatternActionTests.java @@ -30,6 +30,7 @@ public void testGetAutoFollowPattern() { new AutoFollowPattern( "test_alias1", Collections.singletonList("index-*"), + Collections.emptyList(), null, Settings.EMPTY, true, @@ -50,6 +51,7 @@ public void testGetAutoFollowPattern() { new AutoFollowPattern( "test_alias1", Collections.singletonList("index-*"), + Collections.emptyList(), null, Settings.EMPTY, true, diff --git a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/TransportPutAutoFollowPatternActionTests.java b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/TransportPutAutoFollowPatternActionTests.java index b1d7aea4b30d3..0efcc8db1ed2a 100644 --- a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/TransportPutAutoFollowPatternActionTests.java +++ b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/TransportPutAutoFollowPatternActionTests.java @@ -119,6 +119,7 @@ public void testInnerPut_existingLeaderIndicesAndAutoFollowMetadata() { new AutoFollowPattern( "eu_cluster", existingPatterns, + Collections.emptyList(), null, Settings.EMPTY, true, @@ -171,4 +172,51 @@ public void testInnerPut_existingLeaderIndicesAndAutoFollowMetadata() { assertThat(autoFollowMetadata.getHeaders().get("name1"), notNullValue()); } + public void testInnerPutWithExcludingPatterns() { + PutAutoFollowPatternAction.Request request = new PutAutoFollowPatternAction.Request(); + request.setName("name1"); + request.setRemoteCluster("eu_cluster"); + request.setLeaderIndexPatterns(Collections.singletonList("logs-*")); + request.setLeaderIndexExclusionPatterns(Collections.singletonList("logs-2018*")); + final int numberOfReplicas = randomIntBetween(0, 4); + request.setSettings(Settings.builder().put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), numberOfReplicas).build()); + + ClusterState localState = ClusterState.builder(new ClusterName("us_cluster")) + .metadata(Metadata.builder()) + .build(); + + int numLeaderIndices = randomIntBetween(1, 8); + int numMatchingLeaderIndices = randomIntBetween(1, 8); + Metadata.Builder mdBuilder = Metadata.builder(); + for (int i = 0; i < numLeaderIndices; i++) { + mdBuilder.put(IndexMetadata.builder("logs-2018" + i) + .settings(settings(Version.CURRENT)) + .numberOfShards(1) + .numberOfReplicas(0)); + } + for (int i = 0; i < numMatchingLeaderIndices; i++) { + mdBuilder.put(IndexMetadata.builder("logs-2017" + i) + .settings(settings(Version.CURRENT)) + .numberOfShards(1) + .numberOfReplicas(0)); + } + + ClusterState remoteState = ClusterState.builder(new ClusterName("eu_cluster")) + .metadata(mdBuilder) + .build(); + + ClusterState result = TransportPutAutoFollowPatternAction.innerPut(request, null, localState, remoteState); + AutoFollowMetadata autoFollowMetadata = result.metadata().custom(AutoFollowMetadata.TYPE); + assertThat(autoFollowMetadata, notNullValue()); + assertThat(autoFollowMetadata.getPatterns().size(), equalTo(1)); + assertThat(autoFollowMetadata.getPatterns().get("name1").getRemoteCluster(), equalTo("eu_cluster")); + assertThat(autoFollowMetadata.getPatterns().get("name1").getLeaderIndexPatterns().size(), equalTo(1)); + assertThat(autoFollowMetadata.getPatterns().get("name1").getLeaderIndexPatterns().get(0), equalTo("logs-*")); + assertThat(autoFollowMetadata.getPatterns().get("name1").getLeaderIndexExclusionPatterns().size(), equalTo(1)); + assertThat(autoFollowMetadata.getPatterns().get("name1").getLeaderIndexExclusionPatterns().get(0), equalTo("logs-2018*")); + + assertThat(autoFollowMetadata.getFollowedLeaderIndexUUIDs().size(), equalTo(1)); + assertThat(autoFollowMetadata.getFollowedLeaderIndexUUIDs().get("name1").size(), equalTo(numMatchingLeaderIndices)); + } + } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ccr/AutoFollowMetadata.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ccr/AutoFollowMetadata.java index 495cfebb82873..a9ca4aff547d7 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ccr/AutoFollowMetadata.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ccr/AutoFollowMetadata.java @@ -181,6 +181,7 @@ public static class AutoFollowPattern extends ImmutableFollowParameters implemen public static final ParseField ACTIVE = new ParseField("active"); public static final ParseField REMOTE_CLUSTER_FIELD = new ParseField("remote_cluster"); public static final ParseField LEADER_PATTERNS_FIELD = new ParseField("leader_index_patterns"); + public static final ParseField LEADER_EXCLUSION_PATTERNS_FIELD = new ParseField("leader_index_exclusion_patterns"); public static final ParseField FOLLOW_PATTERN_FIELD = new ParseField("follow_index_pattern"); public static final ParseField SETTINGS_FIELD = new ParseField("settings"); @@ -190,24 +191,26 @@ public static class AutoFollowPattern extends ImmutableFollowParameters implemen args -> new AutoFollowPattern( (String) args[0], (List) args[1], - (String) args[2], - args[3] == null ? Settings.EMPTY : (Settings) args[3], - args[4] == null || (boolean) args[4], - (Integer) args[5], + args[2] == null ? Collections.emptyList() : (List) args[2], + (String) args[3], + args[4] == null ? Settings.EMPTY : (Settings) args[4], + args[5] == null || (boolean) args[5], (Integer) args[6], (Integer) args[7], (Integer) args[8], - (ByteSizeValue) args[9], + (Integer) args[9], (ByteSizeValue) args[10], - (Integer) args[11], - (ByteSizeValue) args[12], - (TimeValue) args[13], - (TimeValue) args[14]) + (ByteSizeValue) args[11], + (Integer) args[12], + (ByteSizeValue) args[13], + (TimeValue) args[14], + (TimeValue) args[15]) ); static { PARSER.declareString(ConstructingObjectParser.constructorArg(), REMOTE_CLUSTER_FIELD); PARSER.declareStringArray(ConstructingObjectParser.constructorArg(), LEADER_PATTERNS_FIELD); + PARSER.declareStringArray(ConstructingObjectParser.optionalConstructorArg(), LEADER_EXCLUSION_PATTERNS_FIELD); PARSER.declareString(ConstructingObjectParser.optionalConstructorArg(), FOLLOW_PATTERN_FIELD); PARSER.declareObject( ConstructingObjectParser.optionalConstructorArg(), @@ -220,6 +223,7 @@ public static class AutoFollowPattern extends ImmutableFollowParameters implemen private final String remoteCluster; private final List leaderIndexPatterns; + private final List leaderIndexExclusionPatterns; private final String followIndexPattern; private final Settings settings; private final boolean active; @@ -227,6 +231,7 @@ public static class AutoFollowPattern extends ImmutableFollowParameters implemen public AutoFollowPattern( String remoteCluster, List leaderIndexPatterns, + List leaderIndexExclusionPatterns, String followIndexPattern, Settings settings, boolean active, @@ -245,6 +250,7 @@ public AutoFollowPattern( maxReadRequestSize, maxWriteRequestSize, maxWriteBufferCount, maxWriteBufferSize, maxRetryDelay, pollTimeout); this.remoteCluster = remoteCluster; this.leaderIndexPatterns = leaderIndexPatterns; + this.leaderIndexExclusionPatterns = leaderIndexExclusionPatterns; this.followIndexPattern = followIndexPattern; this.settings = Objects.requireNonNull(settings); this.active = active; @@ -263,8 +269,11 @@ public static AutoFollowPattern readFrom(StreamInput in) throws IOException { return new AutoFollowPattern(remoteCluster, leaderIndexPatterns, followIndexPattern, settings, in); } - private AutoFollowPattern(String remoteCluster, List leaderIndexPatterns, - String followIndexPattern, Settings settings, StreamInput in) throws IOException { + private AutoFollowPattern(String remoteCluster, + List leaderIndexPatterns, + String followIndexPattern, + Settings settings, + StreamInput in) throws IOException { super(in); this.remoteCluster = remoteCluster; this.leaderIndexPatterns = leaderIndexPatterns; @@ -275,18 +284,27 @@ private AutoFollowPattern(String remoteCluster, List leaderIndexPatterns } else { this.active = true; } + if (in.getVersion().onOrAfter(Version.V_8_0_0)) { + this.leaderIndexExclusionPatterns = in.readStringList(); + } else { + this.leaderIndexExclusionPatterns = Collections.emptyList(); + } } public boolean match(IndexAbstraction indexAbstraction) { - return match(leaderIndexPatterns, indexAbstraction); + return match(leaderIndexPatterns, leaderIndexExclusionPatterns, indexAbstraction); } - public static boolean match(List leaderIndexPatterns, IndexAbstraction indexAbstraction) { - boolean matches = Regex.simpleMatch(leaderIndexPatterns, indexAbstraction.getName()); + public static boolean match(List leaderIndexPatterns, + List leaderIndexExclusionPatterns, + IndexAbstraction indexAbstraction) { + boolean matches = Regex.simpleMatch(leaderIndexExclusionPatterns, indexAbstraction.getName()) == false && + Regex.simpleMatch(leaderIndexPatterns, indexAbstraction.getName()); if (matches) { return true; } else { return indexAbstraction.getParentDataStream() != null && + Regex.simpleMatch(leaderIndexExclusionPatterns, indexAbstraction.getParentDataStream().getName()) == false && Regex.simpleMatch(leaderIndexPatterns, indexAbstraction.getParentDataStream().getName()); } } @@ -299,6 +317,10 @@ public List getLeaderIndexPatterns() { return leaderIndexPatterns; } + public List getLeaderIndexExclusionPatterns() { + return leaderIndexExclusionPatterns; + } + public String getFollowIndexPattern() { return followIndexPattern; } @@ -323,6 +345,9 @@ public void writeTo(StreamOutput out) throws IOException { if (out.getVersion().onOrAfter(Version.V_7_5_0)) { out.writeBoolean(active); } + if (out.getVersion().onOrAfter(Version.V_8_0_0)) { + out.writeStringCollection(leaderIndexExclusionPatterns); + } } @Override @@ -330,6 +355,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.field(ACTIVE.getPreferredName(), active); builder.field(REMOTE_CLUSTER_FIELD.getPreferredName(), remoteCluster); builder.array(LEADER_PATTERNS_FIELD.getPreferredName(), leaderIndexPatterns.toArray(new String[0])); + builder.array(LEADER_EXCLUSION_PATTERNS_FIELD.getPreferredName(), leaderIndexExclusionPatterns.toArray(new String[0])); if (followIndexPattern != null) { builder.field(FOLLOW_PATTERN_FIELD.getPreferredName(), followIndexPattern); } @@ -353,13 +379,21 @@ public boolean equals(Object o) { return active == pattern.active && remoteCluster.equals(pattern.remoteCluster) && leaderIndexPatterns.equals(pattern.leaderIndexPatterns) && + leaderIndexExclusionPatterns.equals(pattern.leaderIndexExclusionPatterns) && followIndexPattern.equals(pattern.followIndexPattern) && settings.equals(pattern.settings); } @Override public int hashCode() { - return Objects.hash(super.hashCode(), remoteCluster, leaderIndexPatterns, followIndexPattern, settings, active); + return Objects.hash(super.hashCode(), + remoteCluster, + leaderIndexPatterns, + leaderIndexExclusionPatterns, + followIndexPattern, + settings, + active + ); } } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ccr/action/PutAutoFollowPatternAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ccr/action/PutAutoFollowPatternAction.java index df1266d674488..e5b154d72552a 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ccr/action/PutAutoFollowPatternAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ccr/action/PutAutoFollowPatternAction.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.util.Collections; import java.util.List; import java.util.Objects; @@ -52,6 +53,10 @@ public static class Request extends AcknowledgedRequest implements ToXC PARSER.declareStringArray((params, value) -> params.leaderIndexPatterns = value, AutoFollowPattern.LEADER_PATTERNS_FIELD); PARSER.declareString((params, value) -> params.followIndexNamePattern = value, AutoFollowPattern.FOLLOW_PATTERN_FIELD); PARSER.declareObject((params, value) -> params.settings = value, (p, c) -> Settings.fromXContent(p), SETTINGS_FIELD); + PARSER.declareStringArray( + (params, value) -> params.leaderIndexExclusionPatterns = value, + AutoFollowPattern.LEADER_EXCLUSION_PATTERNS_FIELD + ); FollowParameters.initParser(PARSER); } @@ -62,6 +67,9 @@ public static Request fromXContent(XContentParser parser, String name) throws IO request.setRemoteCluster(parameters.remoteCluster); request.setLeaderIndexPatterns(parameters.leaderIndexPatterns); request.setFollowIndexNamePattern(parameters.followIndexNamePattern); + if (parameters.leaderIndexExclusionPatterns != null) { + request.setLeaderIndexExclusionPatterns(parameters.leaderIndexExclusionPatterns); + } request.setSettings(parameters.settings); request.setParameters(parameters); return request; @@ -73,6 +81,7 @@ public static Request fromXContent(XContentParser parser, String name) throws IO private String followIndexNamePattern; private Settings settings = Settings.EMPTY; private FollowParameters parameters = new FollowParameters(); + private List leaderIndexExclusionPatterns = Collections.emptyList(); public Request() { } @@ -131,6 +140,14 @@ public void setLeaderIndexPatterns(List leaderIndexPatterns) { this.leaderIndexPatterns = leaderIndexPatterns; } + public List getLeaderIndexExclusionPatterns() { + return leaderIndexExclusionPatterns; + } + + public void setLeaderIndexExclusionPatterns(List leaderIndexExclusionPatterns) { + this.leaderIndexExclusionPatterns = leaderIndexExclusionPatterns; + } + public String getFollowIndexNamePattern() { return followIndexNamePattern; } @@ -165,6 +182,9 @@ public Request(StreamInput in) throws IOException { settings = Settings.readSettingsFromStream(in); } parameters = new FollowParameters(in); + if (in.getVersion().onOrAfter(Version.V_8_0_0)) { + leaderIndexExclusionPatterns = in.readStringList(); + } } @Override @@ -178,6 +198,9 @@ public void writeTo(StreamOutput out) throws IOException { Settings.writeSettingsToStream(settings, out); } parameters.writeTo(out); + if (out.getVersion().onOrAfter(Version.V_8_0_0)) { + out.writeStringCollection(leaderIndexExclusionPatterns); + } } @Override @@ -186,6 +209,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws { builder.field(REMOTE_CLUSTER_FIELD.getPreferredName(), remoteCluster); builder.field(AutoFollowPattern.LEADER_PATTERNS_FIELD.getPreferredName(), leaderIndexPatterns); + builder.field(AutoFollowPattern.LEADER_EXCLUSION_PATTERNS_FIELD.getPreferredName(), leaderIndexExclusionPatterns); if (followIndexNamePattern != null) { builder.field(AutoFollowPattern.FOLLOW_PATTERN_FIELD.getPreferredName(), followIndexNamePattern); } @@ -210,13 +234,14 @@ public boolean equals(Object o) { return Objects.equals(name, request.name) && Objects.equals(remoteCluster, request.remoteCluster) && Objects.equals(leaderIndexPatterns, request.leaderIndexPatterns) && + Objects.equals(leaderIndexExclusionPatterns, request.leaderIndexExclusionPatterns) && Objects.equals(followIndexNamePattern, request.followIndexNamePattern) && Objects.equals(parameters, request.parameters); } @Override public int hashCode() { - return Objects.hash(name, remoteCluster, leaderIndexPatterns, followIndexNamePattern, parameters); + return Objects.hash(name, remoteCluster, leaderIndexPatterns, leaderIndexExclusionPatterns, followIndexNamePattern, parameters); } // This class only exists for reuse of the FollowParameters class, see comment above the parser field. @@ -226,7 +251,7 @@ private static class PutAutoFollowPatternParameters extends FollowParameters { private List leaderIndexPatterns; private String followIndexNamePattern; private Settings settings = Settings.EMPTY; - + private List leaderIndexExclusionPatterns; } }