From 02fc16f10e00ad8bd997b47d16879cfe5b8da4ba Mon Sep 17 00:00:00 2001 From: olcbean <26058559+olcbean@users.noreply.github.com> Date: Thu, 15 Feb 2018 17:21:45 +0100 Subject: [PATCH] Add Cluster Put Settings API to the high level REST client (#28633) Relates to #27205 --- .../elasticsearch/client/ClusterClient.java | 66 +++++++ .../org/elasticsearch/client/Request.java | 12 ++ .../client/RestHighLevelClient.java | 10 + .../elasticsearch/client/ClusterClientIT.java | 108 +++++++++++ .../elasticsearch/client/IndicesClientIT.java | 1 - .../elasticsearch/client/RequestTests.java | 44 +++-- .../ClusterClientDocumentationIT.java | 180 ++++++++++++++++++ .../IndicesClientDocumentationIT.java | 6 +- .../high-level/cluster/put_settings.asciidoc | 129 +++++++++++++ .../high-level/indices/put_mapping.asciidoc | 2 +- .../high-level/supported-apis.asciidoc | 7 + .../ClusterUpdateSettingsRequest.java | 64 ++++++- .../ClusterUpdateSettingsResponse.java | 40 +++- .../admin/indices/get/GetIndexRequest.java | 10 +- .../RestClusterUpdateSettingsAction.java | 31 ++- .../ClusterUpdateSettingsRequestTests.java | 74 +++++++ .../ClusterUpdateSettingsResponseTests.java | 83 ++++++++ .../org/elasticsearch/test/ESTestCase.java | 1 + .../elasticsearch/test/XContentTestUtils.java | 2 +- 19 files changed, 821 insertions(+), 49 deletions(-) create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/ClusterClient.java create mode 100644 client/rest-high-level/src/test/java/org/elasticsearch/client/ClusterClientIT.java create mode 100644 client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/ClusterClientDocumentationIT.java create mode 100644 docs/java-rest/high-level/cluster/put_settings.asciidoc create mode 100644 server/src/test/java/org/elasticsearch/action/admin/cluster/settings/ClusterUpdateSettingsRequestTests.java create mode 100644 server/src/test/java/org/elasticsearch/action/admin/cluster/settings/ClusterUpdateSettingsResponseTests.java diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ClusterClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ClusterClient.java new file mode 100644 index 000000000000..177e33d72701 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ClusterClient.java @@ -0,0 +1,66 @@ +/* + * 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; + +import org.apache.http.Header; +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; +import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsResponse; + +import java.io.IOException; + +import static java.util.Collections.emptySet; + +/** + * A wrapper for the {@link RestHighLevelClient} that provides methods for accessing the Cluster API. + *

+ * See Cluster API on elastic.co + */ +public final class ClusterClient { + private final RestHighLevelClient restHighLevelClient; + + ClusterClient(RestHighLevelClient restHighLevelClient) { + this.restHighLevelClient = restHighLevelClient; + } + + /** + * Updates cluster wide specific settings using the Cluster Update Settings API + *

+ * See Cluster Update Settings + * API on elastic.co + */ + public ClusterUpdateSettingsResponse putSettings(ClusterUpdateSettingsRequest clusterUpdateSettingsRequest, Header... headers) + throws IOException { + return restHighLevelClient.performRequestAndParseEntity(clusterUpdateSettingsRequest, Request::clusterPutSettings, + ClusterUpdateSettingsResponse::fromXContent, emptySet(), headers); + } + + /** + * Asynchronously updates cluster wide specific settings using the Cluster Update Settings API + *

+ * See Cluster Update Settings + * API on elastic.co + */ + public void putSettingsAsync(ClusterUpdateSettingsRequest clusterUpdateSettingsRequest, + ActionListener listener, Header... headers) { + restHighLevelClient.performRequestAsyncAndParseEntity(clusterUpdateSettingsRequest, Request::clusterPutSettings, + ClusterUpdateSettingsResponse::fromXContent, listener, emptySet(), headers); + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/Request.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/Request.java index 4ec7315b73b5..f46248c2f5b8 100755 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/Request.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/Request.java @@ -29,6 +29,7 @@ import org.apache.http.entity.ByteArrayEntity; import org.apache.http.entity.ContentType; import org.apache.lucene.util.BytesRef; import org.elasticsearch.action.DocWriteRequest; +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; import org.elasticsearch.action.admin.indices.close.CloseIndexRequest; @@ -528,6 +529,17 @@ public final class Request { return new Request(HttpPut.METHOD_NAME, endpoint, params.getParams(), entity); } + static Request clusterPutSettings(ClusterUpdateSettingsRequest clusterUpdateSettingsRequest) throws IOException { + Params parameters = Params.builder(); + parameters.withFlatSettings(clusterUpdateSettingsRequest.flatSettings()); + parameters.withTimeout(clusterUpdateSettingsRequest.timeout()); + parameters.withMasterTimeout(clusterUpdateSettingsRequest.masterNodeTimeout()); + + String endpoint = buildEndpoint("_cluster/settings"); + HttpEntity entity = createEntity(clusterUpdateSettingsRequest, REQUEST_BODY_CONTENT_TYPE); + return new Request(HttpPut.METHOD_NAME, endpoint, parameters.getParams(), entity); + } + private static HttpEntity createEntity(ToXContent toXContent, XContentType xContentType) throws IOException { BytesRef source = XContentHelper.toXContent(toXContent, xContentType, false).toBytesRef(); return new ByteArrayEntity(source.bytes, source.offset, source.length, createContentType(xContentType)); diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java index 51c6f40c6e37..bf80aa772074 100755 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java @@ -186,6 +186,7 @@ public class RestHighLevelClient implements Closeable { private final CheckedConsumer doClose; private final IndicesClient indicesClient = new IndicesClient(this); + private final ClusterClient clusterClient = new ClusterClient(this); /** * Creates a {@link RestHighLevelClient} given the low level {@link RestClientBuilder} that allows to build the @@ -240,6 +241,15 @@ public class RestHighLevelClient implements Closeable { return indicesClient; } + /** + * Provides a {@link ClusterClient} which can be used to access the Cluster API. + * + * See Cluster API on elastic.co + */ + public final ClusterClient cluster() { + return clusterClient; + } + /** * Executes a bulk request using the Bulk API * diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/ClusterClientIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/ClusterClientIT.java new file mode 100644 index 000000000000..9314bb2e36ce --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/ClusterClientIT.java @@ -0,0 +1,108 @@ +/* + * 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; + +import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; +import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsResponse; +import org.elasticsearch.cluster.routing.allocation.decider.EnableAllocationDecider; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.unit.ByteSizeUnit; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.common.xcontent.support.XContentMapValues; +import org.elasticsearch.indices.recovery.RecoverySettings; +import org.elasticsearch.rest.RestStatus; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; + +public class ClusterClientIT extends ESRestHighLevelClientTestCase { + + public void testClusterPutSettings() throws IOException { + final String transientSettingKey = RecoverySettings.INDICES_RECOVERY_MAX_BYTES_PER_SEC_SETTING.getKey(); + final int transientSettingValue = 10; + + final String persistentSettingKey = EnableAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ENABLE_SETTING.getKey(); + final String persistentSettingValue = EnableAllocationDecider.Allocation.NONE.name(); + + Settings transientSettings = Settings.builder().put(transientSettingKey, transientSettingValue, ByteSizeUnit.BYTES).build(); + Map map = new HashMap<>(); + map.put(persistentSettingKey, persistentSettingValue); + + ClusterUpdateSettingsRequest setRequest = new ClusterUpdateSettingsRequest(); + setRequest.transientSettings(transientSettings); + setRequest.persistentSettings(map); + + ClusterUpdateSettingsResponse setResponse = execute(setRequest, highLevelClient().cluster()::putSettings, + highLevelClient().cluster()::putSettingsAsync); + + assertAcked(setResponse); + assertThat(setResponse.getTransientSettings().get(transientSettingKey), notNullValue()); + assertThat(setResponse.getTransientSettings().get(persistentSettingKey), nullValue()); + assertThat(setResponse.getTransientSettings().get(transientSettingKey), + equalTo(transientSettingValue + ByteSizeUnit.BYTES.getSuffix())); + assertThat(setResponse.getPersistentSettings().get(transientSettingKey), nullValue()); + assertThat(setResponse.getPersistentSettings().get(persistentSettingKey), notNullValue()); + assertThat(setResponse.getPersistentSettings().get(persistentSettingKey), equalTo(persistentSettingValue)); + + Map setMap = getAsMap("/_cluster/settings"); + String transientSetValue = (String) XContentMapValues.extractValue("transient." + transientSettingKey, setMap); + assertThat(transientSetValue, equalTo(transientSettingValue + ByteSizeUnit.BYTES.getSuffix())); + String persistentSetValue = (String) XContentMapValues.extractValue("persistent." + persistentSettingKey, setMap); + assertThat(persistentSetValue, equalTo(persistentSettingValue)); + + ClusterUpdateSettingsRequest resetRequest = new ClusterUpdateSettingsRequest(); + resetRequest.transientSettings(Settings.builder().putNull(transientSettingKey)); + resetRequest.persistentSettings("{\"" + persistentSettingKey + "\": null }", XContentType.JSON); + + ClusterUpdateSettingsResponse resetResponse = execute(resetRequest, highLevelClient().cluster()::putSettings, + highLevelClient().cluster()::putSettingsAsync); + + assertThat(resetResponse.getTransientSettings().get(transientSettingKey), equalTo(null)); + assertThat(resetResponse.getPersistentSettings().get(persistentSettingKey), equalTo(null)); + assertThat(resetResponse.getTransientSettings(), equalTo(Settings.EMPTY)); + assertThat(resetResponse.getPersistentSettings(), equalTo(Settings.EMPTY)); + + Map resetMap = getAsMap("/_cluster/settings"); + String transientResetValue = (String) XContentMapValues.extractValue("transient." + transientSettingKey, resetMap); + assertThat(transientResetValue, equalTo(null)); + String persistentResetValue = (String) XContentMapValues.extractValue("persistent." + persistentSettingKey, resetMap); + assertThat(persistentResetValue, equalTo(null)); + } + + public void testClusterUpdateSettingNonExistent() { + String setting = "no_idea_what_you_are_talking_about"; + int value = 10; + ClusterUpdateSettingsRequest clusterUpdateSettingsRequest = new ClusterUpdateSettingsRequest(); + clusterUpdateSettingsRequest.transientSettings(Settings.builder().put(setting, value).build()); + + ElasticsearchException exception = expectThrows(ElasticsearchException.class, () -> execute(clusterUpdateSettingsRequest, + highLevelClient().cluster()::putSettings, highLevelClient().cluster()::putSettingsAsync)); + assertThat(exception.status(), equalTo(RestStatus.BAD_REQUEST)); + assertThat(exception.getMessage(), equalTo( + "Elasticsearch exception [type=illegal_argument_exception, reason=transient setting [" + setting + "], not recognized]")); + } +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java index 5baef93c0dee..b8c9f4e76c25 100755 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java @@ -167,7 +167,6 @@ public class IndicesClientIT extends ESRestHighLevelClientTestCase { } } - @SuppressWarnings({"unchecked", "rawtypes"}) public void testPutMapping() throws IOException { { // Add mappings to index diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestTests.java index 3e4add16707f..400a47ca47a7 100755 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestTests.java @@ -30,6 +30,7 @@ import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.util.EntityUtils; import org.elasticsearch.action.DocWriteRequest; +import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest; import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions; import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest; @@ -272,7 +273,7 @@ public class RequestTests extends ESTestCase { Map expectedParams = new HashMap<>(); setRandomIndicesOptions(getIndexRequest::indicesOptions, getIndexRequest::indicesOptions, expectedParams); setRandomLocal(getIndexRequest, expectedParams); - setRandomFlatSettings(getIndexRequest, expectedParams); + setRandomFlatSettings(getIndexRequest::flatSettings, expectedParams); setRandomHumanReadable(getIndexRequest, expectedParams); setRandomIncludeDefaults(getIndexRequest, expectedParams); @@ -1115,14 +1116,10 @@ public class RequestTests extends ESTestCase { if (randomBoolean()) { randomAliases(createIndexRequest); } - if (randomBoolean()) { - setRandomWaitForActiveShards(createIndexRequest::waitForActiveShards, expectedParams); - } + setRandomWaitForActiveShards(createIndexRequest::waitForActiveShards, expectedParams); resizeRequest.setTargetIndex(createIndexRequest); } else { - if (randomBoolean()) { - setRandomWaitForActiveShards(resizeRequest::setWaitForActiveShards, expectedParams); - } + setRandomWaitForActiveShards(resizeRequest::setWaitForActiveShards, expectedParams); } Request request = function.apply(resizeRequest); @@ -1133,6 +1130,19 @@ public class RequestTests extends ESTestCase { assertEquals(expectedParams, request.getParameters()); assertToXContentBody(resizeRequest, request.getEntity()); } + + public void testClusterPutSettings() throws IOException { + ClusterUpdateSettingsRequest request = new ClusterUpdateSettingsRequest(); + Map expectedParams = new HashMap<>(); + setRandomFlatSettings(request::flatSettings, expectedParams); + setRandomMasterTimeout(request, expectedParams); + setRandomTimeout(request::timeout, AcknowledgedRequest.DEFAULT_ACK_TIMEOUT, expectedParams); + + Request expectedRequest = Request.clusterPutSettings(request); + assertEquals("/_cluster/settings", expectedRequest.getEndpoint()); + assertEquals(HttpPut.METHOD_NAME, expectedRequest.getMethod()); + assertEquals(expectedParams, expectedRequest.getParameters()); + } private static void assertToXContentBody(ToXContent expectedBody, HttpEntity actualEntity) throws IOException { BytesReference expectedBytes = XContentHelper.toXContent(expectedBody, REQUEST_BODY_CONTENT_TYPE, false); @@ -1289,16 +1299,6 @@ public class RequestTests extends ESTestCase { } } - private static void setRandomFlatSettings(GetIndexRequest request, Map expectedParams) { - if (randomBoolean()) { - boolean flatSettings = randomBoolean(); - request.flatSettings(flatSettings); - if (flatSettings) { - expectedParams.put("flat_settings", String.valueOf(flatSettings)); - } - } - } - private static void setRandomLocal(MasterNodeReadRequest request, Map expectedParams) { if (randomBoolean()) { boolean local = randomBoolean(); @@ -1319,6 +1319,16 @@ public class RequestTests extends ESTestCase { } } + private static void setRandomFlatSettings(Consumer setter, Map expectedParams) { + if (randomBoolean()) { + boolean flatSettings = randomBoolean(); + setter.accept(flatSettings); + if (flatSettings) { + expectedParams.put("flat_settings", String.valueOf(flatSettings)); + } + } + } + private static void setRandomMasterTimeout(MasterNodeRequest request, Map expectedParams) { if (randomBoolean()) { String masterTimeout = randomTimeValue(); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/ClusterClientDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/ClusterClientDocumentationIT.java new file mode 100644 index 000000000000..e9fc4ec01ba9 --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/ClusterClientDocumentationIT.java @@ -0,0 +1,180 @@ +/* + * 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.documentation; + +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.LatchedActionListener; +import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; +import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsResponse; +import org.elasticsearch.client.ESRestHighLevelClientTestCase; +import org.elasticsearch.client.RestHighLevelClient; +import org.elasticsearch.cluster.routing.allocation.decider.EnableAllocationDecider; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.unit.ByteSizeUnit; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.indices.recovery.RecoverySettings; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static org.hamcrest.Matchers.equalTo; + +/** + * This class is used to generate the Java Cluster API documentation. + * You need to wrap your code between two tags like: + * // tag::example[] + * // end::example[] + * + * Where example is your tag name. + * + * Then in the documentation, you can extract what is between tag and end tags with + * ["source","java",subs="attributes,callouts,macros"] + * -------------------------------------------------- + * include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[example] + * -------------------------------------------------- + */ +public class ClusterClientDocumentationIT extends ESRestHighLevelClientTestCase { + + public void testClusterPutSettings() throws IOException { + RestHighLevelClient client = highLevelClient(); + + // tag::put-settings-request + ClusterUpdateSettingsRequest request = new ClusterUpdateSettingsRequest(); + // end::put-settings-request + + // tag::put-settings-create-settings + String transientSettingKey = + RecoverySettings.INDICES_RECOVERY_MAX_BYTES_PER_SEC_SETTING.getKey(); + int transientSettingValue = 10; + Settings transientSettings = + Settings.builder() + .put(transientSettingKey, transientSettingValue, ByteSizeUnit.BYTES) + .build(); // <1> + + String persistentSettingKey = + EnableAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ENABLE_SETTING.getKey(); + String persistentSettingValue = + EnableAllocationDecider.Allocation.NONE.name(); + Settings persistentSettings = + Settings.builder() + .put(persistentSettingKey, persistentSettingValue) + .build(); // <2> + // end::put-settings-create-settings + + // tag::put-settings-request-cluster-settings + request.transientSettings(transientSettings); // <1> + request.persistentSettings(persistentSettings); // <2> + // end::put-settings-request-cluster-settings + + { + // tag::put-settings-settings-builder + Settings.Builder transientSettingsBuilder = + Settings.builder() + .put(transientSettingKey, transientSettingValue, ByteSizeUnit.BYTES); + request.transientSettings(transientSettingsBuilder); // <1> + // end::put-settings-settings-builder + } + { + // tag::put-settings-settings-map + Map map = new HashMap<>(); + map.put(transientSettingKey + , transientSettingValue + ByteSizeUnit.BYTES.getSuffix()); + request.transientSettings(map); // <1> + // end::put-settings-settings-map + } + { + // tag::put-settings-settings-source + request.transientSettings( + "{\"indices.recovery.max_bytes_per_sec\": \"10b\"}" + , XContentType.JSON); // <1> + // end::put-settings-settings-source + } + + // tag::put-settings-request-timeout + request.timeout(TimeValue.timeValueMinutes(2)); // <1> + request.timeout("2m"); // <2> + // end::put-settings-request-timeout + // tag::put-settings-request-masterTimeout + request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.masterNodeTimeout("1m"); // <2> + // end::put-settings-request-masterTimeout + + // tag::put-settings-request-flat-settings + request.flatSettings(true); // <1> + // end::put-settings-request-flat-settings + + // tag::put-settings-execute + ClusterUpdateSettingsResponse response = client.cluster().putSettings(request); + // end::put-settings-execute + + // tag::put-settings-response + boolean acknowledged = response.isAcknowledged(); // <1> + Settings transientSettingsResponse = response.getTransientSettings(); // <2> + Settings persistentSettingsResponse = response.getPersistentSettings(); // <3> + // end::put-settings-response + assertTrue(acknowledged); + assertThat(transientSettingsResponse.get(transientSettingKey), equalTo(transientSettingValue + ByteSizeUnit.BYTES.getSuffix())); + assertThat(persistentSettingsResponse.get(persistentSettingKey), equalTo(persistentSettingValue)); + + // tag::put-settings-request-reset-transient + request.transientSettings(Settings.builder().putNull(transientSettingKey).build()); // <1> + // tag::put-settings-request-reset-transient + request.persistentSettings(Settings.builder().putNull(persistentSettingKey)); + ClusterUpdateSettingsResponse resetResponse = client.cluster().putSettings(request); + + assertTrue(resetResponse.isAcknowledged()); + } + + public void testClusterUpdateSettingsAsync() throws Exception { + RestHighLevelClient client = highLevelClient(); + { + ClusterUpdateSettingsRequest request = new ClusterUpdateSettingsRequest(); + + // tag::put-settings-execute-listener + ActionListener listener = + new ActionListener() { + @Override + public void onResponse(ClusterUpdateSettingsResponse response) { + // <1> + } + + @Override + public void onFailure(Exception e) { + // <2> + } + }; + // end::put-settings-execute-listener + + // Replace the empty listener by a blocking listener in test + final CountDownLatch latch = new CountDownLatch(1); + listener = new LatchedActionListener<>(listener, latch); + + // tag::put-settings-execute-async + client.cluster().putSettingsAsync(request, listener); // <1> + // end::put-settings-execute-async + + assertTrue(latch.await(30L, TimeUnit.SECONDS)); + } + } +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java index 0462454f2982..e30018abd4ca 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java @@ -70,7 +70,7 @@ import java.util.concurrent.TimeUnit; * Then in the documentation, you can extract what is between tag and end tags with * ["source","java",subs="attributes,callouts,macros"] * -------------------------------------------------- - * include-tagged::{doc-tests}/CRUDDocumentationIT.java[example] + * include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[example] * -------------------------------------------------- */ public class IndicesClientDocumentationIT extends ESRestHighLevelClientTestCase { @@ -105,7 +105,7 @@ public class IndicesClientDocumentationIT extends ESRestHighLevelClientTestCase } } - public void testIndicesExistAsync() throws IOException { + public void testIndicesExistAsync() throws Exception { RestHighLevelClient client = highLevelClient(); { @@ -138,6 +138,8 @@ public class IndicesClientDocumentationIT extends ESRestHighLevelClientTestCase // tag::indices-exists-async client.indices().existsAsync(request, listener); // <1> // end::indices-exists-async + + assertTrue(latch.await(30L, TimeUnit.SECONDS)); } } public void testDeleteIndex() throws IOException { diff --git a/docs/java-rest/high-level/cluster/put_settings.asciidoc b/docs/java-rest/high-level/cluster/put_settings.asciidoc new file mode 100644 index 000000000000..2d9f55c1e941 --- /dev/null +++ b/docs/java-rest/high-level/cluster/put_settings.asciidoc @@ -0,0 +1,129 @@ +[[java-rest-high-cluster-put-settings]] +=== Cluster Update Settings API + +The Cluster Update Settings API allows to update cluster wide settings. + +[[java-rest-high-cluster-put-settings-request]] +==== Cluster Update Settings Request + +A `ClusterUpdateSettingsRequest`: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[put-settings-request] +-------------------------------------------------- + +==== Cluster Settings +At least one setting to be updated must be provided: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[put-settings-request-cluster-settings] +-------------------------------------------------- +<1> Sets the transient settings to be applied +<2> Sets the persistent setting to be applied + +==== Providing the Settings +The settings to be applied can be provided in different ways: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[put-settings-create-settings] +-------------------------------------------------- +<1> Creates a transient setting as `Settings` +<2> Creates a persistent setting as `Settings` + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[put-settings-settings-builder] +-------------------------------------------------- +<1> Settings provided as `Settings.Builder` + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[put-settings-settings-source] +-------------------------------------------------- +<1> Settings provided as `String` + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[put-settings-settings-map] +-------------------------------------------------- +<1> Settings provided as a `Map` + +==== Optional Arguments +The following arguments can optionally be provided: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[put-settings-request-flat-settings] +-------------------------------------------------- +<1> Wether the updated settings returned in the `ClusterUpdateSettings` should +be in a flat format + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[put-settings-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}/ClusterClientDocumentationIT.java[put-settings-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-cluster-put-settings-sync]] +==== Synchronous Execution + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[put-settings-execute] +-------------------------------------------------- + +[[java-rest-high-cluster-put-settings-async]] +==== Asynchronous Execution + +The asynchronous execution of a cluster update settings requires both the +`ClusterUpdateSettingsRequest` instance and an `ActionListener` instance to be +passed to the asynchronous method: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[put-settings-execute-async] +-------------------------------------------------- +<1> The `ClusterUpdateSettingsRequest` 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 `ClusterUpdateSettingsResponse` looks like: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[put-settings-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-put-settings-response]] +==== Cluster Update Settings Response + +The returned `ClusterUpdateSettings` allows to retrieve information about the +executed operation as follows: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[put-settings-response] +-------------------------------------------------- +<1> Indicates whether all of the nodes have acknowledged the request +<2> Indicates which transient settings have been applied +<3> Indicates which persistent settings have been applied \ No newline at end of file diff --git a/docs/java-rest/high-level/indices/put_mapping.asciidoc b/docs/java-rest/high-level/indices/put_mapping.asciidoc index 1f7d8c1d5e82..5fa985d02c16 100644 --- a/docs/java-rest/high-level/indices/put_mapping.asciidoc +++ b/docs/java-rest/high-level/indices/put_mapping.asciidoc @@ -20,7 +20,7 @@ A description of the fields to create on the mapping; if not defined, the mappin -------------------------------------------------- include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-mapping-request-source] -------------------------------------------------- -<1> The mapping source provided as a `String` +<1> Mapping source provided as a `String` ==== Providing the mapping source The mapping source can be provided in different ways in addition to diff --git a/docs/java-rest/high-level/supported-apis.asciidoc b/docs/java-rest/high-level/supported-apis.asciidoc index 14e4351eb29b..295de7689f7c 100644 --- a/docs/java-rest/high-level/supported-apis.asciidoc +++ b/docs/java-rest/high-level/supported-apis.asciidoc @@ -71,3 +71,10 @@ include::indices/put_mapping.asciidoc[] include::indices/update_aliases.asciidoc[] include::indices/exists_alias.asciidoc[] +== Cluster APIs + +The Java High Level REST Client supports the following Cluster APIs: + +* <> + +include::cluster/put_settings.asciidoc[] diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/settings/ClusterUpdateSettingsRequest.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/settings/ClusterUpdateSettingsRequest.java index 2a8ed8bf11f4..f282cedff360 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/settings/ClusterUpdateSettingsRequest.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/settings/ClusterUpdateSettingsRequest.java @@ -22,26 +22,42 @@ package org.elasticsearch.action.admin.cluster.settings; import org.elasticsearch.ElasticsearchGenerationException; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.support.master.AcknowledgedRequest; +import org.elasticsearch.common.ParseField; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; import java.io.IOException; import java.util.Map; import static org.elasticsearch.action.ValidateActions.addValidationError; -import static org.elasticsearch.common.settings.Settings.Builder.EMPTY_SETTINGS; import static org.elasticsearch.common.settings.Settings.readSettingsFromStream; import static org.elasticsearch.common.settings.Settings.writeSettingsToStream; +import static org.elasticsearch.common.settings.Settings.Builder.EMPTY_SETTINGS; /** * Request for an update cluster settings action */ -public class ClusterUpdateSettingsRequest extends AcknowledgedRequest { +public class ClusterUpdateSettingsRequest extends AcknowledgedRequest implements ToXContentObject { + private static final ParseField PERSISTENT = new ParseField("persistent"); + private static final ParseField TRANSIENT = new ParseField("transient"); + + private static final ObjectParser PARSER = new ObjectParser<>("cluster_update_settings_request", + false, ClusterUpdateSettingsRequest::new); + + static { + PARSER.declareObject((r, p) -> r.persistentSettings = p, (p, c) -> Settings.fromXContent(p), PERSISTENT); + PARSER.declareObject((r, t) -> r.transientSettings = t, (p, c) -> Settings.fromXContent(p), TRANSIENT); + } + + private boolean flatSettings = false; private Settings transientSettings = EMPTY_SETTINGS; private Settings persistentSettings = EMPTY_SETTINGS; @@ -57,6 +73,29 @@ public class ClusterUpdateSettingsRequest extends AcknowledgedRequesttrue if settings need to be returned in flat format; false otherwise. + */ + public boolean flatSettings() { + return flatSettings; + } + public Settings transientSettings() { return transientSettings; } @@ -92,7 +131,7 @@ public class ClusterUpdateSettingsRequest extends AcknowledgedRequest PARSER = new ConstructingObjectParser<>( + "cluster_update_settings_response", true, a -> new ClusterUpdateSettingsResponse((boolean) a[0])); + static { + declareAcknowledgedField(PARSER); + PARSER.declareObject((r, p) -> r.persistentSettings = p, (p, c) -> Settings.fromXContent(p), PERSISTENT); + PARSER.declareObject((r, t) -> r.transientSettings = t, (p, c) -> Settings.fromXContent(p), TRANSIENT); + } Settings transientSettings; Settings persistentSettings; @@ -39,6 +55,10 @@ public class ClusterUpdateSettingsResponse extends AcknowledgedResponse { this.transientSettings = Settings.EMPTY; } + ClusterUpdateSettingsResponse(boolean acknowledged) { + super(acknowledged); + } + ClusterUpdateSettingsResponse(boolean acknowledged, Settings transientSettings, Settings persistentSettings) { super(acknowledged); this.persistentSettings = persistentSettings; @@ -68,4 +88,22 @@ public class ClusterUpdateSettingsResponse extends AcknowledgedResponse { Settings.writeSettingsToStream(persistentSettings, out); writeAcknowledged(out); } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + addAcknowledgedField(builder); + builder.startObject(PERSISTENT.getPreferredName()); + persistentSettings.toXContent(builder, params); + builder.endObject(); + builder.startObject(TRANSIENT.getPreferredName()); + transientSettings.toXContent(builder, params); + builder.endObject(); + builder.endObject(); + return builder; + } + + public static ClusterUpdateSettingsResponse fromXContent(XContentParser parser) throws IOException { + return PARSER.apply(parser, null); + } } diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/get/GetIndexRequest.java b/server/src/main/java/org/elasticsearch/action/admin/indices/get/GetIndexRequest.java index 477656d96cb5..2a70aa836454 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/get/GetIndexRequest.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/get/GetIndexRequest.java @@ -26,8 +26,6 @@ import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.util.ArrayUtils; import java.io.IOException; -import java.util.Arrays; -import java.util.List; /** * A request to delete an index. Best created with {@link org.elasticsearch.client.Requests#deleteIndexRequest(String)}. @@ -122,6 +120,8 @@ public class GetIndexRequest extends ClusterInfoRequest { /** * Sets the value of "flat_settings". + * Used only by the high-level REST client. + * * @param flatSettings value of "flat_settings" flag to be set * @return this request */ @@ -132,6 +132,8 @@ public class GetIndexRequest extends ClusterInfoRequest { /** * Return settings in flat format. + * Used only by the high-level REST client. + * * @return true if settings need to be returned in flat format; false otherwise. */ public boolean flatSettings() { @@ -140,6 +142,8 @@ public class GetIndexRequest extends ClusterInfoRequest { /** * Sets the value of "include_defaults". + * Used only by the high-level REST client. + * * @param includeDefaults value of "include_defaults" to be set. * @return this request */ @@ -150,6 +154,8 @@ public class GetIndexRequest extends ClusterInfoRequest { /** * Whether to return all default settings for each of the indices. + * Used only by the high-level REST client. + * * @return true if defaults settings for each of the indices need to returned; * false otherwise. */ diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestClusterUpdateSettingsAction.java b/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestClusterUpdateSettingsAction.java index 88e8ada55976..2901cdd2d9ba 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestClusterUpdateSettingsAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestClusterUpdateSettingsAction.java @@ -20,22 +20,25 @@ package org.elasticsearch.rest.action.admin.cluster; import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; -import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsResponse; import org.elasticsearch.client.Requests; import org.elasticsearch.client.node.NodeClient; +import org.elasticsearch.common.ParseField; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.rest.BaseRestHandler; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestRequest; -import org.elasticsearch.rest.action.AcknowledgedRestListener; +import org.elasticsearch.rest.action.RestToXContentListener; import java.io.IOException; import java.util.Map; import java.util.Set; public class RestClusterUpdateSettingsAction extends BaseRestHandler { + + private static final String PERSISTENT = "persistent"; + private static final String TRANSIENT = "transient"; + public RestClusterUpdateSettingsAction(Settings settings, RestController controller) { super(settings); controller.registerHandler(RestRequest.Method.PUT, "/_cluster/settings", this); @@ -56,26 +59,14 @@ public class RestClusterUpdateSettingsAction extends BaseRestHandler { try (XContentParser parser = request.contentParser()) { source = parser.map(); } - if (source.containsKey("transient")) { - clusterUpdateSettingsRequest.transientSettings((Map) source.get("transient")); + if (source.containsKey(TRANSIENT)) { + clusterUpdateSettingsRequest.transientSettings((Map) source.get(TRANSIENT)); } - if (source.containsKey("persistent")) { - clusterUpdateSettingsRequest.persistentSettings((Map) source.get("persistent")); + if (source.containsKey(PERSISTENT)) { + clusterUpdateSettingsRequest.persistentSettings((Map) source.get(PERSISTENT)); } - return channel -> client.admin().cluster().updateSettings(clusterUpdateSettingsRequest, - new AcknowledgedRestListener(channel) { - @Override - protected void addCustomFields(XContentBuilder builder, ClusterUpdateSettingsResponse response) throws IOException { - builder.startObject("persistent"); - response.getPersistentSettings().toXContent(builder, request); - builder.endObject(); - - builder.startObject("transient"); - response.getTransientSettings().toXContent(builder, request); - builder.endObject(); - } - }); + return channel -> client.admin().cluster().updateSettings(clusterUpdateSettingsRequest, new RestToXContentListener<>(channel)); } @Override diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/settings/ClusterUpdateSettingsRequestTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/settings/ClusterUpdateSettingsRequestTests.java new file mode 100644 index 000000000000..fdca03ebcda4 --- /dev/null +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/settings/ClusterUpdateSettingsRequestTests.java @@ -0,0 +1,74 @@ +/* + * 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.settings; + +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.XContentTestUtils; + +import java.io.IOException; +import java.util.Collections; + +import static org.hamcrest.CoreMatchers.equalTo; + +public class ClusterUpdateSettingsRequestTests extends ESTestCase { + + public void testFromXContent() throws IOException { + doFromXContentTestWithRandomFields(false); + } + + public void testFromXContentWithRandomFields() throws IOException { + doFromXContentTestWithRandomFields(true); + } + + private void doFromXContentTestWithRandomFields(boolean addRandomFields) throws IOException { + final ClusterUpdateSettingsRequest request = createTestItem(); + boolean humanReadable = randomBoolean(); + final XContentType xContentType = XContentType.JSON; + BytesReference originalBytes = toShuffledXContent(request, xContentType, ToXContent.EMPTY_PARAMS, humanReadable); + + if (addRandomFields) { + String unsupportedField = "unsupported_field"; + BytesReference mutated = XContentTestUtils.insertIntoXContent(xContentType.xContent(), originalBytes, + Collections.singletonList(""), () -> unsupportedField, () -> randomAlphaOfLengthBetween(3, 10)).bytes(); + IllegalArgumentException iae = expectThrows(IllegalArgumentException.class, + () -> ClusterUpdateSettingsRequest.fromXContent(createParser(xContentType.xContent(), mutated))); + assertThat(iae.getMessage(), + equalTo("[cluster_update_settings_request] unknown field [" + unsupportedField + "], parser not found")); + } else { + XContentParser parser = createParser(xContentType.xContent(), originalBytes); + ClusterUpdateSettingsRequest parsedRequest = ClusterUpdateSettingsRequest.fromXContent(parser); + + assertNull(parser.nextToken()); + assertThat(parsedRequest.transientSettings(), equalTo(request.transientSettings())); + assertThat(parsedRequest.persistentSettings(), equalTo(request.persistentSettings())); + } + } + + private static ClusterUpdateSettingsRequest createTestItem() { + ClusterUpdateSettingsRequest request = new ClusterUpdateSettingsRequest(); + request.persistentSettings(ClusterUpdateSettingsResponseTests.randomClusterSettings(0, 2)); + request.transientSettings(ClusterUpdateSettingsResponseTests.randomClusterSettings(0, 2)); + return request; + } +} diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/settings/ClusterUpdateSettingsResponseTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/settings/ClusterUpdateSettingsResponseTests.java new file mode 100644 index 000000000000..77ccae5a96d3 --- /dev/null +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/settings/ClusterUpdateSettingsResponseTests.java @@ -0,0 +1,83 @@ +/* + * 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.settings; + +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.settings.ClusterSettings; +import org.elasticsearch.common.settings.Setting; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.settings.Settings.Builder; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.test.ESTestCase; + +import java.io.IOException; + +import static org.elasticsearch.test.XContentTestUtils.insertRandomFields; +import static org.hamcrest.CoreMatchers.equalTo; + +public class ClusterUpdateSettingsResponseTests extends ESTestCase { + + public void testFromXContent() throws IOException { + doFromXContentTestWithRandomFields(false); + } + + public void testFromXContentWithRandomFields() throws IOException { + doFromXContentTestWithRandomFields(true); + } + + private void doFromXContentTestWithRandomFields(boolean addRandomFields) throws IOException { + final ClusterUpdateSettingsResponse response = createTestItem(); + boolean humanReadable = randomBoolean(); + final XContentType xContentType = XContentType.JSON; + + BytesReference originalBytes = toShuffledXContent(response, xContentType, ToXContent.EMPTY_PARAMS, humanReadable); + BytesReference mutated; + if (addRandomFields) { + mutated = insertRandomFields(xContentType, originalBytes, p -> p.startsWith("transient") || p.startsWith("persistent"), + random()); + } else { + mutated = originalBytes; + } + + XContentParser parser = createParser(xContentType.xContent(), mutated); + ClusterUpdateSettingsResponse parsedResponse = ClusterUpdateSettingsResponse.fromXContent(parser); + + assertNull(parser.nextToken()); + assertThat(parsedResponse.isAcknowledged(), equalTo(response.isAcknowledged())); + assertThat(response.transientSettings, equalTo(response.transientSettings)); + assertThat(response.persistentSettings, equalTo(response.persistentSettings)); + } + + public static Settings randomClusterSettings(int min, int max) { + int num = randomIntBetween(min, max); + Builder builder = Settings.builder(); + for (int i = 0; i < num; i++) { + Setting setting = randomFrom(ClusterSettings.BUILT_IN_CLUSTER_SETTINGS); + builder.put(setting.getKey(), randomAlphaOfLengthBetween(2, 10)); + } + return builder.build(); + } + + private static ClusterUpdateSettingsResponse createTestItem() { + return new ClusterUpdateSettingsResponse(randomBoolean(), randomClusterSettings(0, 2), randomClusterSettings(0, 2)); + } +} diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java index aaed939c310c..ba8e67467481 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java @@ -29,6 +29,7 @@ import com.carrotsearch.randomizedtesting.generators.RandomNumbers; import com.carrotsearch.randomizedtesting.generators.RandomPicks; import com.carrotsearch.randomizedtesting.generators.RandomStrings; import com.carrotsearch.randomizedtesting.rules.TestRuleAdapter; + import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; diff --git a/test/framework/src/main/java/org/elasticsearch/test/XContentTestUtils.java b/test/framework/src/main/java/org/elasticsearch/test/XContentTestUtils.java index 063f05544431..cd556a9115ad 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/XContentTestUtils.java +++ b/test/framework/src/main/java/org/elasticsearch/test/XContentTestUtils.java @@ -286,7 +286,7 @@ public final class XContentTestUtils { * {@link ObjectPath}. * The key/value arguments can suppliers that either return fixed or random values. */ - static XContentBuilder insertIntoXContent(XContent xContent, BytesReference original, List paths, Supplier key, + public static XContentBuilder insertIntoXContent(XContent xContent, BytesReference original, List paths, Supplier key, Supplier value) throws IOException { ObjectPath object = ObjectPath.createFromXContent(xContent, original); for (String path : paths) {