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..c9526346e5bc1 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 @@ -30,6 +30,7 @@ import org.apache.lucene.util.BytesRef; import org.elasticsearch.action.DocWriteRequest; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksRequest; +import org.elasticsearch.action.admin.cluster.repositories.delete.DeleteRepositoryRequest; import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesRequest; import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequest; import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; @@ -711,6 +712,16 @@ static Request createRepository(PutRepositoryRequest putRepositoryRequest) throw return request; } + static Request deleteRepository(DeleteRepositoryRequest deleteRepositoryRequest) { + String endpoint = new EndpointBuilder().addPathPartAsIs("_snapshot").addPathPart(deleteRepositoryRequest.name()).build(); + Request request = new Request(HttpDelete.METHOD_NAME, endpoint); + + Params parameters = new Params(request); + parameters.withMasterTimeout(deleteRepositoryRequest.masterNodeTimeout()); + parameters.withTimeout(deleteRepositoryRequest.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..d969232f0d70f 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 @@ -21,6 +21,8 @@ import org.apache.http.Header; import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.admin.cluster.repositories.delete.DeleteRepositoryRequest; +import org.elasticsearch.action.admin.cluster.repositories.delete.DeleteRepositoryResponse; import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesRequest; import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesResponse; import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequest; @@ -90,4 +92,28 @@ public void createRepositoryAsync(PutRepositoryRequest putRepositoryRequest, restHighLevelClient.performRequestAsyncAndParseEntity(putRepositoryRequest, RequestConverters::createRepository, PutRepositoryResponse::fromXContent, listener, emptySet(), headers); } + + /** + * Deletes a snapshot repository. + *

+ * See Snapshot and Restore + * API on elastic.co + */ + public DeleteRepositoryResponse deleteRepository(DeleteRepositoryRequest deleteRepositoryRequest, Header... headers) + throws IOException { + return restHighLevelClient.performRequestAndParseEntity(deleteRepositoryRequest, RequestConverters::deleteRepository, + DeleteRepositoryResponse::fromXContent, emptySet(), headers); + } + + /** + * Asynchronously deletes a snapshot repository. + *

+ * See Snapshot and Restore + * API on elastic.co + */ + public void deleteRepositoryAsync(DeleteRepositoryRequest deleteRepositoryRequest, + ActionListener listener, Header... headers) { + restHighLevelClient.performRequestAsyncAndParseEntity(deleteRepositoryRequest, RequestConverters::deleteRepository, + DeleteRepositoryResponse::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..c5ee387d315cb 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 @@ -30,6 +30,7 @@ import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.DocWriteRequest; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksRequest; +import org.elasticsearch.action.admin.cluster.repositories.delete.DeleteRepositoryRequest; import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesRequest; import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequest; import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; @@ -1546,7 +1547,7 @@ public void testGetRepositories() { } public void testCreateRepository() throws IOException { - String repository = "repo"; + String repository = randomIndicesNames(1, 1)[0]; String endpoint = "/_snapshot/" + repository; Path repositoryLocation = PathUtils.get("."); PutRepositoryRequest putRepositoryRequest = new PutRepositoryRequest(repository); @@ -1555,10 +1556,10 @@ public void testCreateRepository() throws IOException { putRepositoryRequest.settings( Settings.builder() - .put(FsRepository.LOCATION_SETTING.getKey(), repositoryLocation) - .put(FsRepository.COMPRESS_SETTING.getKey(), randomBoolean()) - .put(FsRepository.CHUNK_SIZE_SETTING.getKey(), randomIntBetween(100, 1000), ByteSizeUnit.BYTES) - .build()); + .put(FsRepository.LOCATION_SETTING.getKey(), repositoryLocation) + .put(FsRepository.COMPRESS_SETTING.getKey(), randomBoolean()) + .put(FsRepository.CHUNK_SIZE_SETTING.getKey(), randomIntBetween(100, 1000), ByteSizeUnit.BYTES) + .build()); Request request = RequestConverters.createRepository(putRepositoryRequest); assertThat(endpoint, equalTo(request.getEndpoint())); @@ -1566,6 +1567,24 @@ public void testCreateRepository() throws IOException { assertToXContentBody(putRepositoryRequest, request.getEntity()); } + public void testDeleteRepository() { + Map expectedParams = new HashMap<>(); + String repository = randomIndicesNames(1, 1)[0]; + + StringBuilder endpoint = new StringBuilder("/_snapshot/" + repository); + + DeleteRepositoryRequest deleteRepositoryRequest = new DeleteRepositoryRequest(); + deleteRepositoryRequest.name(repository); + setRandomMasterTimeout(deleteRepositoryRequest, expectedParams); + setRandomTimeout(deleteRepositoryRequest::timeout, AcknowledgedRequest.DEFAULT_ACK_TIMEOUT, expectedParams); + + Request request = RequestConverters.deleteRepository(deleteRepositoryRequest); + assertThat(endpoint.toString(), equalTo(request.getEndpoint())); + assertThat(HttpDelete.METHOD_NAME, equalTo(request.getMethod())); + assertThat(expectedParams, equalTo(request.getParameters())); + assertNull(request.getEntity()); + } + 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..02e03bfec764e 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 @@ -19,7 +19,11 @@ package org.elasticsearch.client; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.action.admin.cluster.repositories.delete.DeleteRepositoryRequest; +import org.elasticsearch.action.admin.cluster.repositories.delete.DeleteRepositoryResponse; import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesRequest; import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesResponse; import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequest; @@ -29,6 +33,7 @@ import org.elasticsearch.rest.RestStatus; import java.io.IOException; +import java.util.Collections; import static org.hamcrest.Matchers.equalTo; @@ -40,7 +45,6 @@ private PutRepositoryResponse createTestRepository(String repository, String typ request.type(type); return execute(request, highLevelClient().snapshot()::createRepository, highLevelClient().snapshot()::createRepositoryAsync); - } public void testCreateRepository() throws IOException { @@ -48,7 +52,7 @@ public void testCreateRepository() throws IOException { assertTrue(response.isAcknowledged()); } - public void testModulesGetRepositoriesUsingParams() throws IOException { + public void testSnapshotGetRepositoriesUsingParams() throws IOException { String testRepository = "test"; assertTrue(createTestRepository(testRepository, FsRepository.TYPE, "{\"location\": \".\"}").isAcknowledged()); assertTrue(createTestRepository("other", FsRepository.TYPE, "{\"location\": \".\"}").isAcknowledged()); @@ -60,7 +64,7 @@ public void testModulesGetRepositoriesUsingParams() throws IOException { assertThat(1, equalTo(response.repositories().size())); } - public void testModulesGetDefaultRepositories() throws IOException { + public void testSnapshotGetDefaultRepositories() throws IOException { assertTrue(createTestRepository("other", FsRepository.TYPE, "{\"location\": \".\"}").isAcknowledged()); assertTrue(createTestRepository("test", FsRepository.TYPE, "{\"location\": \".\"}").isAcknowledged()); @@ -69,7 +73,7 @@ public void testModulesGetDefaultRepositories() throws IOException { assertThat(2, equalTo(response.repositories().size())); } - public void testModulesGetRepositoriesNonExistent() throws IOException { + public void testSnapshotGetRepositoriesNonExistent() { String repository = "doesnotexist"; GetRepositoriesRequest request = new GetRepositoriesRequest(new String[]{repository}); ElasticsearchException exception = expectThrows(ElasticsearchException.class, () -> execute(request, @@ -79,4 +83,23 @@ public void testModulesGetRepositoriesNonExistent() throws IOException { assertThat(exception.getMessage(), equalTo( "Elasticsearch exception [type=repository_missing_exception, reason=[" + repository + "] missing]")); } + + public void testSnapshotDeleteRepository() throws IOException { + String repository = "test"; + String repositorySettings = "{\"type\":\"fs\", \"settings\":{\"location\": \".\"}}"; + + highLevelClient().getLowLevelClient().performRequest("put", "_snapshot/" + repository, + Collections.emptyMap(), new StringEntity(repositorySettings, ContentType.APPLICATION_JSON)); + + GetRepositoriesRequest request = new GetRepositoriesRequest(); + GetRepositoriesResponse response = execute(request, highLevelClient().snapshot()::getRepositories, + highLevelClient().snapshot()::getRepositoriesAsync); + assertThat(1, equalTo(response.repositories().size())); + + DeleteRepositoryRequest deleteRequest = new DeleteRepositoryRequest(repository); + DeleteRepositoryResponse deleteResponse = execute(deleteRequest, highLevelClient().snapshot()::deleteRepository, + highLevelClient().snapshot()::deleteRepositoryAsync); + + assertTrue(deleteResponse.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..0a57fafe5be59 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 @@ -21,6 +21,8 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.LatchedActionListener; +import org.elasticsearch.action.admin.cluster.repositories.delete.DeleteRepositoryRequest; +import org.elasticsearch.action.admin.cluster.repositories.delete.DeleteRepositoryResponse; import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesRequest; import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesResponse; import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryRequest; @@ -235,6 +237,66 @@ public void onFailure(Exception e) { } } + public void testSnapshotDeleteRepository() throws IOException { + RestHighLevelClient client = highLevelClient(); + + createTestRepositories(); + + // tag::delete-repository-request + DeleteRepositoryRequest request = new DeleteRepositoryRequest(repositoryName); + // end::delete-repository-request + + // tag::delete-repository-request-masterTimeout + request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.masterNodeTimeout("1m"); // <2> + // end::delete-repository-request-masterTimeout + // tag::delete-repository-request-timeout + request.timeout(TimeValue.timeValueMinutes(1)); // <1> + request.timeout("1m"); // <2> + // end::delete-repository-request-timeout + + // tag::delete-repository-execute + DeleteRepositoryResponse response = client.snapshot().deleteRepository(request); + // end::delete-repository-execute + + // tag::delete-repository-response + boolean acknowledged = response.isAcknowledged(); // <1> + // end::delete-repository-response + assertTrue(acknowledged); + } + + public void testSnapshotDeleteRepositoryAsync() throws InterruptedException { + RestHighLevelClient client = highLevelClient(); + { + DeleteRepositoryRequest request = new DeleteRepositoryRequest(); + + // tag::delete-repository-execute-listener + ActionListener listener = + new ActionListener() { + @Override + public void onResponse(DeleteRepositoryResponse deleteRepositoryResponse) { + // <1> + } + + @Override + public void onFailure(Exception e) { + // <2> + } + }; + // end::delete-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::delete-repository-execute-async + client.snapshot().deleteRepositoryAsync(request, listener); // <1> + // end::delete-repository-execute-async + + assertTrue(latch.await(30L, TimeUnit.SECONDS)); + } + } + private void createTestRepositories() throws IOException { PutRepositoryRequest request = new PutRepositoryRequest(repositoryName); request.type(FsRepository.TYPE); diff --git a/docs/java-rest/high-level/snapshot/delete_repository.asciidoc b/docs/java-rest/high-level/snapshot/delete_repository.asciidoc new file mode 100644 index 0000000000000..e88535f2362a5 --- /dev/null +++ b/docs/java-rest/high-level/snapshot/delete_repository.asciidoc @@ -0,0 +1,82 @@ +[[java-rest-high-snapshot-delete-repository]] +=== Snapshot Delete Repository API + +The Snapshot Delete Repository API allows to delete a registered repository. + +[[java-rest-high-snapshot-delete-repository-request]] +==== Snapshot Delete Repository Request + +A `DeleteRepositoryRequest`: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[delete-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[delete-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-delete-repository-sync]] +==== Synchronous Execution + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[delete-repository-execute] +-------------------------------------------------- + +[[java-rest-high-snapshot-delete-repository-async]] +==== Asynchronous Execution + +The asynchronous execution of a snapshot delete repository requires both the +`DeleteRepositoryRequest` instance and an `ActionListener` instance to be +passed to the asynchronous method: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[delete-repository-execute-async] +-------------------------------------------------- +<1> The `DeleteRepositoryRequest` 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 `DeleteRepositoryResponse` looks like: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[delete-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-delete-repository-response]] +==== Snapshot Delete Repository Response + +The returned `DeleteRepositoryResponse` allows to retrieve information about the +executed operation as follows: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/SnapshotClientDocumentationIT.java[delete-repository-response] +-------------------------------------------------- +<1> Indicates the node has acknowledged the request diff --git a/docs/java-rest/high-level/supported-apis.asciidoc b/docs/java-rest/high-level/supported-apis.asciidoc index b00047359a5d7..d8ec67dade10b 100644 --- a/docs/java-rest/high-level/supported-apis.asciidoc +++ b/docs/java-rest/high-level/supported-apis.asciidoc @@ -114,6 +114,9 @@ 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/delete_repository.asciidoc[] diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/delete/DeleteRepositoryResponse.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/delete/DeleteRepositoryResponse.java index b83c8158c4f7a..43036a2a697ef 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/delete/DeleteRepositoryResponse.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/delete/DeleteRepositoryResponse.java @@ -22,6 +22,9 @@ import org.elasticsearch.action.support.master.AcknowledgedResponse; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.ToXContentObject; +import org.elasticsearch.common.xcontent.XContentParser; import java.io.IOException; @@ -30,6 +33,13 @@ */ public class DeleteRepositoryResponse extends AcknowledgedResponse { + private static final ConstructingObjectParser PARSER = + new ConstructingObjectParser<>("delete_repository", true, args -> new DeleteRepositoryResponse((boolean) args[0])); + + static { + declareAcknowledgedField(PARSER); + } + DeleteRepositoryResponse() { } @@ -49,4 +59,7 @@ public void writeTo(StreamOutput out) throws IOException { writeAcknowledged(out); } + public static DeleteRepositoryResponse fromXContent(XContentParser parser) { + return PARSER.apply(parser, null); + } } diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestDeleteRepositoryAction.java b/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestDeleteRepositoryAction.java index d2740827d1ebf..4b7bb9d8de0fa 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestDeleteRepositoryAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestDeleteRepositoryAction.java @@ -49,7 +49,6 @@ public String getName() { @Override public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException { DeleteRepositoryRequest deleteRepositoryRequest = deleteRepositoryRequest(request.param("repository")); - deleteRepositoryRequest.masterNodeTimeout(request.paramAsTime("master_timeout", deleteRepositoryRequest.masterNodeTimeout())); deleteRepositoryRequest.timeout(request.paramAsTime("timeout", deleteRepositoryRequest.timeout())); deleteRepositoryRequest.masterNodeTimeout(request.paramAsTime("master_timeout", deleteRepositoryRequest.masterNodeTimeout())); return channel -> client.admin().cluster().deleteRepository(deleteRepositoryRequest, new RestToXContentListener<>(channel)); diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/repositories/delete/DeleteRepositoryResponseTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/repositories/delete/DeleteRepositoryResponseTests.java new file mode 100644 index 0000000000000..fe97e778dadb8 --- /dev/null +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/repositories/delete/DeleteRepositoryResponseTests.java @@ -0,0 +1,40 @@ +/* + * 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.delete; + +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.test.AbstractStreamableXContentTestCase; + +public class DeleteRepositoryResponseTests extends AbstractStreamableXContentTestCase { + + @Override + protected DeleteRepositoryResponse doParseInstance(XContentParser parser) { + return DeleteRepositoryResponse.fromXContent(parser); + } + + @Override + protected DeleteRepositoryResponse createBlankInstance() { + return new DeleteRepositoryResponse(); + } + + @Override + protected DeleteRepositoryResponse createTestInstance() { + return new DeleteRepositoryResponse(randomBoolean()); + } +}