From 0b70b9959f13238222e280477c9e38dbe3e0080c Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Wed, 20 Feb 2019 15:37:40 +0100 Subject: [PATCH] Add support for ccr follow info api to HLRC. (#39115) This API was introduces after #33824 was closed. --- .../org/elasticsearch/client/CcrClient.java | 44 +++++ .../client/CcrRequestConverters.java | 9 + .../client/ccr/FollowConfig.java | 40 ++++ .../client/ccr/FollowInfoRequest.java | 37 ++++ .../client/ccr/FollowInfoResponse.java | 178 ++++++++++++++++++ .../java/org/elasticsearch/client/CCRIT.java | 22 +++ .../client/ccr/FollowConfigTests.java | 80 ++++++++ .../client/ccr/FollowInfoResponseTests.java | 77 ++++++++ .../documentation/CCRDocumentationIT.java | 71 +++++++ .../high-level/ccr/get_follow_info.asciidoc | 35 ++++ .../high-level/supported-apis.asciidoc | 2 + 11 files changed, 595 insertions(+) create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/FollowInfoRequest.java create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/FollowInfoResponse.java create mode 100644 client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/FollowConfigTests.java create mode 100644 client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/FollowInfoResponseTests.java create mode 100644 docs/java-rest/high-level/ccr/get_follow_info.asciidoc diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/CcrClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/CcrClient.java index 9a17dabf39504..b6c6866966725 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/CcrClient.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/CcrClient.java @@ -23,6 +23,8 @@ import org.elasticsearch.client.ccr.CcrStatsRequest; import org.elasticsearch.client.ccr.CcrStatsResponse; import org.elasticsearch.client.ccr.DeleteAutoFollowPatternRequest; +import org.elasticsearch.client.ccr.FollowInfoRequest; +import org.elasticsearch.client.ccr.FollowInfoResponse; import org.elasticsearch.client.ccr.FollowStatsRequest; import org.elasticsearch.client.ccr.FollowStatsResponse; import org.elasticsearch.client.ccr.GetAutoFollowPatternRequest; @@ -452,4 +454,46 @@ public void getFollowStatsAsync(FollowStatsRequest request, ); } + /** + * Gets follow info for specific indices. + * + * See + * the docs for more. + * + * @param request the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @return the response + * @throws IOException in case there is a problem sending the request or parsing back the response + */ + public FollowInfoResponse getFollowInfo(FollowInfoRequest request, RequestOptions options) throws IOException { + return restHighLevelClient.performRequestAndParseEntity( + request, + CcrRequestConverters::getFollowInfo, + options, + FollowInfoResponse::fromXContent, + Collections.emptySet() + ); + } + + /** + * Asynchronously gets follow info for specific indices. + * + * See + * the docs for more. + * + * @param request the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + */ + public void getFollowInfoAsync(FollowInfoRequest request, + RequestOptions options, + ActionListener listener) { + restHighLevelClient.performRequestAsyncAndParseEntity( + request, + CcrRequestConverters::getFollowInfo, + options, + FollowInfoResponse::fromXContent, + listener, + Collections.emptySet() + ); + } } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/CcrRequestConverters.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/CcrRequestConverters.java index 526db2a86a761..2e05aee9d7598 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/CcrRequestConverters.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/CcrRequestConverters.java @@ -25,6 +25,7 @@ import org.apache.http.client.methods.HttpPut; import org.elasticsearch.client.ccr.CcrStatsRequest; import org.elasticsearch.client.ccr.DeleteAutoFollowPatternRequest; +import org.elasticsearch.client.ccr.FollowInfoRequest; import org.elasticsearch.client.ccr.FollowStatsRequest; import org.elasticsearch.client.ccr.GetAutoFollowPatternRequest; import org.elasticsearch.client.ccr.PauseFollowRequest; @@ -119,4 +120,12 @@ static Request getFollowStats(FollowStatsRequest followStatsRequest) { return new Request(HttpGet.METHOD_NAME, endpoint); } + static Request getFollowInfo(FollowInfoRequest followInfoRequest) { + String endpoint = new RequestConverters.EndpointBuilder() + .addPathPart(followInfoRequest.getFollowerIndex()) + .addPathPartAsIs("_ccr", "info") + .build(); + return new Request(HttpGet.METHOD_NAME, endpoint); + } + } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/FollowConfig.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/FollowConfig.java index eb9b5e80767db..37b0c5fa2c5d9 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/FollowConfig.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/FollowConfig.java @@ -22,8 +22,10 @@ import org.elasticsearch.common.ParseField; import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; import java.io.IOException; import java.util.Objects; @@ -41,6 +43,44 @@ public class FollowConfig { static final ParseField MAX_RETRY_DELAY_FIELD = new ParseField("max_retry_delay"); static final ParseField READ_POLL_TIMEOUT = new ParseField("read_poll_timeout"); + private static final ObjectParser PARSER = new ObjectParser<>( + "follow_config", + true, + FollowConfig::new); + + static { + PARSER.declareInt(FollowConfig::setMaxReadRequestOperationCount, MAX_READ_REQUEST_OPERATION_COUNT); + PARSER.declareInt(FollowConfig::setMaxOutstandingReadRequests, MAX_OUTSTANDING_READ_REQUESTS); + PARSER.declareField( + FollowConfig::setMaxReadRequestSize, + (p, c) -> ByteSizeValue.parseBytesSizeValue(p.text(), MAX_READ_REQUEST_SIZE.getPreferredName()), + MAX_READ_REQUEST_SIZE, + ObjectParser.ValueType.STRING); + PARSER.declareInt(FollowConfig::setMaxWriteRequestOperationCount, MAX_WRITE_REQUEST_OPERATION_COUNT); + PARSER.declareField( + FollowConfig::setMaxWriteRequestSize, + (p, c) -> ByteSizeValue.parseBytesSizeValue(p.text(), MAX_WRITE_REQUEST_SIZE.getPreferredName()), + MAX_WRITE_REQUEST_SIZE, + ObjectParser.ValueType.STRING); + PARSER.declareInt(FollowConfig::setMaxOutstandingWriteRequests, MAX_OUTSTANDING_WRITE_REQUESTS); + PARSER.declareInt(FollowConfig::setMaxWriteBufferCount, MAX_WRITE_BUFFER_COUNT); + PARSER.declareField( + FollowConfig::setMaxWriteBufferSize, + (p, c) -> ByteSizeValue.parseBytesSizeValue(p.text(), MAX_WRITE_BUFFER_SIZE.getPreferredName()), + MAX_WRITE_BUFFER_SIZE, + ObjectParser.ValueType.STRING); + PARSER.declareField(FollowConfig::setMaxRetryDelay, + (p, c) -> TimeValue.parseTimeValue(p.text(), MAX_RETRY_DELAY_FIELD.getPreferredName()), + MAX_RETRY_DELAY_FIELD, ObjectParser.ValueType.STRING); + PARSER.declareField(FollowConfig::setReadPollTimeout, + (p, c) -> TimeValue.parseTimeValue(p.text(), READ_POLL_TIMEOUT.getPreferredName()), + READ_POLL_TIMEOUT, ObjectParser.ValueType.STRING); + } + + static FollowConfig fromXContent(XContentParser parser) { + return PARSER.apply(parser, null); + } + private Integer maxReadRequestOperationCount; private Integer maxOutstandingReadRequests; private ByteSizeValue maxReadRequestSize; diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/FollowInfoRequest.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/FollowInfoRequest.java new file mode 100644 index 0000000000000..3fe9136fcbdb7 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/FollowInfoRequest.java @@ -0,0 +1,37 @@ +/* + * 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.client.ccr; + +import org.elasticsearch.client.Validatable; + +import java.util.Objects; + +public final class FollowInfoRequest implements Validatable { + + private final String followerIndex; + + public FollowInfoRequest(String followerIndex) { + this.followerIndex = Objects.requireNonNull(followerIndex); + } + + public String getFollowerIndex() { + return followerIndex; + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/FollowInfoResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/FollowInfoResponse.java new file mode 100644 index 0000000000000..3e15db318a3c1 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/FollowInfoResponse.java @@ -0,0 +1,178 @@ +/* + * 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.client.ccr; + +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.XContentParser; + +import java.util.List; +import java.util.Objects; + +public final class FollowInfoResponse { + + static final ParseField FOLLOWER_INDICES_FIELD = new ParseField("follower_indices"); + + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "indices", + true, + args -> { + @SuppressWarnings("unchecked") + List infos = (List) args[0]; + return new FollowInfoResponse(infos); + }); + + static { + PARSER.declareObjectArray(ConstructingObjectParser.constructorArg(), FollowerInfo.PARSER, FOLLOWER_INDICES_FIELD); + } + + public static FollowInfoResponse fromXContent(XContentParser parser) { + return PARSER.apply(parser, null); + } + + private final List infos; + + FollowInfoResponse(List infos) { + this.infos = infos; + } + + public List getInfos() { + return infos; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + FollowInfoResponse that = (FollowInfoResponse) o; + return infos.equals(that.infos); + } + + @Override + public int hashCode() { + return Objects.hash(infos); + } + + public static final class FollowerInfo { + + static final ParseField FOLLOWER_INDEX_FIELD = new ParseField("follower_index"); + static final ParseField REMOTE_CLUSTER_FIELD = new ParseField("remote_cluster"); + static final ParseField LEADER_INDEX_FIELD = new ParseField("leader_index"); + static final ParseField STATUS_FIELD = new ParseField("status"); + static final ParseField PARAMETERS_FIELD = new ParseField("parameters"); + + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "follower_info", + true, + args -> { + return new FollowerInfo((String) args[0], (String) args[1], (String) args[2], + Status.fromString((String) args[3]), (FollowConfig) args[4]); + }); + + static { + PARSER.declareString(ConstructingObjectParser.constructorArg(), FOLLOWER_INDEX_FIELD); + PARSER.declareString(ConstructingObjectParser.constructorArg(), REMOTE_CLUSTER_FIELD); + PARSER.declareString(ConstructingObjectParser.constructorArg(), LEADER_INDEX_FIELD); + PARSER.declareString(ConstructingObjectParser.constructorArg(), STATUS_FIELD); + PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), + (p, c) -> FollowConfig.fromXContent(p), PARAMETERS_FIELD); + } + + private final String followerIndex; + private final String remoteCluster; + private final String leaderIndex; + private final Status status; + private final FollowConfig parameters; + + FollowerInfo(String followerIndex, String remoteCluster, String leaderIndex, Status status, + FollowConfig parameters) { + this.followerIndex = followerIndex; + this.remoteCluster = remoteCluster; + this.leaderIndex = leaderIndex; + this.status = status; + this.parameters = parameters; + } + + public String getFollowerIndex() { + return followerIndex; + } + + public String getRemoteCluster() { + return remoteCluster; + } + + public String getLeaderIndex() { + return leaderIndex; + } + + public Status getStatus() { + return status; + } + + public FollowConfig getParameters() { + return parameters; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + FollowerInfo that = (FollowerInfo) o; + return Objects.equals(followerIndex, that.followerIndex) && + Objects.equals(remoteCluster, that.remoteCluster) && + Objects.equals(leaderIndex, that.leaderIndex) && + status == that.status && + Objects.equals(parameters, that.parameters); + } + + @Override + public int hashCode() { + return Objects.hash(followerIndex, remoteCluster, leaderIndex, status, parameters); + } + + } + + public enum Status { + + ACTIVE("active"), + PAUSED("paused"); + + private final String name; + + Status(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public static Status fromString(String value) { + switch (value) { + case "active": + return Status.ACTIVE; + case "paused": + return Status.PAUSED; + default: + throw new IllegalArgumentException("unexpected status value [" + value + "]"); + } + } + } +} 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 ee2685dee6d92..14e2f977e63d1 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 @@ -32,6 +32,8 @@ import org.elasticsearch.client.ccr.CcrStatsRequest; import org.elasticsearch.client.ccr.CcrStatsResponse; import org.elasticsearch.client.ccr.DeleteAutoFollowPatternRequest; +import org.elasticsearch.client.ccr.FollowInfoRequest; +import org.elasticsearch.client.ccr.FollowInfoResponse; import org.elasticsearch.client.ccr.FollowStatsRequest; import org.elasticsearch.client.ccr.FollowStatsResponse; import org.elasticsearch.client.ccr.GetAutoFollowPatternRequest; @@ -113,6 +115,15 @@ public void testIndexFollowing() throws Exception { try { assertBusy(() -> { + FollowInfoRequest followInfoRequest = new FollowInfoRequest("follower"); + FollowInfoResponse followInfoResponse = + execute(followInfoRequest, ccrClient::getFollowInfo, ccrClient::getFollowInfoAsync); + assertThat(followInfoResponse.getInfos().size(), equalTo(1)); + assertThat(followInfoResponse.getInfos().get(0).getFollowerIndex(), equalTo("follower")); + assertThat(followInfoResponse.getInfos().get(0).getLeaderIndex(), equalTo("leader")); + assertThat(followInfoResponse.getInfos().get(0).getRemoteCluster(), equalTo("local_cluster")); + assertThat(followInfoResponse.getInfos().get(0).getStatus(), equalTo(FollowInfoResponse.Status.ACTIVE)); + FollowStatsRequest followStatsRequest = new FollowStatsRequest("follower"); FollowStatsResponse followStatsResponse = execute(followStatsRequest, ccrClient::getFollowStats, ccrClient::getFollowStatsAsync); @@ -170,6 +181,17 @@ public void testIndexFollowing() throws Exception { pauseFollowResponse = execute(pauseFollowRequest, ccrClient::pauseFollow, ccrClient::pauseFollowAsync); assertThat(pauseFollowResponse.isAcknowledged(), is(true)); + assertBusy(() -> { + FollowInfoRequest followInfoRequest = new FollowInfoRequest("follower"); + FollowInfoResponse followInfoResponse = + execute(followInfoRequest, ccrClient::getFollowInfo, ccrClient::getFollowInfoAsync); + assertThat(followInfoResponse.getInfos().size(), equalTo(1)); + assertThat(followInfoResponse.getInfos().get(0).getFollowerIndex(), equalTo("follower")); + assertThat(followInfoResponse.getInfos().get(0).getLeaderIndex(), equalTo("leader")); + assertThat(followInfoResponse.getInfos().get(0).getRemoteCluster(), equalTo("local_cluster")); + assertThat(followInfoResponse.getInfos().get(0).getStatus(), equalTo(FollowInfoResponse.Status.PAUSED)); + }); + // Need to close index prior to unfollowing it: CloseIndexRequest closeIndexRequest = new CloseIndexRequest("follower"); org.elasticsearch.action.support.master.AcknowledgedResponse closeIndexReponse = diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/FollowConfigTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/FollowConfigTests.java new file mode 100644 index 0000000000000..0d8b4ca872696 --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/FollowConfigTests.java @@ -0,0 +1,80 @@ +/* + * 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.client.ccr; + +import org.elasticsearch.common.unit.ByteSizeValue; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.test.ESTestCase; + +import java.io.IOException; + +import static org.elasticsearch.test.AbstractXContentTestCase.xContentTester; + +public class FollowConfigTests extends ESTestCase { + + public void testFromXContent() throws IOException { + xContentTester(this::createParser, + FollowConfigTests::createTestInstance, + (followConfig, xContentBuilder) -> { + xContentBuilder.startObject(); + followConfig.toXContentFragment(xContentBuilder, ToXContent.EMPTY_PARAMS); + xContentBuilder.endObject(); + }, + FollowConfig::fromXContent) + .supportsUnknownFields(true) + .test(); + } + + static FollowConfig createTestInstance() { + FollowConfig followConfig = new FollowConfig(); + if (randomBoolean()) { + followConfig.setMaxOutstandingReadRequests(randomIntBetween(0, Integer.MAX_VALUE)); + } + if (randomBoolean()) { + followConfig.setMaxOutstandingWriteRequests(randomIntBetween(0, Integer.MAX_VALUE)); + } + if (randomBoolean()) { + followConfig.setMaxReadRequestOperationCount(randomIntBetween(0, Integer.MAX_VALUE)); + } + if (randomBoolean()) { + followConfig.setMaxReadRequestSize(new ByteSizeValue(randomNonNegativeLong())); + } + if (randomBoolean()) { + followConfig.setMaxWriteBufferCount(randomIntBetween(0, Integer.MAX_VALUE)); + } + if (randomBoolean()) { + followConfig.setMaxWriteBufferSize(new ByteSizeValue(randomNonNegativeLong())); + } + if (randomBoolean()) { + followConfig.setMaxWriteRequestOperationCount(randomIntBetween(0, Integer.MAX_VALUE)); + } + if (randomBoolean()) { + followConfig.setMaxWriteRequestSize(new ByteSizeValue(randomNonNegativeLong())); + } + if (randomBoolean()) { + followConfig.setMaxRetryDelay(new TimeValue(randomNonNegativeLong())); + } + if (randomBoolean()) { + followConfig.setReadPollTimeout(new TimeValue(randomNonNegativeLong())); + } + return followConfig; + } +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/FollowInfoResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/FollowInfoResponseTests.java new file mode 100644 index 0000000000000..5cd327495dc1c --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/FollowInfoResponseTests.java @@ -0,0 +1,77 @@ +/* + * 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.client.ccr; + +import org.elasticsearch.client.ccr.FollowInfoResponse.FollowerInfo; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.test.ESTestCase; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static org.elasticsearch.test.AbstractXContentTestCase.xContentTester; + +public class FollowInfoResponseTests extends ESTestCase { + + public void testFromXContent() throws IOException { + xContentTester(this::createParser, + FollowInfoResponseTests::createTestInstance, + FollowInfoResponseTests::toXContent, + FollowInfoResponse::fromXContent) + .supportsUnknownFields(true) + .test(); + } + + private static void toXContent(FollowInfoResponse response, XContentBuilder builder) throws IOException { + builder.startObject(); + builder.startArray(FollowInfoResponse.FOLLOWER_INDICES_FIELD.getPreferredName()); + for (FollowerInfo info : response.getInfos()) { + builder.startObject(); + builder.field(FollowerInfo.FOLLOWER_INDEX_FIELD.getPreferredName(), info.getFollowerIndex()); + builder.field(FollowerInfo.REMOTE_CLUSTER_FIELD.getPreferredName(), info.getRemoteCluster()); + builder.field(FollowerInfo.LEADER_INDEX_FIELD.getPreferredName(), info.getLeaderIndex()); + builder.field(FollowerInfo.STATUS_FIELD.getPreferredName(), info.getStatus().getName()); + if (info.getParameters() != null) { + builder.startObject(FollowerInfo.PARAMETERS_FIELD.getPreferredName()); + { + info.getParameters().toXContentFragment(builder, ToXContent.EMPTY_PARAMS); + } + builder.endObject(); + } + builder.endObject(); + } + builder.endArray(); + builder.endObject(); + } + + private static FollowInfoResponse createTestInstance() { + int numInfos = randomIntBetween(0, 64); + List infos = new ArrayList<>(numInfos); + for (int i = 0; i < numInfos; i++) { + FollowInfoResponse.Status status = randomFrom(FollowInfoResponse.Status.values()); + FollowConfig followConfig = randomBoolean() ? FollowConfigTests.createTestInstance() : null; + infos.add(new FollowerInfo(randomAlphaOfLength(4), randomAlphaOfLength(4), randomAlphaOfLength(4), status, followConfig)); + } + return new FollowInfoResponse(infos); + } + +} 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 2e54d1c4a1a7c..23cdd39787d32 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 @@ -36,6 +36,8 @@ import org.elasticsearch.client.ccr.CcrStatsRequest; import org.elasticsearch.client.ccr.CcrStatsResponse; import org.elasticsearch.client.ccr.DeleteAutoFollowPatternRequest; +import org.elasticsearch.client.ccr.FollowInfoRequest; +import org.elasticsearch.client.ccr.FollowInfoResponse; import org.elasticsearch.client.ccr.FollowStatsRequest; import org.elasticsearch.client.ccr.FollowStatsResponse; import org.elasticsearch.client.ccr.GetAutoFollowPatternRequest; @@ -58,6 +60,7 @@ import java.io.IOException; import java.util.Arrays; import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -697,6 +700,74 @@ public void onFailure(Exception e) { } } + public void testGetFollowInfos() throws Exception { + RestHighLevelClient client = highLevelClient(); + + { + // Create leader index: + CreateIndexRequest createIndexRequest = new CreateIndexRequest("leader"); + createIndexRequest.settings(Collections.singletonMap("index.soft_deletes.enabled", true)); + CreateIndexResponse response = client.indices().create(createIndexRequest, RequestOptions.DEFAULT); + assertThat(response.isAcknowledged(), is(true)); + } + { + // Follow index, so that we can query for follow stats: + PutFollowRequest putFollowRequest = new PutFollowRequest("local", "leader", "follower", ActiveShardCount.ONE); + PutFollowResponse putFollowResponse = client.ccr().putFollow(putFollowRequest, RequestOptions.DEFAULT); + assertThat(putFollowResponse.isFollowIndexCreated(), is(true)); + assertThat(putFollowResponse.isFollowIndexShardsAcked(), is(true)); + assertThat(putFollowResponse.isIndexFollowingStarted(), is(true)); + } + + // tag::ccr-get-follow-info-request + FollowInfoRequest request = + new FollowInfoRequest("follower"); // <1> + // end::ccr-get-follow-info-request + + // tag::ccr-get-follow-info-execute + FollowInfoResponse response = client.ccr() + .getFollowInfo(request, RequestOptions.DEFAULT); + // end::ccr-get-follow-info-execute + + // tag::ccr-get-follow-info-response + List infos = + response.getInfos(); // <1> + // end::ccr-get-follow-info-response + + // tag::ccr-get-follow-info-execute-listener + ActionListener listener = + new ActionListener() { + @Override + public void onResponse(FollowInfoResponse response) { // <1> + List infos = + response.getInfos(); + } + + @Override + public void onFailure(Exception e) { + // <2> + } + }; + // end::ccr-get-follow-info-execute-listener + + // Replace the empty listener by a blocking listener in test + final CountDownLatch latch = new CountDownLatch(1); + listener = new LatchedActionListener<>(listener, latch); + + // tag::ccr-get-follow-info-execute-async + client.ccr().getFollowInfoAsync(request, + RequestOptions.DEFAULT, listener); // <1> + // end::ccr-get-follow-info-execute-async + + assertTrue(latch.await(30L, TimeUnit.SECONDS)); + + { + PauseFollowRequest pauseFollowRequest = new PauseFollowRequest("follower"); + AcknowledgedResponse pauseFollowResponse = client.ccr().pauseFollow(pauseFollowRequest, RequestOptions.DEFAULT); + assertThat(pauseFollowResponse.isAcknowledged(), is(true)); + } + } + static Map toMap(Response response) throws IOException { return XContentHelper.convertToMap(JsonXContent.jsonXContent, EntityUtils.toString(response.getEntity()), false); } diff --git a/docs/java-rest/high-level/ccr/get_follow_info.asciidoc b/docs/java-rest/high-level/ccr/get_follow_info.asciidoc new file mode 100644 index 0000000000000..3d4e8d5456ef6 --- /dev/null +++ b/docs/java-rest/high-level/ccr/get_follow_info.asciidoc @@ -0,0 +1,35 @@ +-- +:api: ccr-get-follow-info +:request: FollowInfoRequest +:response: FollowInfoResponse +-- + +[id="{upid}-{api}"] +=== Get Follow Info API + + +[id="{upid}-{api}-request"] +==== Request + +The Get Follow Info API allows you to get follow information (parameters and status) for specific follower indices. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[{api}-request] +-------------------------------------------------- +<1> The follower index to get follow information for. + +[id="{upid}-{api}-response"] +==== Response + +The returned +{response}+ includes follow information for the specified follower indices + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[{api}-response] +-------------------------------------------------- +<1> The follow information for specified follower indices. + +include::../execution.asciidoc[] + + diff --git a/docs/java-rest/high-level/supported-apis.asciidoc b/docs/java-rest/high-level/supported-apis.asciidoc index 1df10985e7e3b..1f3bee46cc858 100644 --- a/docs/java-rest/high-level/supported-apis.asciidoc +++ b/docs/java-rest/high-level/supported-apis.asciidoc @@ -507,6 +507,7 @@ The Java High Level REST Client supports the following CCR APIs: * <<{upid}-ccr-get-auto-follow-pattern>> * <<{upid}-ccr-get-stats>> * <<{upid}-ccr-get-follow-stats>> +* <<{upid}-ccr-get-follow-info>> include::ccr/put_follow.asciidoc[] include::ccr/pause_follow.asciidoc[] @@ -517,6 +518,7 @@ include::ccr/delete_auto_follow_pattern.asciidoc[] include::ccr/get_auto_follow_pattern.asciidoc[] include::ccr/get_stats.asciidoc[] include::ccr/get_follow_stats.asciidoc[] +include::ccr/get_follow_info.asciidoc[] == Index Lifecycle Management APIs