diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/IndexLifecycleClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/IndexLifecycleClient.java index 8528a9fbe9f07..67c0b43b91ee8 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/IndexLifecycleClient.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/IndexLifecycleClient.java @@ -22,6 +22,8 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.support.master.AcknowledgedResponse; import org.elasticsearch.client.indexlifecycle.DeleteLifecyclePolicyRequest; +import org.elasticsearch.client.indexlifecycle.LifecycleManagementStatusRequest; +import org.elasticsearch.client.indexlifecycle.LifecycleManagementStatusResponse; import org.elasticsearch.client.indexlifecycle.PutLifecyclePolicyRequest; import org.elasticsearch.protocol.xpack.indexlifecycle.ExplainLifecycleRequest; import org.elasticsearch.protocol.xpack.indexlifecycle.ExplainLifecycleResponse; @@ -169,6 +171,35 @@ public AcknowledgedResponse stopILM(StopILMRequest request, RequestOptions optio AcknowledgedResponse::fromXContent, emptySet()); } + /** + * Get the status of index lifecycle management + * See + * the docs for more. + * + * @param request the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + */ + public LifecycleManagementStatusResponse lifecycleManagementStatus(LifecycleManagementStatusRequest request, RequestOptions options) + throws IOException { + return restHighLevelClient.performRequestAndParseEntity(request, RequestConverters::lifecycleManagementStatus, options, + LifecycleManagementStatusResponse::fromXContent, emptySet()); + } + + /** + * Asynchronously get the status of index lifecycle management + * See + * the docs for more. + * + * @param request the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @param listener the listener to be notified upon request completion + */ + public void lifecycleManagementStatusAsync(LifecycleManagementStatusRequest request, RequestOptions options, + ActionListener listener) { + restHighLevelClient.performRequestAsyncAndParseEntity(request, RequestConverters::lifecycleManagementStatus, options, + LifecycleManagementStatusResponse::fromXContent, listener, emptySet()); + } + /** * Asynchronously stop the Index Lifecycle Management feature. * See 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 31832c8d9eae8..e9ab9819e3e71 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 @@ -88,6 +88,7 @@ import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.action.support.WriteRequest; import org.elasticsearch.action.update.UpdateRequest; +import org.elasticsearch.client.indexlifecycle.LifecycleManagementStatusRequest; import org.elasticsearch.client.indexlifecycle.PutLifecyclePolicyRequest; import org.elasticsearch.client.indexlifecycle.DeleteLifecyclePolicyRequest; import org.elasticsearch.client.security.RefreshPolicy; @@ -1285,6 +1286,18 @@ static Request stopILM(StopILMRequest stopILMRequest) { return request; } + static Request lifecycleManagementStatus(LifecycleManagementStatusRequest lifecycleManagementStatusRequest){ + Request request = new Request(HttpGet.METHOD_NAME, + new EndpointBuilder() + .addPathPartAsIs("_ilm") + .addPathPartAsIs("status") + .build()); + Params params = new Params(request); + params.withMasterTimeout(lifecycleManagementStatusRequest.masterNodeTimeout()); + params.withTimeout(lifecycleManagementStatusRequest.timeout()); + return request; + } + static Request explainLifecycle(ExplainLifecycleRequest explainLifecycleRequest) { String[] indices = explainLifecycleRequest.indices() == null ? Strings.EMPTY_ARRAY : explainLifecycleRequest.indices(); Request request = new Request(HttpGet.METHOD_NAME, diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/TimedRequest.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/TimedRequest.java index 40388eae288e6..ee0eb8a46b920 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/TimedRequest.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/TimedRequest.java @@ -26,7 +26,7 @@ * Please note, any requests that use a ackTimeout should set timeout as they * represent the same backing field on the server. */ -public class TimedRequest implements Validatable { +public abstract class TimedRequest implements Validatable { public static final TimeValue DEFAULT_TIMEOUT = TimeValue.timeValueSeconds(30); public static final TimeValue DEFAULT_MASTER_TIMEOUT = TimeValue.timeValueSeconds(30); @@ -36,7 +36,6 @@ public class TimedRequest implements Validatable { public void setTimeout(TimeValue timeout) { this.timeout = timeout; - } public void setMasterTimeout(TimeValue masterTimeout) { diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/indexlifecycle/LifecycleManagementStatusRequest.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/indexlifecycle/LifecycleManagementStatusRequest.java new file mode 100644 index 0000000000000..5db3d2d8c4e11 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/indexlifecycle/LifecycleManagementStatusRequest.java @@ -0,0 +1,28 @@ +/* + * 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.indexlifecycle; + +import org.elasticsearch.client.TimedRequest; + +/** + * A {@link TimedRequest} to get the current status of index lifecycle management. + */ +public class LifecycleManagementStatusRequest extends TimedRequest { +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/indexlifecycle/LifecycleManagementStatusResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/indexlifecycle/LifecycleManagementStatusResponse.java new file mode 100644 index 0000000000000..c1586d7e1c738 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/indexlifecycle/LifecycleManagementStatusResponse.java @@ -0,0 +1,68 @@ +/* + * 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.indexlifecycle; + +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.XContentParser; + +import java.util.Objects; + +/** + * The current status of index lifecycle management. See {@link OperationMode} for available statuses. + */ +public class LifecycleManagementStatusResponse { + + private final OperationMode operationMode; + private static final String OPERATION_MODE = "operation_mode"; + @SuppressWarnings("unchecked") + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + OPERATION_MODE, a -> new LifecycleManagementStatusResponse((String) a[0])); + + static { + PARSER.declareString(ConstructingObjectParser.constructorArg(), new ParseField(OPERATION_MODE)); + } + + //package private for testing + LifecycleManagementStatusResponse(String operationMode) { + this.operationMode = OperationMode.fromString(operationMode); + } + + public OperationMode getOperationMode() { + return operationMode; + } + + public static LifecycleManagementStatusResponse fromXContent(XContentParser parser) { + return PARSER.apply(parser, null); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + LifecycleManagementStatusResponse that = (LifecycleManagementStatusResponse) o; + return operationMode == that.operationMode; + } + + @Override + public int hashCode() { + return Objects.hash(operationMode); + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/indexlifecycle/OperationMode.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/indexlifecycle/OperationMode.java index a0894ccc474fb..81634e5824ec8 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/indexlifecycle/OperationMode.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/indexlifecycle/OperationMode.java @@ -20,6 +20,9 @@ import org.elasticsearch.action.admin.indices.shrink.ShrinkAction; +import java.util.EnumSet; +import java.util.Locale; + /** * Enum representing the different modes that Index Lifecycle Service can operate in. */ @@ -56,4 +59,10 @@ public boolean isValidChange(OperationMode nextMode) { }; public abstract boolean isValidChange(OperationMode nextMode); + + static OperationMode fromString(String string) { + return EnumSet.allOf(OperationMode.class).stream() + .filter(e -> string.equalsIgnoreCase(e.name())).findFirst() + .orElseThrow(() -> new IllegalArgumentException(String.format(Locale.ROOT, "%s is not a valid operation_mode", string))); + } } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/IndexLifecycleIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/IndexLifecycleIT.java index 9979c21e29d1d..62eb769414e42 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/IndexLifecycleIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/IndexLifecycleIT.java @@ -19,21 +19,23 @@ package org.elasticsearch.client; -import org.apache.http.util.EntityUtils; import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest; import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse; import org.elasticsearch.action.support.master.AcknowledgedResponse; import org.elasticsearch.client.indexlifecycle.AllocateAction; import org.elasticsearch.client.indexlifecycle.DeleteAction; +import org.elasticsearch.client.indexlifecycle.DeleteLifecyclePolicyRequest; import org.elasticsearch.client.indexlifecycle.ForceMergeAction; import org.elasticsearch.client.indexlifecycle.LifecycleAction; +import org.elasticsearch.client.indexlifecycle.LifecycleManagementStatusRequest; +import org.elasticsearch.client.indexlifecycle.LifecycleManagementStatusResponse; import org.elasticsearch.client.indexlifecycle.LifecyclePolicy; +import org.elasticsearch.client.indexlifecycle.OperationMode; import org.elasticsearch.client.indexlifecycle.Phase; import org.elasticsearch.client.indexlifecycle.PutLifecyclePolicyRequest; import org.elasticsearch.client.indexlifecycle.RolloverAction; import org.elasticsearch.client.indexlifecycle.ShrinkAction; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.client.indexlifecycle.DeleteLifecyclePolicyRequest; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.protocol.xpack.indexlifecycle.ExplainLifecycleRequest; import org.elasticsearch.protocol.xpack.indexlifecycle.ExplainLifecycleResponse; @@ -88,35 +90,33 @@ public void testStartStopILM() throws Exception { createIndex("baz", Settings.builder().put("index.lifecycle.name", "eggplant").build()); createIndex("squash", Settings.EMPTY); - // TODO: NORELEASE convert this to using the high level client once - // there are APIs for it - Request statusReq = new Request("GET", "/_ilm/status"); - Response statusResponse = client().performRequest(statusReq); - String statusResponseString = EntityUtils.toString(statusResponse.getEntity()); - assertEquals("{\"operation_mode\":\"RUNNING\"}", statusResponseString); + LifecycleManagementStatusRequest statusRequest = new LifecycleManagementStatusRequest(); + LifecycleManagementStatusResponse statusResponse = execute( + statusRequest, + highLevelClient().indexLifecycle()::lifecycleManagementStatus, + highLevelClient().indexLifecycle()::lifecycleManagementStatusAsync); + assertEquals(statusResponse.getOperationMode(), OperationMode.RUNNING); StopILMRequest stopReq = new StopILMRequest(); AcknowledgedResponse stopResponse = execute(stopReq, highLevelClient().indexLifecycle()::stopILM, highLevelClient().indexLifecycle()::stopILMAsync); assertTrue(stopResponse.isAcknowledged()); - // TODO: NORELEASE convert this to using the high level client once there are APIs for it - statusReq = new Request("GET", "/_ilm/status"); - statusResponse = client().performRequest(statusReq); - statusResponseString = EntityUtils.toString(statusResponse.getEntity()); - assertThat(statusResponseString, - Matchers.anyOf(equalTo("{\"operation_mode\":\"STOPPING\"}"), equalTo("{\"operation_mode\":\"STOPPED\"}"))); + + statusResponse = execute(statusRequest, highLevelClient().indexLifecycle()::lifecycleManagementStatus, + highLevelClient().indexLifecycle()::lifecycleManagementStatusAsync); + assertThat(statusResponse.getOperationMode(), + Matchers.anyOf(equalTo(OperationMode.STOPPING), + equalTo(OperationMode.STOPPED))); StartILMRequest startReq = new StartILMRequest(); AcknowledgedResponse startResponse = execute(startReq, highLevelClient().indexLifecycle()::startILM, highLevelClient().indexLifecycle()::startILMAsync); assertTrue(startResponse.isAcknowledged()); - // TODO: NORELEASE convert this to using the high level client once there are APIs for it - statusReq = new Request("GET", "/_ilm/status"); - statusResponse = client().performRequest(statusReq); - statusResponseString = EntityUtils.toString(statusResponse.getEntity()); - assertEquals("{\"operation_mode\":\"RUNNING\"}", statusResponseString); + statusResponse = execute(statusRequest, highLevelClient().indexLifecycle()::lifecycleManagementStatus, + highLevelClient().indexLifecycle()::lifecycleManagementStatusAsync); + assertEquals(statusResponse.getOperationMode(), OperationMode.RUNNING); } public void testExplainLifecycle() throws Exception { 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 f814a80374cbd..23fd4575d6f85 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 @@ -97,6 +97,7 @@ import org.elasticsearch.action.support.replication.ReplicationRequest; import org.elasticsearch.action.update.UpdateRequest; import org.elasticsearch.client.RequestConverters.EndpointBuilder; +import org.elasticsearch.client.indexlifecycle.LifecycleManagementStatusRequest; import org.elasticsearch.client.indexlifecycle.LifecyclePolicy; import org.elasticsearch.client.indexlifecycle.PutLifecyclePolicyRequest; import org.elasticsearch.client.indexlifecycle.DeleteLifecyclePolicyRequest; @@ -2828,6 +2829,18 @@ public void testStopILM() throws Exception { assertThat(request.getParameters(), equalTo(expectedParams)); } + public void testLifecycleManagementStatus() throws Exception { + LifecycleManagementStatusRequest req = new LifecycleManagementStatusRequest(); + Map expectedParams = new HashMap<>(); + setRandomMasterTimeout(req::setMasterTimeout, TimedRequest.DEFAULT_TIMEOUT, expectedParams); + setRandomTimeoutTimeValue(req::setTimeout, TimedRequest.DEFAULT_MASTER_TIMEOUT, expectedParams); + + Request request = RequestConverters.lifecycleManagementStatus(req); + assertThat(request.getMethod(), equalTo(HttpGet.METHOD_NAME)); + assertThat(request.getEndpoint(), equalTo("/_ilm/status")); + assertThat(request.getParameters(), equalTo(expectedParams)); + } + public void testExplainLifecycle() throws Exception { ExplainLifecycleRequest req = new ExplainLifecycleRequest(); String[] indices = rarely() ? null : randomIndicesNames(0, 10); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/TimedRequestTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/TimedRequestTests.java new file mode 100644 index 0000000000000..34f477eff7f8c --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/TimedRequestTests.java @@ -0,0 +1,42 @@ +/* + * 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.common.unit.TimeValue; +import org.elasticsearch.test.ESTestCase; + +public class TimedRequestTests extends ESTestCase { + + public void testDefaults() { + TimedRequest timedRequest = new TimedRequest(){}; + assertEquals(timedRequest.timeout(), TimedRequest.DEFAULT_TIMEOUT); + assertEquals(timedRequest.masterNodeTimeout(), TimedRequest.DEFAULT_MASTER_TIMEOUT); + } + + public void testNonDefaults() { + TimedRequest timedRequest = new TimedRequest(){}; + TimeValue timeout = TimeValue.timeValueSeconds(randomIntBetween(0, 1000)); + TimeValue masterTimeout = TimeValue.timeValueSeconds(randomIntBetween(0,1000)); + timedRequest.setTimeout(timeout); + timedRequest.setMasterTimeout(masterTimeout); + assertEquals(timedRequest.timeout(), timeout); + assertEquals(timedRequest.masterNodeTimeout(), masterTimeout); + } +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/indexlifecycle/LifecycleManagementStatusResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/indexlifecycle/LifecycleManagementStatusResponseTests.java new file mode 100644 index 0000000000000..144039b8995c6 --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/indexlifecycle/LifecycleManagementStatusResponseTests.java @@ -0,0 +1,57 @@ +/* + * 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.indexlifecycle; + +import org.elasticsearch.common.xcontent.DeprecationHandler; +import org.elasticsearch.common.xcontent.NamedXContentRegistry; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.test.ESTestCase; +import org.hamcrest.CoreMatchers; + +import java.io.IOException; +import java.util.EnumSet; +import java.util.stream.Collectors; + +public class LifecycleManagementStatusResponseTests extends ESTestCase { + + public void testAllValidStatuses() { + EnumSet.allOf(OperationMode.class) + .forEach(e -> assertEquals(new LifecycleManagementStatusResponse(e.name()).getOperationMode(), e)); + } + + public void testXContent() throws IOException { + XContentType xContentType = XContentType.JSON; + String mode = randomFrom(EnumSet.allOf(OperationMode.class) + .stream().map(Enum::name).collect(Collectors.toList())); + XContentParser parser = xContentType.xContent().createParser(NamedXContentRegistry.EMPTY, + DeprecationHandler.THROW_UNSUPPORTED_OPERATION, "{\"operation_mode\" : \"" + mode + "\"}"); + assertEquals(LifecycleManagementStatusResponse.fromXContent(parser).getOperationMode(), OperationMode.fromString(mode)); + } + + public void testXContentInvalid() throws IOException { + XContentType xContentType = XContentType.JSON; + String mode = randomAlphaOfLength(10); + XContentParser parser = xContentType.xContent().createParser(NamedXContentRegistry.EMPTY, + DeprecationHandler.THROW_UNSUPPORTED_OPERATION, "{\"operation_mode\" : \"" + mode + "\"}"); + Exception e = expectThrows(IllegalArgumentException.class, () -> LifecycleManagementStatusResponse.fromXContent(parser)); + assertThat(e.getMessage(), CoreMatchers.containsString("failed to parse field [operation_mode]")); + } +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/indexlifecycle/OperationModeTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/indexlifecycle/OperationModeTests.java index 873b864734c8d..27651ba4a8c41 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/indexlifecycle/OperationModeTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/indexlifecycle/OperationModeTests.java @@ -20,6 +20,9 @@ package org.elasticsearch.client.indexlifecycle; import org.elasticsearch.test.ESTestCase; +import org.hamcrest.CoreMatchers; + +import java.util.EnumSet; public class OperationModeTests extends ESTestCase { @@ -36,4 +39,14 @@ public void testIsValidChange() { assertFalse(OperationMode.STOPPED.isValidChange(OperationMode.STOPPING)); assertFalse(OperationMode.STOPPED.isValidChange(OperationMode.STOPPED)); } + + public void testFromName() { + EnumSet.allOf(OperationMode.class).forEach(e -> assertEquals(OperationMode.fromString(e.name()), e)); + } + + public void testFromNameInvalid() { + String invalidName = randomAlphaOfLength(10); + Exception e = expectThrows(IllegalArgumentException.class, () -> OperationMode.fromString(invalidName)); + assertThat(e.getMessage(), CoreMatchers.containsString(invalidName + " is not a valid operation_mode")); + } }