HLRC: ML Close Job (#32943)
* HLRC: Adding ML Close Job API HLRC: Adding ML Close Job API * reconciling request converters * Adding serialization tests and addressing PR comments * Changing constructor order
This commit is contained in:
parent
9050c7e846
commit
3fbaae10af
|
@ -23,6 +23,8 @@ import org.apache.http.client.methods.HttpDelete;
|
||||||
import org.apache.http.client.methods.HttpPost;
|
import org.apache.http.client.methods.HttpPost;
|
||||||
import org.apache.http.client.methods.HttpPut;
|
import org.apache.http.client.methods.HttpPut;
|
||||||
import org.elasticsearch.client.RequestConverters.EndpointBuilder;
|
import org.elasticsearch.client.RequestConverters.EndpointBuilder;
|
||||||
|
import org.elasticsearch.common.Strings;
|
||||||
|
import org.elasticsearch.protocol.xpack.ml.CloseJobRequest;
|
||||||
import org.elasticsearch.protocol.xpack.ml.DeleteJobRequest;
|
import org.elasticsearch.protocol.xpack.ml.DeleteJobRequest;
|
||||||
import org.elasticsearch.protocol.xpack.ml.OpenJobRequest;
|
import org.elasticsearch.protocol.xpack.ml.OpenJobRequest;
|
||||||
import org.elasticsearch.protocol.xpack.ml.PutJobRequest;
|
import org.elasticsearch.protocol.xpack.ml.PutJobRequest;
|
||||||
|
@ -61,6 +63,30 @@ final class MLRequestConverters {
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Request closeJob(CloseJobRequest closeJobRequest) {
|
||||||
|
String endpoint = new EndpointBuilder()
|
||||||
|
.addPathPartAsIs("_xpack")
|
||||||
|
.addPathPartAsIs("ml")
|
||||||
|
.addPathPartAsIs("anomaly_detectors")
|
||||||
|
.addPathPart(Strings.collectionToCommaDelimitedString(closeJobRequest.getJobIds()))
|
||||||
|
.addPathPartAsIs("_close")
|
||||||
|
.build();
|
||||||
|
Request request = new Request(HttpPost.METHOD_NAME, endpoint);
|
||||||
|
|
||||||
|
RequestConverters.Params params = new RequestConverters.Params(request);
|
||||||
|
if (closeJobRequest.isForce() != null) {
|
||||||
|
params.putParam("force", Boolean.toString(closeJobRequest.isForce()));
|
||||||
|
}
|
||||||
|
if (closeJobRequest.isAllowNoJobs() != null) {
|
||||||
|
params.putParam("allow_no_jobs", Boolean.toString(closeJobRequest.isAllowNoJobs()));
|
||||||
|
}
|
||||||
|
if (closeJobRequest.getTimeout() != null) {
|
||||||
|
params.putParam("timeout", closeJobRequest.getTimeout().getStringRep());
|
||||||
|
}
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
static Request deleteJob(DeleteJobRequest deleteJobRequest) {
|
static Request deleteJob(DeleteJobRequest deleteJobRequest) {
|
||||||
String endpoint = new EndpointBuilder()
|
String endpoint = new EndpointBuilder()
|
||||||
.addPathPartAsIs("_xpack")
|
.addPathPartAsIs("_xpack")
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
package org.elasticsearch.client;
|
package org.elasticsearch.client;
|
||||||
|
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
|
import org.elasticsearch.protocol.xpack.ml.CloseJobRequest;
|
||||||
|
import org.elasticsearch.protocol.xpack.ml.CloseJobResponse;
|
||||||
import org.elasticsearch.protocol.xpack.ml.DeleteJobRequest;
|
import org.elasticsearch.protocol.xpack.ml.DeleteJobRequest;
|
||||||
import org.elasticsearch.protocol.xpack.ml.DeleteJobResponse;
|
import org.elasticsearch.protocol.xpack.ml.DeleteJobResponse;
|
||||||
import org.elasticsearch.protocol.xpack.ml.OpenJobRequest;
|
import org.elasticsearch.protocol.xpack.ml.OpenJobRequest;
|
||||||
|
@ -166,4 +168,40 @@ public final class MachineLearningClient {
|
||||||
listener,
|
listener,
|
||||||
Collections.emptySet());
|
Collections.emptySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes one or more Machine Learning Jobs. A job can be opened and closed multiple times throughout its lifecycle.
|
||||||
|
*
|
||||||
|
* A closed job cannot receive data or perform analysis operations, but you can still explore and navigate results.
|
||||||
|
*
|
||||||
|
* @param request request containing job_ids and additional options. See {@link CloseJobRequest}
|
||||||
|
* @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
|
||||||
|
* @return response containing if the job was successfully closed or not.
|
||||||
|
* @throws IOException when there is a serialization issue sending the request or receiving the response
|
||||||
|
*/
|
||||||
|
public CloseJobResponse closeJob(CloseJobRequest request, RequestOptions options) throws IOException {
|
||||||
|
return restHighLevelClient.performRequestAndParseEntity(request,
|
||||||
|
MLRequestConverters::closeJob,
|
||||||
|
options,
|
||||||
|
CloseJobResponse::fromXContent,
|
||||||
|
Collections.emptySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes one or more Machine Learning Jobs asynchronously, notifies listener on completion
|
||||||
|
*
|
||||||
|
* A closed job cannot receive data or perform analysis operations, but you can still explore and navigate results.
|
||||||
|
*
|
||||||
|
* @param request request containing job_ids and additional options. See {@link CloseJobRequest}
|
||||||
|
* @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
|
||||||
|
* @param listener Listener to be notified upon request completion
|
||||||
|
*/
|
||||||
|
public void closeJobAsync(CloseJobRequest request, RequestOptions options, ActionListener<CloseJobResponse> listener) {
|
||||||
|
restHighLevelClient.performRequestAsyncAndParseEntity(request,
|
||||||
|
MLRequestConverters::closeJob,
|
||||||
|
options,
|
||||||
|
CloseJobResponse::fromXContent,
|
||||||
|
listener,
|
||||||
|
Collections.emptySet());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.apache.http.client.methods.HttpPost;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
import org.elasticsearch.common.xcontent.json.JsonXContent;
|
import org.elasticsearch.common.xcontent.json.JsonXContent;
|
||||||
|
import org.elasticsearch.protocol.xpack.ml.CloseJobRequest;
|
||||||
import org.elasticsearch.protocol.xpack.ml.DeleteJobRequest;
|
import org.elasticsearch.protocol.xpack.ml.DeleteJobRequest;
|
||||||
import org.elasticsearch.protocol.xpack.ml.OpenJobRequest;
|
import org.elasticsearch.protocol.xpack.ml.OpenJobRequest;
|
||||||
import org.elasticsearch.protocol.xpack.ml.PutJobRequest;
|
import org.elasticsearch.protocol.xpack.ml.PutJobRequest;
|
||||||
|
@ -66,6 +67,29 @@ public class MLRequestConvertersTests extends ESTestCase {
|
||||||
assertEquals(bos.toString("UTF-8"), "{\"job_id\":\""+ jobId +"\",\"timeout\":\"10m\"}");
|
assertEquals(bos.toString("UTF-8"), "{\"job_id\":\""+ jobId +"\",\"timeout\":\"10m\"}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testCloseJob() {
|
||||||
|
String jobId = "somejobid";
|
||||||
|
CloseJobRequest closeJobRequest = new CloseJobRequest(jobId);
|
||||||
|
|
||||||
|
Request request = MLRequestConverters.closeJob(closeJobRequest);
|
||||||
|
assertEquals(HttpPost.METHOD_NAME, request.getMethod());
|
||||||
|
assertEquals("/_xpack/ml/anomaly_detectors/" + jobId + "/_close", request.getEndpoint());
|
||||||
|
assertFalse(request.getParameters().containsKey("force"));
|
||||||
|
assertFalse(request.getParameters().containsKey("allow_no_jobs"));
|
||||||
|
assertFalse(request.getParameters().containsKey("timeout"));
|
||||||
|
|
||||||
|
closeJobRequest = new CloseJobRequest(jobId, "otherjobs*");
|
||||||
|
closeJobRequest.setForce(true);
|
||||||
|
closeJobRequest.setAllowNoJobs(false);
|
||||||
|
closeJobRequest.setTimeout(TimeValue.timeValueMinutes(10));
|
||||||
|
request = MLRequestConverters.closeJob(closeJobRequest);
|
||||||
|
|
||||||
|
assertEquals("/_xpack/ml/anomaly_detectors/" + jobId + ",otherjobs*/_close", request.getEndpoint());
|
||||||
|
assertEquals(Boolean.toString(true), request.getParameters().get("force"));
|
||||||
|
assertEquals(Boolean.toString(false), request.getParameters().get("allow_no_jobs"));
|
||||||
|
assertEquals("10m", request.getParameters().get("timeout"));
|
||||||
|
}
|
||||||
|
|
||||||
public void testDeleteJob() {
|
public void testDeleteJob() {
|
||||||
String jobId = randomAlphaOfLength(10);
|
String jobId = randomAlphaOfLength(10);
|
||||||
DeleteJobRequest deleteJobRequest = new DeleteJobRequest(jobId);
|
DeleteJobRequest deleteJobRequest = new DeleteJobRequest(jobId);
|
||||||
|
@ -87,4 +111,4 @@ public class MLRequestConvertersTests extends ESTestCase {
|
||||||
jobBuilder.setAnalysisConfig(analysisConfig);
|
jobBuilder.setAnalysisConfig(analysisConfig);
|
||||||
return jobBuilder.build();
|
return jobBuilder.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@ package org.elasticsearch.client;
|
||||||
import com.carrotsearch.randomizedtesting.generators.CodepointSetGenerator;
|
import com.carrotsearch.randomizedtesting.generators.CodepointSetGenerator;
|
||||||
import org.apache.lucene.util.LuceneTestCase.AwaitsFix;
|
import org.apache.lucene.util.LuceneTestCase.AwaitsFix;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
|
import org.elasticsearch.protocol.xpack.ml.CloseJobRequest;
|
||||||
|
import org.elasticsearch.protocol.xpack.ml.CloseJobResponse;
|
||||||
import org.elasticsearch.protocol.xpack.ml.DeleteJobRequest;
|
import org.elasticsearch.protocol.xpack.ml.DeleteJobRequest;
|
||||||
import org.elasticsearch.protocol.xpack.ml.DeleteJobResponse;
|
import org.elasticsearch.protocol.xpack.ml.DeleteJobResponse;
|
||||||
import org.elasticsearch.protocol.xpack.ml.OpenJobRequest;
|
import org.elasticsearch.protocol.xpack.ml.OpenJobRequest;
|
||||||
|
@ -77,6 +79,19 @@ public class MachineLearningIT extends ESRestHighLevelClientTestCase {
|
||||||
assertTrue(response.isOpened());
|
assertTrue(response.isOpened());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testCloseJob() throws Exception {
|
||||||
|
String jobId = randomValidJobId();
|
||||||
|
Job job = buildJob(jobId);
|
||||||
|
MachineLearningClient machineLearningClient = highLevelClient().machineLearning();
|
||||||
|
machineLearningClient.putJob(new PutJobRequest(job), RequestOptions.DEFAULT);
|
||||||
|
machineLearningClient.openJob(new OpenJobRequest(jobId), RequestOptions.DEFAULT);
|
||||||
|
|
||||||
|
CloseJobResponse response = execute(new CloseJobRequest(jobId),
|
||||||
|
machineLearningClient::closeJob,
|
||||||
|
machineLearningClient::closeJobAsync);
|
||||||
|
assertTrue(response.isClosed());
|
||||||
|
}
|
||||||
|
|
||||||
public static String randomValidJobId() {
|
public static String randomValidJobId() {
|
||||||
CodepointSetGenerator generator = new CodepointSetGenerator("abcdefghijklmnopqrstuvwxyz0123456789".toCharArray());
|
CodepointSetGenerator generator = new CodepointSetGenerator("abcdefghijklmnopqrstuvwxyz0123456789".toCharArray());
|
||||||
return generator.ofCodePointsLength(random(), 10, 10);
|
return generator.ofCodePointsLength(random(), 10, 10);
|
||||||
|
|
|
@ -25,6 +25,8 @@ import org.elasticsearch.client.MachineLearningIT;
|
||||||
import org.elasticsearch.client.RequestOptions;
|
import org.elasticsearch.client.RequestOptions;
|
||||||
import org.elasticsearch.client.RestHighLevelClient;
|
import org.elasticsearch.client.RestHighLevelClient;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
|
import org.elasticsearch.protocol.xpack.ml.CloseJobRequest;
|
||||||
|
import org.elasticsearch.protocol.xpack.ml.CloseJobResponse;
|
||||||
import org.elasticsearch.protocol.xpack.ml.DeleteJobRequest;
|
import org.elasticsearch.protocol.xpack.ml.DeleteJobRequest;
|
||||||
import org.elasticsearch.protocol.xpack.ml.DeleteJobResponse;
|
import org.elasticsearch.protocol.xpack.ml.DeleteJobResponse;
|
||||||
import org.elasticsearch.protocol.xpack.ml.OpenJobRequest;
|
import org.elasticsearch.protocol.xpack.ml.OpenJobRequest;
|
||||||
|
@ -221,4 +223,56 @@ public class MlClientDocumentationIT extends ESRestHighLevelClientTestCase {
|
||||||
assertTrue(latch.await(30L, TimeUnit.SECONDS));
|
assertTrue(latch.await(30L, TimeUnit.SECONDS));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testCloseJob() throws Exception {
|
||||||
|
RestHighLevelClient client = highLevelClient();
|
||||||
|
|
||||||
|
{
|
||||||
|
Job job = MachineLearningIT.buildJob("closing-my-first-machine-learning-job");
|
||||||
|
client.machineLearning().putJob(new PutJobRequest(job), RequestOptions.DEFAULT);
|
||||||
|
client.machineLearning().openJob(new OpenJobRequest(job.getId()), RequestOptions.DEFAULT);
|
||||||
|
|
||||||
|
//tag::x-pack-ml-close-job-request
|
||||||
|
CloseJobRequest closeJobRequest = new CloseJobRequest("closing-my-first-machine-learning-job", "otherjobs*"); //<1>
|
||||||
|
closeJobRequest.setForce(false); //<2>
|
||||||
|
closeJobRequest.setAllowNoJobs(true); //<3>
|
||||||
|
closeJobRequest.setTimeout(TimeValue.timeValueMinutes(10)); //<4>
|
||||||
|
//end::x-pack-ml-close-job-request
|
||||||
|
|
||||||
|
//tag::x-pack-ml-close-job-execute
|
||||||
|
CloseJobResponse closeJobResponse = client.machineLearning().closeJob(closeJobRequest, RequestOptions.DEFAULT);
|
||||||
|
boolean isClosed = closeJobResponse.isClosed(); //<1>
|
||||||
|
//end::x-pack-ml-close-job-execute
|
||||||
|
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Job job = MachineLearningIT.buildJob("closing-my-second-machine-learning-job");
|
||||||
|
client.machineLearning().putJob(new PutJobRequest(job), RequestOptions.DEFAULT);
|
||||||
|
client.machineLearning().openJob(new OpenJobRequest(job.getId()), RequestOptions.DEFAULT);
|
||||||
|
|
||||||
|
//tag::x-pack-ml-close-job-listener
|
||||||
|
ActionListener<CloseJobResponse> listener = new ActionListener<CloseJobResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(CloseJobResponse closeJobResponse) {
|
||||||
|
//<1>
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Exception e) {
|
||||||
|
// <2>
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//end::x-pack-ml-close-job-listener
|
||||||
|
CloseJobRequest closeJobRequest = new CloseJobRequest("closing-my-second-machine-learning-job");
|
||||||
|
// Replace the empty listener by a blocking listener in test
|
||||||
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
listener = new LatchedActionListener<>(listener, latch);
|
||||||
|
|
||||||
|
// tag::x-pack-ml-close-job-execute-async
|
||||||
|
client.machineLearning().closeJobAsync(closeJobRequest, RequestOptions.DEFAULT, listener); //<1>
|
||||||
|
// end::x-pack-ml-close-job-execute-async
|
||||||
|
|
||||||
|
assertTrue(latch.await(30L, TimeUnit.SECONDS));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
[[java-rest-high-x-pack-ml-close-job]]
|
||||||
|
=== Close Job API
|
||||||
|
|
||||||
|
The Close Job API provides the ability to close {ml} jobs in the cluster.
|
||||||
|
It accepts a `CloseJobRequest` object and responds
|
||||||
|
with a `CloseJobResponse` object.
|
||||||
|
|
||||||
|
[[java-rest-high-x-pack-ml-close-job-request]]
|
||||||
|
==== Close Job Request
|
||||||
|
|
||||||
|
A `CloseJobRequest` object gets created with an existing non-null `jobId`.
|
||||||
|
|
||||||
|
["source","java",subs="attributes,callouts,macros"]
|
||||||
|
--------------------------------------------------
|
||||||
|
include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-close-job-request]
|
||||||
|
--------------------------------------------------
|
||||||
|
<1> Constructing a new request referencing existing job IDs
|
||||||
|
<2> Optionally used to close a failed job, or to forcefully close a job
|
||||||
|
which has not responded to its initial close request.
|
||||||
|
<3> Optionally set to ignore if a wildcard expression matches no jobs.
|
||||||
|
(This includes `_all` string or when no jobs have been specified)
|
||||||
|
<4> Optionally setting the `timeout` value for how long the
|
||||||
|
execution should wait for the job to be closed.
|
||||||
|
|
||||||
|
[[java-rest-high-x-pack-ml-close-job-execution]]
|
||||||
|
==== Execution
|
||||||
|
|
||||||
|
The request can be executed through the `MachineLearningClient` contained
|
||||||
|
in the `RestHighLevelClient` object, accessed via the `machineLearningClient()` method.
|
||||||
|
|
||||||
|
["source","java",subs="attributes,callouts,macros"]
|
||||||
|
--------------------------------------------------
|
||||||
|
include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-close-job-execute]
|
||||||
|
--------------------------------------------------
|
||||||
|
<1> `isClosed()` from the `CloseJobResponse` indicates if the job was successfully
|
||||||
|
closed or not.
|
||||||
|
|
||||||
|
[[java-rest-high-x-pack-ml-close-job-execution-async]]
|
||||||
|
==== Asynchronous Execution
|
||||||
|
|
||||||
|
The request can also be executed asynchronously:
|
||||||
|
|
||||||
|
["source","java",subs="attributes,callouts,macros"]
|
||||||
|
--------------------------------------------------
|
||||||
|
include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-close-job-execute-async]
|
||||||
|
--------------------------------------------------
|
||||||
|
<1> The `CloseJobRequest` to execute and the `ActionListener` to use when
|
||||||
|
the execution completes
|
||||||
|
|
||||||
|
The method does not block and returns immediately. The passed `ActionListener` is used
|
||||||
|
to notify the caller of completion. A typical `ActionListener` for `CloseJobResponse` may
|
||||||
|
look like
|
||||||
|
|
||||||
|
["source","java",subs="attributes,callouts,macros"]
|
||||||
|
--------------------------------------------------
|
||||||
|
include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-close-job-listener]
|
||||||
|
--------------------------------------------------
|
||||||
|
<1> `onResponse` is called back when the action is completed successfully
|
||||||
|
<2> `onFailure` is called back when some unexpected error occurs
|
|
@ -44,7 +44,7 @@ include-tagged::{doc-tests}/MlClientDocumentationIT.java[x-pack-ml-open-job-exec
|
||||||
the execution completes
|
the execution completes
|
||||||
|
|
||||||
The method does not block and returns immediately. The passed `ActionListener` is used
|
The method does not block and returns immediately. The passed `ActionListener` is used
|
||||||
to notify the caller of completion. A typical `ActionListner` for `OpenJobResponse` may
|
to notify the caller of completion. A typical `ActionListener` for `OpenJobResponse` may
|
||||||
look like
|
look like
|
||||||
|
|
||||||
["source","java",subs="attributes,callouts,macros"]
|
["source","java",subs="attributes,callouts,macros"]
|
||||||
|
|
|
@ -207,10 +207,12 @@ The Java High Level REST Client supports the following Machine Learning APIs:
|
||||||
* <<java-rest-high-x-pack-ml-put-job>>
|
* <<java-rest-high-x-pack-ml-put-job>>
|
||||||
* <<java-rest-high-x-pack-ml-delete-job>>
|
* <<java-rest-high-x-pack-ml-delete-job>>
|
||||||
* <<java-rest-high-x-pack-ml-open-job>>
|
* <<java-rest-high-x-pack-ml-open-job>>
|
||||||
|
* <<java-rest-high-x-pack-ml-close-job>>
|
||||||
|
|
||||||
include::ml/put-job.asciidoc[]
|
include::ml/put-job.asciidoc[]
|
||||||
include::ml/delete-job.asciidoc[]
|
include::ml/delete-job.asciidoc[]
|
||||||
include::ml/open-job.asciidoc[]
|
include::ml/open-job.asciidoc[]
|
||||||
|
include::ml/close-job.asciidoc[]
|
||||||
|
|
||||||
== Migration APIs
|
== Migration APIs
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,191 @@
|
||||||
|
/*
|
||||||
|
* 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.protocol.xpack.ml;
|
||||||
|
|
||||||
|
import org.elasticsearch.action.ActionRequest;
|
||||||
|
import org.elasticsearch.action.ActionRequestValidationException;
|
||||||
|
import org.elasticsearch.common.ParseField;
|
||||||
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
|
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
|
||||||
|
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.InvalidParameterException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class CloseJobRequest extends ActionRequest implements ToXContentObject {
|
||||||
|
|
||||||
|
public static final ParseField JOB_IDS = new ParseField("job_ids");
|
||||||
|
public static final ParseField TIMEOUT = new ParseField("timeout");
|
||||||
|
public static final ParseField FORCE = new ParseField("force");
|
||||||
|
public static final ParseField ALLOW_NO_JOBS = new ParseField("allow_no_jobs");
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static final ConstructingObjectParser<CloseJobRequest, Void> PARSER = new ConstructingObjectParser<>(
|
||||||
|
"close_job_request",
|
||||||
|
true, a -> new CloseJobRequest((List<String>) a[0]));
|
||||||
|
|
||||||
|
static {
|
||||||
|
PARSER.declareStringArray(ConstructingObjectParser.constructorArg(), JOB_IDS);
|
||||||
|
PARSER.declareString((obj, val) -> obj.setTimeout(TimeValue.parseTimeValue(val, TIMEOUT.getPreferredName())), TIMEOUT);
|
||||||
|
PARSER.declareBoolean(CloseJobRequest::setForce, FORCE);
|
||||||
|
PARSER.declareBoolean(CloseJobRequest::setAllowNoJobs, ALLOW_NO_JOBS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String ALL_JOBS = "_all";
|
||||||
|
|
||||||
|
private final List<String> jobIds;
|
||||||
|
private TimeValue timeout;
|
||||||
|
private Boolean force;
|
||||||
|
private Boolean allowNoJobs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Explicitly close all jobs
|
||||||
|
*
|
||||||
|
* @return a {@link CloseJobRequest} for all existing jobs
|
||||||
|
*/
|
||||||
|
public static CloseJobRequest closeAllJobsRequest(){
|
||||||
|
return new CloseJobRequest(ALL_JOBS);
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseJobRequest(List<String> jobIds) {
|
||||||
|
if (jobIds.isEmpty()) {
|
||||||
|
throw new InvalidParameterException("jobIds must not be empty");
|
||||||
|
}
|
||||||
|
if (jobIds.stream().anyMatch(Objects::isNull)) {
|
||||||
|
throw new NullPointerException("jobIds must not contain null values");
|
||||||
|
}
|
||||||
|
this.jobIds = new ArrayList<>(jobIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the specified Jobs via their unique jobIds
|
||||||
|
*
|
||||||
|
* @param jobIds must be non-null and non-empty and each jobId must be non-null
|
||||||
|
*/
|
||||||
|
public CloseJobRequest(String... jobIds) {
|
||||||
|
this(Arrays.asList(jobIds));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All the jobIds to be closed
|
||||||
|
*/
|
||||||
|
public List<String> getJobIds() {
|
||||||
|
return jobIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How long to wait for the close request to complete before timing out.
|
||||||
|
*
|
||||||
|
* Default: 30 minutes
|
||||||
|
*/
|
||||||
|
public TimeValue getTimeout() {
|
||||||
|
return timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link CloseJobRequest#getTimeout()}
|
||||||
|
*/
|
||||||
|
public void setTimeout(TimeValue timeout) {
|
||||||
|
this.timeout = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should the closing be forced.
|
||||||
|
*
|
||||||
|
* Use to close a failed job, or to forcefully close a job which has not responded to its initial close request.
|
||||||
|
*/
|
||||||
|
public Boolean isForce() {
|
||||||
|
return force;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link CloseJobRequest#isForce()}
|
||||||
|
*/
|
||||||
|
public void setForce(boolean force) {
|
||||||
|
this.force = force;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to ignore if a wildcard expression matches no jobs.
|
||||||
|
*
|
||||||
|
* This includes `_all` string or when no jobs have been specified
|
||||||
|
*/
|
||||||
|
public Boolean isAllowNoJobs() {
|
||||||
|
return allowNoJobs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link CloseJobRequest#isAllowNoJobs()}
|
||||||
|
*/
|
||||||
|
public void setAllowNoJobs(boolean allowNoJobs) {
|
||||||
|
this.allowNoJobs = allowNoJobs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ActionRequestValidationException validate() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(jobIds, timeout, allowNoJobs, force);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
if (this == other) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (other == null || getClass() != other.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseJobRequest that = (CloseJobRequest) other;
|
||||||
|
return Objects.equals(jobIds, that.jobIds) &&
|
||||||
|
Objects.equals(timeout, that.timeout) &&
|
||||||
|
Objects.equals(allowNoJobs, that.allowNoJobs) &&
|
||||||
|
Objects.equals(force, that.force);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||||
|
builder.startObject();
|
||||||
|
|
||||||
|
builder.field(JOB_IDS.getPreferredName(), jobIds);
|
||||||
|
|
||||||
|
if (timeout != null) {
|
||||||
|
builder.field(TIMEOUT.getPreferredName(), timeout.getStringRep());
|
||||||
|
}
|
||||||
|
if (force != null) {
|
||||||
|
builder.field(FORCE.getPreferredName(), force);
|
||||||
|
}
|
||||||
|
if (allowNoJobs != null) {
|
||||||
|
builder.field(ALLOW_NO_JOBS.getPreferredName(), allowNoJobs);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.endObject();
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
* 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.protocol.xpack.ml;
|
||||||
|
|
||||||
|
import org.elasticsearch.action.ActionResponse;
|
||||||
|
import org.elasticsearch.common.ParseField;
|
||||||
|
import org.elasticsearch.common.xcontent.ObjectParser;
|
||||||
|
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class CloseJobResponse extends ActionResponse implements ToXContentObject {
|
||||||
|
|
||||||
|
private static final ParseField CLOSED = new ParseField("closed");
|
||||||
|
|
||||||
|
public static final ObjectParser<CloseJobResponse, Void> PARSER =
|
||||||
|
new ObjectParser<>("close_job_response", true, CloseJobResponse::new);
|
||||||
|
|
||||||
|
static {
|
||||||
|
PARSER.declareBoolean(CloseJobResponse::setClosed, CLOSED);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean closed;
|
||||||
|
|
||||||
|
CloseJobResponse() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public CloseJobResponse(boolean closed) {
|
||||||
|
this.closed = closed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CloseJobResponse fromXContent(XContentParser parser) throws IOException {
|
||||||
|
return PARSER.parse(parser, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isClosed() {
|
||||||
|
return closed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClosed(boolean closed) {
|
||||||
|
this.closed = closed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
if (this == other) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (other == null || getClass() != other.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseJobResponse that = (CloseJobResponse) other;
|
||||||
|
return isClosed() == that.isClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(isClosed());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||||
|
builder.startObject();
|
||||||
|
builder.field(CLOSED.getPreferredName(), closed);
|
||||||
|
builder.endObject();
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* 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.protocol.xpack.ml;
|
||||||
|
|
||||||
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
|
import org.elasticsearch.test.AbstractXContentTestCase;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class CloseJobRequestTests extends AbstractXContentTestCase<CloseJobRequest> {
|
||||||
|
|
||||||
|
public void testCloseAllJobsRequest() {
|
||||||
|
CloseJobRequest request = CloseJobRequest.closeAllJobsRequest();
|
||||||
|
assertEquals(request.getJobIds().size(), 1);
|
||||||
|
assertEquals(request.getJobIds().get(0), "_all");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testWithNullJobIds() {
|
||||||
|
Exception exception = expectThrows(IllegalArgumentException.class, CloseJobRequest::new);
|
||||||
|
assertEquals(exception.getMessage(), "jobIds must not be empty");
|
||||||
|
|
||||||
|
exception = expectThrows(NullPointerException.class, () -> new CloseJobRequest("job1", null));
|
||||||
|
assertEquals(exception.getMessage(), "jobIds must not contain null values");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected CloseJobRequest createTestInstance() {
|
||||||
|
int jobCount = randomIntBetween(1, 10);
|
||||||
|
List<String> jobIds = new ArrayList<>(jobCount);
|
||||||
|
|
||||||
|
for (int i = 0; i < jobCount; i++) {
|
||||||
|
jobIds.add(randomAlphaOfLength(10));
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseJobRequest request = new CloseJobRequest(jobIds.toArray(new String[0]));
|
||||||
|
|
||||||
|
if (randomBoolean()) {
|
||||||
|
request.setAllowNoJobs(randomBoolean());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (randomBoolean()) {
|
||||||
|
request.setTimeout(TimeValue.timeValueMinutes(randomIntBetween(1, 10)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (randomBoolean()) {
|
||||||
|
request.setForce(randomBoolean());
|
||||||
|
}
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected CloseJobRequest doParseInstance(XContentParser parser) throws IOException {
|
||||||
|
return CloseJobRequest.PARSER.parse(parser, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsUnknownFields() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.protocol.xpack.ml;
|
||||||
|
|
||||||
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
|
import org.elasticsearch.test.AbstractXContentTestCase;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class CloseJobResponseTests extends AbstractXContentTestCase<CloseJobResponse> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected CloseJobResponse createTestInstance() {
|
||||||
|
return new CloseJobResponse(randomBoolean());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected CloseJobResponse doParseInstance(XContentParser parser) throws IOException {
|
||||||
|
return CloseJobResponse.fromXContent(parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsUnknownFields() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue