diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java index a5a6b9f7bd271..de61d07e34ed0 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java @@ -32,6 +32,7 @@ import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksRequest; import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesRequest; import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequest; +import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryRequest; import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest; import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest; @@ -711,6 +712,19 @@ static Request createRepository(PutRepositoryRequest putRepositoryRequest) throw return request; } + static Request verifyRepository(VerifyRepositoryRequest verifyRepositoryRequest) { + String endpoint = new EndpointBuilder().addPathPartAsIs("_snapshot") + .addPathPart(verifyRepositoryRequest.name()) + .addPathPartAsIs("_verify") + .build(); + Request request = new Request(HttpPost.METHOD_NAME, endpoint); + + Params parameters = new Params(request); + parameters.withMasterTimeout(verifyRepositoryRequest.masterNodeTimeout()); + parameters.withTimeout(verifyRepositoryRequest.timeout()); + return request; + } + static Request putTemplate(PutIndexTemplateRequest putIndexTemplateRequest) throws IOException { String endpoint = new EndpointBuilder().addPathPartAsIs("_template").addPathPart(putIndexTemplateRequest.name()).build(); Request request = new Request(HttpPut.METHOD_NAME, endpoint); diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/SnapshotClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/SnapshotClient.java index aec94586bee30..0b65b5b5d299d 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/SnapshotClient.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/SnapshotClient.java @@ -25,6 +25,8 @@ import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesResponse; import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequest; import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryResponse; +import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryRequest; +import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryRestResponse; import java.io.IOException; @@ -90,4 +92,28 @@ public void createRepositoryAsync(PutRepositoryRequest putRepositoryRequest, restHighLevelClient.performRequestAsyncAndParseEntity(putRepositoryRequest, RequestConverters::createRepository, PutRepositoryResponse::fromXContent, listener, emptySet(), headers); } + + /** + * Verifies a snapshot repository. + *

+ * See Snapshot and Restore + * API on elastic.co + */ + public VerifyRepositoryRestResponse verifyRepository(VerifyRepositoryRequest verifyRepositoryRequest, Header... headers) + throws IOException { + return restHighLevelClient.performRequestAndParseEntity(verifyRepositoryRequest, RequestConverters::verifyRepository, + VerifyRepositoryRestResponse::fromXContent, emptySet(), headers); + } + + /** + * Asynchronously verifies a snapshot repository. + *

+ * See Snapshot and Restore + * API on elastic.co + */ + public void verifyRepositoryAsync(VerifyRepositoryRequest verifyRepositoryRequest, + ActionListener listener, Header... headers) { + restHighLevelClient.performRequestAsyncAndParseEntity(verifyRepositoryRequest, RequestConverters::verifyRepository, + VerifyRepositoryRestResponse::fromXContent, listener, emptySet(), headers); + } } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java index 4a0276e74d228..62d76029668b0 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java @@ -32,6 +32,7 @@ import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksRequest; import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesRequest; import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequest; +import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryRequest; import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; import org.elasticsearch.action.admin.indices.alias.Alias; import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest; @@ -1566,6 +1567,21 @@ public void testCreateRepository() throws IOException { assertToXContentBody(putRepositoryRequest, request.getEntity()); } + public void testVerifyRepository() throws IOException { + Map expectedParams = new HashMap<>(); + String repository = "repo"; + String endpoint = "/_snapshot/" + repository + "/_verify"; + + VerifyRepositoryRequest verifyRepositoryRequest = new VerifyRepositoryRequest(repository); + setRandomMasterTimeout(verifyRepositoryRequest, expectedParams); + setRandomTimeout(verifyRepositoryRequest::timeout, AcknowledgedRequest.DEFAULT_ACK_TIMEOUT, expectedParams); + + Request request = RequestConverters.verifyRepository(verifyRepositoryRequest); + assertThat(endpoint, equalTo(request.getEndpoint())); + assertThat(HttpPost.METHOD_NAME, equalTo(request.getMethod())); + assertThat(expectedParams, equalTo(request.getParameters())); + } + public void testPutTemplateRequest() throws Exception { Map names = new HashMap<>(); names.put("log", "log"); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/SnapshotIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/SnapshotIT.java index 1d0ea953cd5c1..18d7f00df8e18 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/SnapshotIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/SnapshotIT.java @@ -24,6 +24,8 @@ import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesResponse; import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequest; import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryResponse; +import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryRequest; +import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryRestResponse; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.repositories.fs.FsRepository; import org.elasticsearch.rest.RestStatus; @@ -48,6 +50,16 @@ public void testCreateRepository() throws IOException { assertTrue(response.isAcknowledged()); } + public void testVerifyRepository() throws IOException { + PutRepositoryResponse putRepositoryResponse = createTestRepository("test", FsRepository.TYPE, "{\"location\": \".\"}"); + assertTrue(putRepositoryResponse.isAcknowledged()); + + VerifyRepositoryRequest request = new VerifyRepositoryRequest("test"); + VerifyRepositoryRestResponse response = execute(request, highLevelClient().snapshot()::verifyRepository, + highLevelClient().snapshot()::verifyRepositoryAsync); + assertThat(response.nodes().size(), equalTo(1)); + } + public void testModulesGetRepositoriesUsingParams() throws IOException { String testRepository = "test"; assertTrue(createTestRepository(testRepository, FsRepository.TYPE, "{\"location\": \".\"}").isAcknowledged()); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SnapshotClientDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SnapshotClientDocumentationIT.java index c57f8e2a2fbd5..c007287f78d74 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SnapshotClientDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SnapshotClientDocumentationIT.java @@ -25,6 +25,8 @@ import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesResponse; import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequest; import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryResponse; +import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryRequest; +import org.elasticsearch.action.admin.cluster.repositories.verify.VerifyRepositoryRestResponse; import org.elasticsearch.client.ESRestHighLevelClientTestCase; import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.cluster.metadata.RepositoryMetaData; @@ -64,6 +66,66 @@ public class SnapshotClientDocumentationIT extends ESRestHighLevelClientTestCase private static final String repositoryName = "test_repository"; + public void testSnapshotVerifyRepository() throws IOException { + RestHighLevelClient client = highLevelClient(); + createTestRepositories(); + + // tag::verify-repository-request + VerifyRepositoryRequest request = new VerifyRepositoryRequest(repositoryName); + // end::verify-repository-request + + // tag::verify-repository-request-masterTimeout + request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.masterNodeTimeout("1m"); // <2> + // end::verify-repository-request-masterTimeout + // tag::verify-repository-request-timeout + request.timeout(TimeValue.timeValueMinutes(1)); // <1> + request.timeout("1m"); // <2> + // end::verify-repository-request-timeout + + // tag::verify-repository-execute + VerifyRepositoryRestResponse response = client.snapshot().verifyRepository(request); + // end::verify-repository-execute + + // tag::verify-repository-response + List repositoryMetaDataResponse = response.nodes(); + // end::verify-repository-response + assertThat(1, equalTo(repositoryMetaDataResponse.size())); + assertThat("node-0", equalTo(repositoryMetaDataResponse.get(0).getName())); + } + + public void testSnapshotVerifyRepositoryAsync() throws InterruptedException { + RestHighLevelClient client = highLevelClient(); + { + VerifyRepositoryRequest request = new VerifyRepositoryRequest(repositoryName); + + // tag::verify-repository-execute-listener + ActionListener listener = + new ActionListener() { + @Override + public void onResponse(VerifyRepositoryRestResponse verifyRepositoryRestResponse) { + // <1> + } + + @Override + public void onFailure(Exception e) { + // <2> + } + }; + // end::verify-repository-execute-listener + + // Replace the empty listener by a blocking listener in test + final CountDownLatch latch = new CountDownLatch(1); + listener = new LatchedActionListener<>(listener, latch); + + // tag::verify-repository-execute-async + client.snapshot().verifyRepositoryAsync(request, listener); // <1> + // end::verify-repository-execute-async + + assertTrue(latch.await(30L, TimeUnit.SECONDS)); + } + } + public void testSnapshotCreateRepository() throws IOException { RestHighLevelClient client = highLevelClient(); diff --git a/docs/java-rest/high-level/snapshot/verify_repository.asciidoc b/docs/java-rest/high-level/snapshot/verify_repository.asciidoc new file mode 100644 index 0000000000000..a9ccf4edb3182 --- /dev/null +++ b/docs/java-rest/high-level/snapshot/verify_repository.asciidoc @@ -0,0 +1,81 @@ +[[java-rest-high-snapshot-verify-repository]] +=== Snapshot Verify Repository API + +The Snapshot Verify Repository API allows to verify a registered repository. + +[[java-rest-high-snapshot-verify-repository-request]] +==== Snapshot Verify Repository Request + +A `VerifyRepositoryRequest`: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[verify-repository-request] +-------------------------------------------------- + +==== Optional Arguments +The following arguments can optionally be provided: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[create-repository-request-timeout] +-------------------------------------------------- +<1> Timeout to wait for the all the nodes to acknowledge the settings were applied +as a `TimeValue` +<2> Timeout to wait for the all the nodes to acknowledge the settings were applied +as a `String` + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[verify-repository-request-masterTimeout] +-------------------------------------------------- +<1> Timeout to connect to the master node as a `TimeValue` +<2> Timeout to connect to the master node as a `String` + +[[java-rest-high-snapshot-verify-repository-sync]] +==== Synchronous Execution + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[verify-repository-execute] +-------------------------------------------------- + +[[java-rest-high-snapshot-verify-repository-async]] +==== Asynchronous Execution + +The asynchronous execution of a snapshot verify repository requires both the +`VerifyRepositoryRequest` instance and an `ActionListener` instance to be +passed to the asynchronous method: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[verify-repository-execute-async] +-------------------------------------------------- +<1> The `VerifyRepositoryRequest` to execute and the `ActionListener` +to use when the execution completes + +The asynchronous method does not block and returns immediately. Once it is +completed the `ActionListener` is called back using the `onResponse` method +if the execution successfully completed or using the `onFailure` method if +it failed. + +A typical listener for `VerifyRepositoryRestResponse` looks like: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[verify-repository-execute-listener] +-------------------------------------------------- +<1> Called when the execution is successfully completed. The response is +provided as an argument +<2> Called in case of a failure. The raised exception is provided as an argument + +[[java-rest-high-cluster-verify-repository-response]] +==== Snapshot Verify Repository Response + +The returned `VerifyRepositoryRestResponse` allows to retrieve information about the +executed operation as follows: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[verify-repository-response] +-------------------------------------------------- diff --git a/docs/java-rest/high-level/supported-apis.asciidoc b/docs/java-rest/high-level/supported-apis.asciidoc index b00047359a5d7..882af0268c10d 100644 --- a/docs/java-rest/high-level/supported-apis.asciidoc +++ b/docs/java-rest/high-level/supported-apis.asciidoc @@ -114,6 +114,10 @@ include::cluster/list_tasks.asciidoc[] The Java High Level REST Client supports the following Snapshot APIs: * <> +* <> +* <> + include::snapshot/get_repository.asciidoc[] include::snapshot/create_repository.asciidoc[] +include::snapshot/verify_repository.asciidoc[] diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/verify/VerifyRepositoryRestResponse.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/verify/VerifyRepositoryRestResponse.java new file mode 100644 index 0000000000000..87635ed11e8cc --- /dev/null +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/verify/VerifyRepositoryRestResponse.java @@ -0,0 +1,149 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.elasticsearch.action.admin.cluster.repositories.verify; + +import org.elasticsearch.action.ActionResponse; +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.io.stream.Streamable; +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.ToXContentObject; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +public class VerifyRepositoryRestResponse implements ToXContentObject { + + public static class NodeView implements ToXContentObject { + private static final ObjectParser.NamedObjectParser PARSER; + static { + ObjectParser parser = new ObjectParser<>("nodes"); + parser.declareString(NodeView::setName, new ParseField(Fields.NAME)); + PARSER = (XContentParser p, Void v, String name )-> parser.parse(p, new NodeView(name), null); + } + + final String nodeId; + String name; + + public NodeView(String nodeId) { this.nodeId = nodeId; } + + public NodeView(String nodeId, String name) { + this(nodeId); + this.name = name; + } + + void setName(String name) { this.name = name; } + + public String getName() { return name; } + + public String getNodeId() { return nodeId; } + + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(nodeId); + builder.field(Fields.NAME, name); + builder.endObject(); + return builder; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + NodeView other = (NodeView) obj; + return Objects.equals(nodeId, other.nodeId) && + Objects.equals(name, other.name); + } + + @Override + public int hashCode() { + return Objects.hash(nodeId, name); + } + } + + private static final ObjectParser PARSER = + new ObjectParser<>(VerifyRepositoryRestResponse.class.getName(), VerifyRepositoryRestResponse::new); + static { + PARSER.declareNamedObjects(VerifyRepositoryRestResponse::nodes, NodeView.PARSER, new ParseField("nodes")); + } + + private List nodes = new ArrayList<>(); + + VerifyRepositoryRestResponse() { + } + + public List nodes() { + return nodes; + } + + void nodes(List nodes) { + this.nodes = nodes; + } + + static final class Fields { + static final String NODES = "nodes"; + static final String NAME = "name"; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.startObject(Fields.NODES); + for (NodeView node : nodes) { + node.toXContent(builder, params); + } + builder.endObject(); + builder.endObject(); + return builder; + } + + @Override + public String toString() { + return Strings.toString(this); + } + + public static VerifyRepositoryRestResponse fromXContent(XContentParser parser) { + return PARSER.apply(parser, null); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + VerifyRepositoryRestResponse other = (VerifyRepositoryRestResponse) obj; + return nodes.equals(other.nodes); + } + + @Override + public int hashCode() { + return nodes.hashCode(); + } +} diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/repositories/verify/VerifyRepositoryRestResponseTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/repositories/verify/VerifyRepositoryRestResponseTests.java new file mode 100644 index 0000000000000..b9d6f323edeb2 --- /dev/null +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/repositories/verify/VerifyRepositoryRestResponseTests.java @@ -0,0 +1,48 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.elasticsearch.action.admin.cluster.repositories.verify; + +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.test.AbstractXContentTestCase; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class VerifyRepositoryRestResponseTests extends AbstractXContentTestCase { + + @Override + protected VerifyRepositoryRestResponse doParseInstance(XContentParser parser) throws IOException { + return VerifyRepositoryRestResponse.fromXContent(parser); + } + + @Override + protected boolean supportsUnknownFields() { + return false; + } + + @Override + protected VerifyRepositoryRestResponse createTestInstance() { + VerifyRepositoryRestResponse response = new VerifyRepositoryRestResponse(); + List nodes = new ArrayList<>(); + nodes.add(new VerifyRepositoryRestResponse.NodeView("node-id", "node-name")); + response.nodes(nodes); + return response; + } +}