diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java
index 248d86c7c4217..5aa64a5c1375e 100644
--- a/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java
+++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java
@@ -51,6 +51,8 @@
import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsResponse;
import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
import org.elasticsearch.action.admin.indices.shrink.ResizeResponse;
+import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest;
+import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateResponse;
import java.io.IOException;
import java.util.Collections;
@@ -456,4 +458,26 @@ public void putSettingsAsync(UpdateSettingsRequest updateSettingsRequest, Action
UpdateSettingsResponse::fromXContent, listener, emptySet(), headers);
}
+ /**
+ * Puts an index template using the Index Templates API
+ *
+ * See Index Templates API
+ * on elastic.co
+ */
+ public PutIndexTemplateResponse putTemplate(PutIndexTemplateRequest putIndexTemplateRequest, Header... headers) throws IOException {
+ return restHighLevelClient.performRequestAndParseEntity(putIndexTemplateRequest, RequestConverters::putTemplate,
+ PutIndexTemplateResponse::fromXContent, emptySet(), headers);
+ }
+
+ /**
+ * Asynchronously puts an index template using the Index Templates API
+ *
+ * See Index Templates API
+ * on elastic.co
+ */
+ public void putTemplateAsync(PutIndexTemplateRequest putIndexTemplateRequest,
+ ActionListener listener, Header... headers) {
+ restHighLevelClient.performRequestAsyncAndParseEntity(putIndexTemplateRequest, RequestConverters::putTemplate,
+ PutIndexTemplateResponse::fromXContent, listener, emptySet(), headers);
+ }
}
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 705d8dfc9d252..720c934026b0b 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
@@ -47,6 +47,7 @@
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest;
import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
import org.elasticsearch.action.admin.indices.shrink.ResizeType;
+import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesRequest;
@@ -77,7 +78,6 @@
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.VersionType;
import org.elasticsearch.index.rankeval.RankEvalRequest;
-import org.elasticsearch.rest.action.RestFieldCapabilitiesAction;
import org.elasticsearch.rest.action.search.RestSearchAction;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
@@ -86,10 +86,7 @@
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.Locale;
-import java.util.Map;
import java.util.StringJoiner;
final class RequestConverters {
@@ -647,6 +644,21 @@ static Request indexPutSettings(UpdateSettingsRequest updateSettingsRequest) thr
return request;
}
+ static Request putTemplate(PutIndexTemplateRequest putIndexTemplateRequest) throws IOException {
+ String endpoint = new EndpointBuilder().addPathPartAsIs("_template").addPathPart(putIndexTemplateRequest.name()).build();
+ Request request = new Request(HttpPut.METHOD_NAME, endpoint);
+ Params params = new Params(request);
+ params.withMasterTimeout(putIndexTemplateRequest.masterNodeTimeout());
+ if (putIndexTemplateRequest.create()) {
+ params.putParam("create", Boolean.TRUE.toString());
+ }
+ if (Strings.hasText(putIndexTemplateRequest.cause())) {
+ params.putParam("cause", putIndexTemplateRequest.cause());
+ }
+ request.setEntity(createEntity(putIndexTemplateRequest, REQUEST_BODY_CONTENT_TYPE));
+ return request;
+ }
+
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/test/java/org/elasticsearch/client/IndicesClientIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java
index eb09084200bd2..931447d85d44a 100644
--- 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
@@ -56,11 +56,14 @@
import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
import org.elasticsearch.action.admin.indices.shrink.ResizeResponse;
import org.elasticsearch.action.admin.indices.shrink.ResizeType;
+import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest;
+import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.action.support.broadcast.BroadcastResponse;
import org.elasticsearch.cluster.metadata.IndexMetaData;
+import org.elasticsearch.common.ValidationException;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.ByteSizeUnit;
@@ -73,11 +76,19 @@
import org.elasticsearch.rest.RestStatus;
import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.Map;
import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF_REPLICAS;
+import static org.elasticsearch.common.xcontent.support.XContentMapValues.extractRawValues;
+import static org.elasticsearch.common.xcontent.support.XContentMapValues.extractValue;
import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasEntry;
+import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.startsWith;
@@ -812,4 +823,59 @@ public void testIndexPutSettingNonExistent() throws IOException {
+ "or check the breaking changes documentation for removed settings]"));
}
+ @SuppressWarnings("unchecked")
+ public void testPutTemplate() throws Exception {
+ PutIndexTemplateRequest putTemplateRequest = new PutIndexTemplateRequest()
+ .name("my-template")
+ .patterns(Arrays.asList("pattern-1", "name-*"))
+ .order(10)
+ .create(randomBoolean())
+ .settings(Settings.builder().put("number_of_shards", "3").put("number_of_replicas", "0"))
+ .mapping("doc", "host_name", "type=keyword", "description", "type=text")
+ .alias(new Alias("alias-1").indexRouting("abc")).alias(new Alias("{index}-write").searchRouting("xyz"));
+
+ PutIndexTemplateResponse putTemplateResponse = execute(putTemplateRequest,
+ highLevelClient().indices()::putTemplate, highLevelClient().indices()::putTemplateAsync);
+ assertThat(putTemplateResponse.isAcknowledged(), equalTo(true));
+
+ Map templates = getAsMap("/_template/my-template");
+ assertThat(templates.keySet(), hasSize(1));
+ assertThat(extractValue("my-template.order", templates), equalTo(10));
+ assertThat(extractRawValues("my-template.index_patterns", templates), contains("pattern-1", "name-*"));
+ assertThat(extractValue("my-template.settings.index.number_of_shards", templates), equalTo("3"));
+ assertThat(extractValue("my-template.settings.index.number_of_replicas", templates), equalTo("0"));
+ assertThat(extractValue("my-template.mappings.doc.properties.host_name.type", templates), equalTo("keyword"));
+ assertThat(extractValue("my-template.mappings.doc.properties.description.type", templates), equalTo("text"));
+ assertThat((Map) extractValue("my-template.aliases.alias-1", templates), hasEntry("index_routing", "abc"));
+ assertThat((Map) extractValue("my-template.aliases.{index}-write", templates), hasEntry("search_routing", "xyz"));
+ }
+
+ public void testPutTemplateBadRequests() throws Exception {
+ RestHighLevelClient client = highLevelClient();
+
+ // Failed to validate because index patterns are missing
+ PutIndexTemplateRequest withoutPattern = new PutIndexTemplateRequest("t1");
+ ValidationException withoutPatternError = expectThrows(ValidationException.class,
+ () -> execute(withoutPattern, client.indices()::putTemplate, client.indices()::putTemplateAsync));
+ assertThat(withoutPatternError.validationErrors(), contains("index patterns are missing"));
+
+ // Create-only specified but an template exists already
+ PutIndexTemplateRequest goodTemplate = new PutIndexTemplateRequest("t2").patterns(Arrays.asList("qa-*", "prod-*"));
+ assertTrue(execute(goodTemplate, client.indices()::putTemplate, client.indices()::putTemplateAsync).isAcknowledged());
+ goodTemplate.create(true);
+ ElasticsearchException alreadyExistsError = expectThrows(ElasticsearchException.class,
+ () -> execute(goodTemplate, client.indices()::putTemplate, client.indices()::putTemplateAsync));
+ assertThat(alreadyExistsError.getDetailedMessage(),
+ containsString("[type=illegal_argument_exception, reason=index_template [t2] already exists]"));
+ goodTemplate.create(false);
+ assertTrue(execute(goodTemplate, client.indices()::putTemplate, client.indices()::putTemplateAsync).isAcknowledged());
+
+ // Rejected due to unknown settings
+ PutIndexTemplateRequest unknownSettingTemplate = new PutIndexTemplateRequest("t3")
+ .patterns(Collections.singletonList("any"))
+ .settings(Settings.builder().put("this-setting-does-not-exist", 100));
+ ElasticsearchStatusException unknownSettingError = expectThrows(ElasticsearchStatusException.class,
+ () -> execute(unknownSettingTemplate, client.indices()::putTemplate, client.indices()::putTemplateAsync));
+ assertThat(unknownSettingError.getDetailedMessage(), containsString("unknown setting [index.this-setting-does-not-exist]"));
+ }
}
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 1953c820b8af8..70c209c30abf2 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
@@ -26,12 +26,11 @@
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.ByteArrayEntity;
-import org.apache.http.entity.ContentType;
-import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.DocWriteRequest;
import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest;
+import org.elasticsearch.action.admin.indices.alias.Alias;
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;
@@ -46,10 +45,11 @@
import org.elasticsearch.action.admin.indices.open.OpenIndexRequest;
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
import org.elasticsearch.action.admin.indices.rollover.RolloverRequest;
-import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest;
+import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest;
import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
import org.elasticsearch.action.admin.indices.shrink.ResizeType;
+import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkShardRequest;
import org.elasticsearch.action.delete.DeleteRequest;
@@ -70,6 +70,7 @@
import org.elasticsearch.action.support.master.MasterNodeRequest;
import org.elasticsearch.action.support.replication.ReplicationRequest;
import org.elasticsearch.action.update.UpdateRequest;
+import org.elasticsearch.client.RequestConverters.EndpointBuilder;
import org.elasticsearch.common.CheckedBiConsumer;
import org.elasticsearch.common.CheckedFunction;
import org.elasticsearch.common.Strings;
@@ -77,15 +78,13 @@
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.Streams;
import org.elasticsearch.common.lucene.uid.Versions;
-import org.elasticsearch.common.settings.IndexScopedSettings;
+import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
-import org.elasticsearch.client.RequestConverters.EndpointBuilder;
-import org.elasticsearch.client.RequestConverters.Params;
import org.elasticsearch.index.RandomCreateIndexGenerator;
import org.elasticsearch.index.VersionType;
import org.elasticsearch.index.query.TermQueryBuilder;
@@ -94,7 +93,6 @@
import org.elasticsearch.index.rankeval.RankEvalSpec;
import org.elasticsearch.index.rankeval.RatedRequest;
import org.elasticsearch.index.rankeval.RestRankEvalAction;
-import org.elasticsearch.rest.action.RestFieldCapabilitiesAction;
import org.elasticsearch.rest.action.search.RestSearchAction;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
@@ -111,8 +109,6 @@
import java.io.IOException;
import java.io.InputStream;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -121,7 +117,6 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
-import java.util.Set;
import java.util.StringJoiner;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -1432,6 +1427,48 @@ public void testIndexPutSettings() throws IOException {
assertEquals(expectedParams, request.getParameters());
}
+ public void testPutTemplateRequest() throws Exception {
+ Map names = new HashMap<>();
+ names.put("log", "log");
+ names.put("template#1", "template%231");
+ names.put("-#template", "-%23template");
+ names.put("foo^bar", "foo%5Ebar");
+
+ PutIndexTemplateRequest putTemplateRequest = new PutIndexTemplateRequest()
+ .name(randomFrom(names.keySet()))
+ .patterns(Arrays.asList(generateRandomStringArray(20, 100, false, false)));
+ if (randomBoolean()) {
+ putTemplateRequest.order(randomInt());
+ }
+ if (randomBoolean()) {
+ putTemplateRequest.version(randomInt());
+ }
+ if (randomBoolean()) {
+ putTemplateRequest.settings(Settings.builder().put("setting-" + randomInt(), randomTimeValue()));
+ }
+ if (randomBoolean()) {
+ putTemplateRequest.mapping("doc-" + randomInt(), "field-" + randomInt(), "type=" + randomFrom("text", "keyword"));
+ }
+ if (randomBoolean()) {
+ putTemplateRequest.alias(new Alias("alias-" + randomInt()));
+ }
+ Map expectedParams = new HashMap<>();
+ if (randomBoolean()) {
+ expectedParams.put("create", Boolean.TRUE.toString());
+ putTemplateRequest.create(true);
+ }
+ if (randomBoolean()) {
+ String cause = randomUnicodeOfCodepointLengthBetween(1, 50);
+ putTemplateRequest.cause(cause);
+ expectedParams.put("cause", cause);
+ }
+ setRandomMasterTimeout(putTemplateRequest, expectedParams);
+ Request request = RequestConverters.putTemplate(putTemplateRequest);
+ assertThat(request.getEndpoint(), equalTo("/_template/" + names.get(putTemplateRequest.name())));
+ assertThat(request.getParameters(), equalTo(expectedParams));
+ assertToXContentBody(putTemplateRequest, request.getEntity());
+ }
+
private static void assertToXContentBody(ToXContent expectedBody, HttpEntity actualEntity) throws IOException {
BytesReference expectedBytes = XContentHelper.toXContent(expectedBody, REQUEST_BODY_CONTENT_TYPE, false);
assertEquals(XContentType.JSON.mediaTypeWithoutParameters(), actualEntity.getContentType().getValue());
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 33cd12152851b..1dd9834d8f53f 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
@@ -55,6 +55,10 @@
import org.elasticsearch.action.admin.indices.shrink.ResizeRequest;
import org.elasticsearch.action.admin.indices.shrink.ResizeResponse;
import org.elasticsearch.action.admin.indices.shrink.ResizeType;
+import org.elasticsearch.action.admin.indices.template.delete.DeleteIndexTemplateRequest;
+import org.elasticsearch.action.admin.indices.template.delete.DeleteIndexTemplateResponse;
+import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest;
+import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateResponse;
import org.elasticsearch.action.support.ActiveShardCount;
import org.elasticsearch.action.support.DefaultShardOperationFailedException;
import org.elasticsearch.action.support.IndicesOptions;
@@ -71,11 +75,14 @@
import org.elasticsearch.rest.RestStatus;
import java.io.IOException;
+import java.util.Arrays;
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 Indices API documentation.
* You need to wrap your code between two tags like:
@@ -1598,4 +1605,164 @@ public void onFailure(Exception e) {
assertTrue(latch.await(30L, TimeUnit.SECONDS));
}
+ public void testPutTemplate() throws Exception {
+ RestHighLevelClient client = highLevelClient();
+
+ // tag::put-template-request
+ PutIndexTemplateRequest request = new PutIndexTemplateRequest("my-template"); // <1>
+ request.patterns(Arrays.asList("pattern-1", "log-*")); // <2>
+ // end::put-template-request
+
+ // tag::put-template-request-settings
+ request.settings(Settings.builder() // <1>
+ .put("index.number_of_shards", 3)
+ .put("index.number_of_replicas", 1)
+ );
+ // end::put-template-request-settings
+
+ {
+ // tag::put-template-request-mappings-json
+ request.mapping("tweet", // <1>
+ "{\n" +
+ " \"tweet\": {\n" +
+ " \"properties\": {\n" +
+ " \"message\": {\n" +
+ " \"type\": \"text\"\n" +
+ " }\n" +
+ " }\n" +
+ " }\n" +
+ "}", // <2>
+ XContentType.JSON);
+ // end::put-template-request-mappings-json
+ assertTrue(client.indices().putTemplate(request).isAcknowledged());
+ }
+ {
+ //tag::put-template-request-mappings-map
+ Map jsonMap = new HashMap<>();
+ Map message = new HashMap<>();
+ message.put("type", "text");
+ Map properties = new HashMap<>();
+ properties.put("message", message);
+ Map tweet = new HashMap<>();
+ tweet.put("properties", properties);
+ jsonMap.put("tweet", tweet);
+ request.mapping("tweet", jsonMap); // <1>
+ //end::put-template-request-mappings-map
+ assertTrue(client.indices().putTemplate(request).isAcknowledged());
+ }
+ {
+ //tag::put-template-request-mappings-xcontent
+ XContentBuilder builder = XContentFactory.jsonBuilder();
+ builder.startObject();
+ {
+ builder.startObject("tweet");
+ {
+ builder.startObject("properties");
+ {
+ builder.startObject("message");
+ {
+ builder.field("type", "text");
+ }
+ builder.endObject();
+ }
+ builder.endObject();
+ }
+ builder.endObject();
+ }
+ builder.endObject();
+ request.mapping("tweet", builder); // <1>
+ //end::put-template-request-mappings-xcontent
+ assertTrue(client.indices().putTemplate(request).isAcknowledged());
+ }
+ {
+ //tag::put-template-request-mappings-shortcut
+ request.mapping("tweet", "message", "type=text"); // <1>
+ //end::put-template-request-mappings-shortcut
+ assertTrue(client.indices().putTemplate(request).isAcknowledged());
+ }
+
+ // tag::put-template-request-aliases
+ request.alias(new Alias("twitter_alias").filter(QueryBuilders.termQuery("user", "kimchy"))); // <1>
+ request.alias(new Alias("{index}_alias").searchRouting("xyz")); // <2>
+ // end::put-template-request-aliases
+
+ // tag::put-template-request-order
+ request.order(20); // <1>
+ // end::put-template-request-order
+
+ // tag::put-template-request-version
+ request.version(4); // <1>
+ // end::put-template-request-version
+
+ // tag::put-template-whole-source
+ request.source("{\n" +
+ " \"index_patterns\": [\n" +
+ " \"log-*\",\n" +
+ " \"pattern-1\"\n" +
+ " ],\n" +
+ " \"order\": 1,\n" +
+ " \"settings\": {\n" +
+ " \"number_of_shards\": 1\n" +
+ " },\n" +
+ " \"mappings\": {\n" +
+ " \"tweet\": {\n" +
+ " \"properties\": {\n" +
+ " \"message\": {\n" +
+ " \"type\": \"text\"\n" +
+ " }\n" +
+ " }\n" +
+ " }\n" +
+ " },\n" +
+ " \"aliases\": {\n" +
+ " \"alias-1\": {},\n" +
+ " \"{index}-alias\": {}\n" +
+ " }\n" +
+ "}", XContentType.JSON); // <1>
+ // end::put-template-whole-source
+
+ // tag::put-template-request-create
+ request.create(true); // <1>
+ // end::put-template-request-create
+
+ // tag::put-template-request-masterTimeout
+ request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1>
+ request.masterNodeTimeout("1m"); // <2>
+ // end::put-template-request-masterTimeout
+
+ request.create(false); // make test happy
+
+ // tag::put-template-execute
+ PutIndexTemplateResponse putTemplateResponse = client.indices().putTemplate(request);
+ // end::put-template-execute
+
+ // tag::put-template-response
+ boolean acknowledged = putTemplateResponse.isAcknowledged(); // <1>
+ // end::put-template-response
+ assertTrue(acknowledged);
+
+ // tag::put-template-execute-listener
+ ActionListener listener =
+ new ActionListener() {
+ @Override
+ public void onResponse(PutIndexTemplateResponse putTemplateResponse) {
+ // <1>
+ }
+
+ @Override
+ public void onFailure(Exception e) {
+ // <2>
+ }
+ };
+ // end::put-template-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-template-execute-async
+ client.indices().putTemplateAsync(request, listener); // <1>
+ // end::put-template-execute-async
+
+ assertTrue(latch.await(30L, TimeUnit.SECONDS));
+ }
}
diff --git a/docs/CHANGELOG.asciidoc b/docs/CHANGELOG.asciidoc
index 1b5afe9d0b841..8dd8e64884533 100644
--- a/docs/CHANGELOG.asciidoc
+++ b/docs/CHANGELOG.asciidoc
@@ -107,7 +107,6 @@ ones that the user is authorized to access in case field level security is enabl
Fixed prerelease version of elasticsearch in the `deb` package to sort before GA versions
({pull}29000[#29000])
-Respect accept header on requests with no handler ({pull}30383[#30383])
Rollup::
* Validate timezone in range queries to ensure they match the selected job when
searching ({pull}30338[#30338])
@@ -118,6 +117,7 @@ Fail snapshot operations early when creating or deleting a snapshot on a reposit
written to by an older Elasticsearch after writing to it with a newer Elasticsearch version. ({pull}30140[#30140])
Fix NPE when CumulativeSum agg encounters null value/empty bucket ({pull}29641[#29641])
+Do not fail snapshot when deleting a missing snapshotted file ({pull}30332[#30332])
//[float]
//=== Regressions
@@ -165,6 +165,8 @@ synchronous and predictable. Also the trigger engine thread is only started on
data nodes. And the Execute Watch API can be triggered regardless is watcher is
started or stopped. ({pull}30118[#30118])
+Added put index template API to the high level rest client ({pull}30400[#30400])
+
[float]
=== Bug Fixes
@@ -212,6 +214,8 @@ coming[6.3.1]
Reduce the number of object allocations made by {security} when resolving the indices and aliases for a request ({pull}30180[#30180])
+Respect accept header on requests with no handler ({pull}30383[#30383])
+
//[float]
//=== Regressions
diff --git a/docs/java-rest/high-level/indices/put_template.asciidoc b/docs/java-rest/high-level/indices/put_template.asciidoc
new file mode 100644
index 0000000000000..7f0f3a1fee7f5
--- /dev/null
+++ b/docs/java-rest/high-level/indices/put_template.asciidoc
@@ -0,0 +1,168 @@
+[[java-rest-high-put-template]]
+=== Put Template API
+
+[[java-rest-high-put-template-request]]
+==== Put Index Template Request
+
+A `PutIndexTemplateRequest` specifies the `name` of a template and `patterns`
+which controls whether the template should be applied to the new index.
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-template-request]
+--------------------------------------------------
+<1> The name of the template
+<2> The patterns of the template
+
+==== Settings
+The settings of the template will be applied to the new index whose name matches the
+template's patterns.
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-template-request-settings]
+--------------------------------------------------
+<1> Settings for this template
+
+[[java-rest-high-put-template-request-mappings]]
+==== Mappings
+The mapping of the template will be applied to the new index whose name matches the
+template's patterns.
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-template-request-mappings-json]
+--------------------------------------------------
+<1> The type to define
+<2> The mapping for this type, provided as a JSON string
+
+The mapping source can be provided in different ways in addition to the
+`String` example shown above:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-template-request-mappings-map]
+--------------------------------------------------
+<1> Mapping source provided as a `Map` which gets automatically converted
+to JSON format
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-template-request-mappings-xcontent]
+--------------------------------------------------
+<1> Mapping source provided as an `XContentBuilder` object, the Elasticsearch
+built-in helpers to generate JSON content
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-template-request-mappings-shortcut]
+--------------------------------------------------
+<1> Mapping source provided as `Object` key-pairs, which gets converted to
+JSON format
+
+==== Aliases
+The aliases of the template will define aliasing to the index whose name matches the
+template's patterns. A placeholder `{index}` can be used in an alias of a template.
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-template-request-aliases]
+--------------------------------------------------
+<1> The alias to define
+<2> The alias to define with placeholder
+
+==== Order
+In case multiple templates match an index, the orders of matching templates determine
+the sequence that settings, mappings, and alias of each matching template is applied.
+Templates with lower orders are applied first, and higher orders override them.
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-template-request-order]
+--------------------------------------------------
+<1> The order of the template
+
+==== Version
+A template can optionally specify a version number which can be used to simplify template
+management by external systems.
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-template-request-version]
+--------------------------------------------------
+<1> The version number of the template
+
+==== Providing the whole source
+The whole source including all of its sections (mappings, settings and aliases)
+can also be provided:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-template-whole-source]
+--------------------------------------------------
+<1> The source provided as a JSON string. It can also be provided as a `Map`
+or an `XContentBuilder`.
+
+==== Optional arguments
+The following arguments can optionally be provided:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-template-request-create]
+--------------------------------------------------
+<1> To force to only create a new template; do not overwrite the existing template
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-template-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-put-template-sync]]
+==== Synchronous Execution
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-template-execute]
+--------------------------------------------------
+
+[[java-rest-high-put-template-async]]
+==== Asynchronous Execution
+
+The asynchronous execution of a put template request requires both the `PutIndexTemplateRequest`
+instance and an `ActionListener` instance to be passed to the asynchronous method:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-template-execute-async]
+--------------------------------------------------
+<1> The `PutIndexTemplateRequest` 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 `PutIndexTemplateResponse` looks like:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-template-execute-listener]
+--------------------------------------------------
+<1> Called when the execution is successfully completed. The response is
+provided as an argument
+<2> Called in case of failure. The raised exception is provided as an argument
+
+[[java-rest-high-put-template-response]]
+==== Put Index Template Response
+
+The returned `PutIndexTemplateResponse` allows to retrieve information about the
+executed operation as follows:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[put-template-response]
+--------------------------------------------------
+<1> Indicates whether all of the nodes have acknowledged the request
diff --git a/docs/java-rest/high-level/supported-apis.asciidoc b/docs/java-rest/high-level/supported-apis.asciidoc
index 4d845e538415f..6623e09242f32 100644
--- a/docs/java-rest/high-level/supported-apis.asciidoc
+++ b/docs/java-rest/high-level/supported-apis.asciidoc
@@ -95,6 +95,7 @@ include::indices/update_aliases.asciidoc[]
include::indices/exists_alias.asciidoc[]
include::indices/put_settings.asciidoc[]
include::indices/get_settings.asciidoc[]
+include::indices/put_template.asciidoc[]
== Cluster APIs
diff --git a/docs/plugins/analysis-nori.asciidoc b/docs/plugins/analysis-nori.asciidoc
index 9e6b8edcfa745..dd47ca819a7b2 100644
--- a/docs/plugins/analysis-nori.asciidoc
+++ b/docs/plugins/analysis-nori.asciidoc
@@ -278,7 +278,8 @@ It accepts the following setting:
and defaults to:
-```
+[source,js]
+--------------------------------------------------
"stoptags": [
"E",
"IC",
@@ -288,7 +289,8 @@ and defaults to:
"XPN", "XSA", "XSN", "XSV",
"UNA", "NA", "VSV"
]
-```
+--------------------------------------------------
+// NOTCONSOLE
For example:
diff --git a/docs/plugins/api.asciidoc b/docs/plugins/api.asciidoc
index a2fbc5165ac94..4dffb9608821f 100644
--- a/docs/plugins/api.asciidoc
+++ b/docs/plugins/api.asciidoc
@@ -19,6 +19,9 @@ A number of plugins have been contributed by our community:
* https://github.com/YannBrrd/elasticsearch-entity-resolution[Entity Resolution Plugin]:
Uses http://github.com/larsga/Duke[Duke] for duplication detection (by Yann Barraud)
+
+* https://github.com/zentity-io/zentity[Entity Resolution Plugin] (https://zentity.io[zentity]):
+ Real-time entity resolution with pure Elasticsearch (by Dave Moore)
* https://github.com/NLPchina/elasticsearch-sql/[SQL language Plugin]:
Allows Elasticsearch to be queried with SQL (by nlpcn)
diff --git a/docs/reference/indices/forcemerge.asciidoc b/docs/reference/indices/forcemerge.asciidoc
index 26baf214176d1..fc56386a2773c 100644
--- a/docs/reference/indices/forcemerge.asciidoc
+++ b/docs/reference/indices/forcemerge.asciidoc
@@ -38,6 +38,12 @@ deletes. Defaults to `false`. Note that this won't override the
`flush`:: Should a flush be performed after the forced merge. Defaults to
`true`.
+[source,js]
+--------------------------------------------------
+POST /kimchy/_forcemerge?only_expunge_deletes=false&max_num_segments=100&flush=true
+
+--------------------------------------------------
+
[float]
[[forcemerge-multi-index]]
=== Multi Index
diff --git a/docs/reference/search/request/preference.asciidoc b/docs/reference/search/request/preference.asciidoc
index dbd9055ff8c86..4fd801c5f76e3 100644
--- a/docs/reference/search/request/preference.asciidoc
+++ b/docs/reference/search/request/preference.asciidoc
@@ -2,7 +2,8 @@
=== Preference
Controls a `preference` of which shard copies on which to execute the
-search. By default, the operation is randomized among the available shard copies.
+search. By default, the operation is randomized among the available shard
+copies, unless allocation awareness is used.
The `preference` is a query string parameter which can be set to:
diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_settings/11_reset.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_settings/11_reset.yml
index bc2dace0e1871..d7bd87cc73a82 100644
--- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_settings/11_reset.yml
+++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_settings/11_reset.yml
@@ -23,10 +23,15 @@ Test reset index settings:
indices.get_settings:
flat_settings: false
- is_false: test-index.settings.index\.refresh_interval
- - do:
- indices.get_settings:
- include_defaults: true
- flat_settings: true
- index: test-index
- - match:
- test-index.defaults.index\.refresh_interval: "1s"
+
+# Disabled until https://github.com/elastic/elasticsearch/pull/29229 is back-ported
+# That PR changed the execution path of index settings default to be on the master
+# until the PR is back-ported the old master will not return default settings.
+#
+# - do:
+# indices.get_settings:
+# include_defaults: true
+# flat_settings: true
+# index: test-index
+# - match:
+# test-index.defaults.index\.refresh_interval: "1s"
diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/template/put/PutIndexTemplateRequest.java b/server/src/main/java/org/elasticsearch/action/admin/indices/template/put/PutIndexTemplateRequest.java
index 8cd1fac6f6fd1..b018e24a565b8 100644
--- a/server/src/main/java/org/elasticsearch/action/admin/indices/template/put/PutIndexTemplateRequest.java
+++ b/server/src/main/java/org/elasticsearch/action/admin/indices/template/put/PutIndexTemplateRequest.java
@@ -39,6 +39,7 @@
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
+import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentHelper;
@@ -58,14 +59,14 @@
import java.util.stream.Collectors;
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;
/**
* A request to create an index template.
*/
-public class PutIndexTemplateRequest extends MasterNodeRequest implements IndicesRequest {
+public class PutIndexTemplateRequest extends MasterNodeRequest implements IndicesRequest, ToXContent {
private static final DeprecationLogger DEPRECATION_LOGGER = new DeprecationLogger(Loggers.getLogger(PutIndexTemplateRequest.class));
@@ -539,4 +540,34 @@ public void writeTo(StreamOutput out) throws IOException {
}
out.writeOptionalVInt(version);
}
+
+ @Override
+ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
+ if (customs.isEmpty() == false) {
+ throw new IllegalArgumentException("Custom data type is no longer supported in index template [" + customs + "]");
+ }
+ builder.field("index_patterns", indexPatterns);
+ builder.field("order", order);
+ if (version != null) {
+ builder.field("version", version);
+ }
+
+ builder.startObject("settings");
+ settings.toXContent(builder, params);
+ builder.endObject();
+
+ builder.startObject("mappings");
+ for (Map.Entry entry : mappings.entrySet()) {
+ Map mapping = XContentHelper.convertToMap(new BytesArray(entry.getValue()), false).v2();
+ builder.field(entry.getKey(), mapping);
+ }
+ builder.endObject();
+
+ builder.startObject("aliases");
+ for (Alias alias : aliases) {
+ alias.toXContent(builder, params);
+ }
+ builder.endObject();
+ return builder;
+ }
}
diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/template/put/PutIndexTemplateResponse.java b/server/src/main/java/org/elasticsearch/action/admin/indices/template/put/PutIndexTemplateResponse.java
index bf6e05a6c7b43..6c8a5291b12d5 100644
--- a/server/src/main/java/org/elasticsearch/action/admin/indices/template/put/PutIndexTemplateResponse.java
+++ b/server/src/main/java/org/elasticsearch/action/admin/indices/template/put/PutIndexTemplateResponse.java
@@ -21,6 +21,8 @@
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
+import org.elasticsearch.common.xcontent.ConstructingObjectParser;
+import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
@@ -47,4 +49,14 @@ public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
writeAcknowledged(out);
}
+
+ private static final ConstructingObjectParser PARSER;
+ static {
+ PARSER = new ConstructingObjectParser<>("put_index_template", true, args -> new PutIndexTemplateResponse((boolean) args[0]));
+ declareAcknowledgedField(PARSER);
+ }
+
+ public static PutIndexTemplateResponse fromXContent(XContentParser parser) {
+ return PARSER.apply(parser, null);
+ }
}
diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/validate/query/TransportValidateQueryAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/validate/query/TransportValidateQueryAction.java
index 5be321734b5db..c90a7428268cd 100644
--- a/server/src/main/java/org/elasticsearch/action/admin/indices/validate/query/TransportValidateQueryAction.java
+++ b/server/src/main/java/org/elasticsearch/action/admin/indices/validate/query/TransportValidateQueryAction.java
@@ -184,7 +184,7 @@ protected ValidateQueryResponse newResponse(ValidateQueryRequest request, Atomic
}
@Override
- protected ShardValidateQueryResponse shardOperation(ShardValidateQueryRequest request) throws IOException {
+ protected ShardValidateQueryResponse shardOperation(ShardValidateQueryRequest request, Task task) throws IOException {
boolean valid;
String explanation = null;
String error = null;
diff --git a/server/src/main/java/org/elasticsearch/action/support/broadcast/TransportBroadcastAction.java b/server/src/main/java/org/elasticsearch/action/support/broadcast/TransportBroadcastAction.java
index 0961ab74c4703..60eaa19eaff63 100644
--- a/server/src/main/java/org/elasticsearch/action/support/broadcast/TransportBroadcastAction.java
+++ b/server/src/main/java/org/elasticsearch/action/support/broadcast/TransportBroadcastAction.java
@@ -84,11 +84,7 @@ protected final void doExecute(Request request, ActionListener listene
protected abstract ShardResponse newShardResponse();
- protected abstract ShardResponse shardOperation(ShardRequest request) throws IOException;
-
- protected ShardResponse shardOperation(ShardRequest request, Task task) throws IOException {
- return shardOperation(request);
- }
+ protected abstract ShardResponse shardOperation(ShardRequest request, Task task) throws IOException;
/**
* Determines the shards this operation will be executed on. The operation is executed once per shard iterator, typically
@@ -284,7 +280,7 @@ class ShardTransportHandler implements TransportRequestHandler {
@Override
public void messageReceived(ShardRequest request, TransportChannel channel, Task task) throws Exception {
- channel.sendResponse(shardOperation(request));
+ channel.sendResponse(shardOperation(request, task));
}
@Override
diff --git a/server/src/main/java/org/elasticsearch/common/blobstore/BlobContainer.java b/server/src/main/java/org/elasticsearch/common/blobstore/BlobContainer.java
index a04c75941e7b6..6b9992e7e4c3a 100644
--- a/server/src/main/java/org/elasticsearch/common/blobstore/BlobContainer.java
+++ b/server/src/main/java/org/elasticsearch/common/blobstore/BlobContainer.java
@@ -75,7 +75,8 @@ public interface BlobContainer {
void writeBlob(String blobName, InputStream inputStream, long blobSize) throws IOException;
/**
- * Deletes a blob with giving name, if the blob exists. If the blob does not exist, this method throws an IOException.
+ * Deletes a blob with giving name, if the blob exists. If the blob does not exist,
+ * this method throws a NoSuchFileException.
*
* @param blobName
* The name of the blob to delete.
@@ -84,6 +85,21 @@ public interface BlobContainer {
*/
void deleteBlob(String blobName) throws IOException;
+ /**
+ * Deletes a blob with giving name, ignoring if the blob does not exist.
+ *
+ * @param blobName
+ * The name of the blob to delete.
+ * @throws IOException if the blob exists but could not be deleted.
+ */
+ default void deleteBlobIgnoringIfNotExists(String blobName) throws IOException {
+ try {
+ deleteBlob(blobName);
+ } catch (final NoSuchFileException ignored) {
+ // This exception is ignored
+ }
+ }
+
/**
* Lists all blobs in the container.
*
diff --git a/server/src/main/java/org/elasticsearch/index/snapshots/blobstore/BlobStoreIndexShardSnapshots.java b/server/src/main/java/org/elasticsearch/index/snapshots/blobstore/BlobStoreIndexShardSnapshots.java
index d25b1eb04866d..34b07932e48ff 100644
--- a/server/src/main/java/org/elasticsearch/index/snapshots/blobstore/BlobStoreIndexShardSnapshots.java
+++ b/server/src/main/java/org/elasticsearch/index/snapshots/blobstore/BlobStoreIndexShardSnapshots.java
@@ -102,13 +102,6 @@ private BlobStoreIndexShardSnapshots(Map files, List snapshots, int fileListGeneration, Map blobs) {
- BlobStoreIndexShardSnapshots newSnapshots = new BlobStoreIndexShardSnapshots(snapshots);
- // delete old index files first
- for (String blobName : blobs.keySet()) {
- if (indexShardSnapshotsFormat.isTempBlobName(blobName) || blobName.startsWith(SNAPSHOT_INDEX_PREFIX)) {
- try {
- blobContainer.deleteBlob(blobName);
- } catch (IOException e) {
- // We cannot delete index file - this is fatal, we cannot continue, otherwise we might end up
- // with references to non-existing files
- throw new IndexShardSnapshotFailedException(shardId, "error deleting index file ["
- + blobName + "] during cleanup", e);
- }
+ protected void finalize(final List snapshots,
+ final int fileListGeneration,
+ final Map blobs,
+ final String reason) {
+ final String indexGeneration = Integer.toString(fileListGeneration);
+ final String currentIndexGen = indexShardSnapshotsFormat.blobName(indexGeneration);
+
+ final BlobStoreIndexShardSnapshots updatedSnapshots = new BlobStoreIndexShardSnapshots(snapshots);
+ try {
+ // If we deleted all snapshots, we don't need to create a new index file
+ if (snapshots.size() > 0) {
+ indexShardSnapshotsFormat.writeAtomic(updatedSnapshots, blobContainer, indexGeneration);
}
- }
- // now go over all the blobs, and if they don't exist in a snapshot, delete them
- for (String blobName : blobs.keySet()) {
- // delete unused files
- if (blobName.startsWith(DATA_BLOB_PREFIX)) {
- if (newSnapshots.findNameFile(BlobStoreIndexShardSnapshot.FileInfo.canonicalName(blobName)) == null) {
+ // Delete old index files
+ for (final String blobName : blobs.keySet()) {
+ if (indexShardSnapshotsFormat.isTempBlobName(blobName) || blobName.startsWith(SNAPSHOT_INDEX_PREFIX)) {
try {
- blobContainer.deleteBlob(blobName);
+ blobContainer.deleteBlobIgnoringIfNotExists(blobName);
} catch (IOException e) {
- // TODO: don't catch and let the user handle it?
- logger.debug(() -> new ParameterizedMessage("[{}] [{}] error deleting blob [{}] during cleanup", snapshotId, shardId, blobName), e);
+ logger.warn(() -> new ParameterizedMessage("[{}][{}] failed to delete index blob [{}] during finalization",
+ snapshotId, shardId, blobName), e);
+ throw e;
}
}
}
- }
- // If we deleted all snapshots - we don't need to create the index file
- if (snapshots.size() > 0) {
- try {
- indexShardSnapshotsFormat.writeAtomic(newSnapshots, blobContainer, Integer.toString(fileListGeneration));
- } catch (IOException e) {
- throw new IndexShardSnapshotFailedException(shardId, "Failed to write file list", e);
+ // Delete all blobs that don't exist in a snapshot
+ for (final String blobName : blobs.keySet()) {
+ if (blobName.startsWith(DATA_BLOB_PREFIX) && (updatedSnapshots.findNameFile(canonicalName(blobName)) == null)) {
+ try {
+ blobContainer.deleteBlobIgnoringIfNotExists(blobName);
+ } catch (IOException e) {
+ logger.warn(() -> new ParameterizedMessage("[{}][{}] failed to delete data blob [{}] during finalization",
+ snapshotId, shardId, blobName), e);
+ }
+ }
}
+ } catch (IOException e) {
+ String message = "Failed to finalize " + reason + " with shard index [" + currentIndexGen + "]";
+ throw new IndexShardSnapshotFailedException(shardId, message, e);
}
}
@@ -1003,7 +1007,7 @@ protected long findLatestFileNameGeneration(Map blobs) {
if (!name.startsWith(DATA_BLOB_PREFIX)) {
continue;
}
- name = BlobStoreIndexShardSnapshot.FileInfo.canonicalName(name);
+ name = canonicalName(name);
try {
long currentGen = Long.parseLong(name.substring(DATA_BLOB_PREFIX.length()), Character.MAX_RADIX);
if (currentGen > generation) {
@@ -1217,7 +1221,7 @@ public void snapshot(final IndexCommit snapshotIndexCommit) {
newSnapshotsList.add(point);
}
// finalize the snapshot and rewrite the snapshot index with the next sequential snapshot index
- finalize(newSnapshotsList, fileListGeneration + 1, blobs);
+ finalize(newSnapshotsList, fileListGeneration + 1, blobs, "snapshot creation [" + snapshotId + "]");
snapshotStatus.moveToDone(System.currentTimeMillis());
}
diff --git a/server/src/test/java/org/elasticsearch/action/admin/indices/create/ShrinkIndexIT.java b/server/src/test/java/org/elasticsearch/action/admin/indices/create/ShrinkIndexIT.java
index e48f151081f62..e4bb197f80ace 100644
--- a/server/src/test/java/org/elasticsearch/action/admin/indices/create/ShrinkIndexIT.java
+++ b/server/src/test/java/org/elasticsearch/action/admin/indices/create/ShrinkIndexIT.java
@@ -83,6 +83,7 @@ protected Collection> nodePlugins() {
return Arrays.asList(InternalSettingsPlugin.class);
}
+ @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/30416")
public void testCreateShrinkIndexToN() {
int[][] possibleShardSplits = new int[][] {{8,4,2}, {9, 3, 1}, {4, 2, 1}, {15,5,1}};
int[] shardSplits = randomFrom(possibleShardSplits);
diff --git a/server/src/test/java/org/elasticsearch/action/admin/indices/template/put/PutIndexTemplateRequestTests.java b/server/src/test/java/org/elasticsearch/action/admin/indices/template/put/PutIndexTemplateRequestTests.java
index 72cbe2bd9ecab..294213452596f 100644
--- a/server/src/test/java/org/elasticsearch/action/admin/indices/template/put/PutIndexTemplateRequestTests.java
+++ b/server/src/test/java/org/elasticsearch/action/admin/indices/template/put/PutIndexTemplateRequestTests.java
@@ -20,10 +20,15 @@
import org.elasticsearch.Version;
import org.elasticsearch.action.ActionRequestValidationException;
+import org.elasticsearch.action.admin.indices.alias.Alias;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesArray;
+import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.xcontent.ToXContent;
+import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.common.xcontent.yaml.YamlXContent;
@@ -35,6 +40,7 @@
import java.util.Collections;
import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.core.Is.is;
@@ -131,4 +137,52 @@ public void testValidateErrorMessage() throws Exception {
assertThat(noError, is(nullValue()));
}
+ private PutIndexTemplateRequest randomPutIndexTemplateRequest() throws IOException {
+ PutIndexTemplateRequest request = new PutIndexTemplateRequest();
+ request.name("test");
+ if (randomBoolean()){
+ request.version(randomInt());
+ }
+ if (randomBoolean()){
+ request.order(randomInt());
+ }
+ request.patterns(Arrays.asList(generateRandomStringArray(20, 100, false, false)));
+ int numAlias = between(0, 5);
+ for (int i = 0; i < numAlias; i++) {
+ Alias alias = new Alias(randomRealisticUnicodeOfLengthBetween(1, 10));
+ if (randomBoolean()) {
+ alias.indexRouting(randomRealisticUnicodeOfLengthBetween(1, 10));
+ }
+ if (randomBoolean()) {
+ alias.searchRouting(randomRealisticUnicodeOfLengthBetween(1, 10));
+ }
+ request.alias(alias);
+ }
+ if (randomBoolean()) {
+ request.mapping("doc", XContentFactory.jsonBuilder().startObject()
+ .startObject("doc").startObject("properties")
+ .startObject("field-" + randomInt()).field("type", randomFrom("keyword", "text")).endObject()
+ .endObject().endObject().endObject());
+ }
+ if (randomBoolean()){
+ request.settings(Settings.builder().put("setting1", randomLong()).put("setting2", randomTimeValue()).build());
+ }
+ return request;
+ }
+
+ public void testFromToXContentPutTemplateRequest() throws Exception {
+ for (int i = 0; i < 10; i++) {
+ PutIndexTemplateRequest expected = randomPutIndexTemplateRequest();
+ XContentType xContentType = randomFrom(XContentType.values());
+ BytesReference shuffled = toShuffledXContent(expected, xContentType, ToXContent.EMPTY_PARAMS, randomBoolean());
+ PutIndexTemplateRequest parsed = new PutIndexTemplateRequest().source(shuffled, xContentType);
+ assertNotSame(expected, parsed);
+ assertThat(parsed.version(), equalTo(expected.version()));
+ assertThat(parsed.order(), equalTo(expected.order()));
+ assertThat(parsed.patterns(), equalTo(expected.patterns()));
+ assertThat(parsed.aliases(), equalTo(expected.aliases()));
+ assertThat(parsed.mappings(), equalTo(expected.mappings()));
+ assertThat(parsed.settings(), equalTo(expected.settings()));
+ }
+ }
}
diff --git a/server/src/test/java/org/elasticsearch/discovery/ClusterDisruptionIT.java b/server/src/test/java/org/elasticsearch/discovery/ClusterDisruptionIT.java
index 2998ec8a6ba66..fab38a2b73b4a 100644
--- a/server/src/test/java/org/elasticsearch/discovery/ClusterDisruptionIT.java
+++ b/server/src/test/java/org/elasticsearch/discovery/ClusterDisruptionIT.java
@@ -22,7 +22,6 @@
import org.apache.logging.log4j.message.ParameterizedMessage;
import org.apache.lucene.index.CorruptIndexException;
import org.elasticsearch.ElasticsearchException;
-import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.NoShardAvailableActionException;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequestBuilder;
@@ -61,10 +60,13 @@
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
+import static org.elasticsearch.action.DocWriteResponse.Result.CREATED;
+import static org.elasticsearch.action.DocWriteResponse.Result.UPDATED;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.isOneOf;
import static org.hamcrest.Matchers.not;
/**
@@ -135,7 +137,7 @@ public void testAckedIndexing() throws Exception {
.setSource("{}", XContentType.JSON)
.setTimeout(timeout)
.get(timeout);
- assertEquals(DocWriteResponse.Result.CREATED, response.getResult());
+ assertThat(response.getResult(), isOneOf(CREATED, UPDATED));
ackedDocs.put(id, node);
logger.trace("[{}] indexed id [{}] through node [{}], response [{}]", name, id, node, response);
} catch (ElasticsearchException e) {
diff --git a/test/framework/src/main/java/org/elasticsearch/repositories/ESBlobStoreContainerTestCase.java b/test/framework/src/main/java/org/elasticsearch/repositories/ESBlobStoreContainerTestCase.java
index 18be4e9437770..8aff12edc8a53 100644
--- a/test/framework/src/main/java/org/elasticsearch/repositories/ESBlobStoreContainerTestCase.java
+++ b/test/framework/src/main/java/org/elasticsearch/repositories/ESBlobStoreContainerTestCase.java
@@ -29,13 +29,14 @@
import java.io.IOException;
import java.io.InputStream;
+import java.nio.file.NoSuchFileException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
-import static org.elasticsearch.repositories.ESBlobStoreTestCase.writeRandomBlob;
import static org.elasticsearch.repositories.ESBlobStoreTestCase.randomBytes;
import static org.elasticsearch.repositories.ESBlobStoreTestCase.readBlobFully;
+import static org.elasticsearch.repositories.ESBlobStoreTestCase.writeRandomBlob;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.notNullValue;
@@ -116,7 +117,7 @@ public void testDeleteBlob() throws IOException {
try (BlobStore store = newBlobStore()) {
final String blobName = "foobar";
final BlobContainer container = store.blobContainer(new BlobPath());
- expectThrows(IOException.class, () -> container.deleteBlob(blobName));
+ expectThrows(NoSuchFileException.class, () -> container.deleteBlob(blobName));
byte[] data = randomBytes(randomIntBetween(10, scaledRandomIntBetween(1024, 1 << 16)));
final BytesArray bytesArray = new BytesArray(data);
@@ -124,7 +125,19 @@ public void testDeleteBlob() throws IOException {
container.deleteBlob(blobName); // should not raise
// blob deleted, so should raise again
- expectThrows(IOException.class, () -> container.deleteBlob(blobName));
+ expectThrows(NoSuchFileException.class, () -> container.deleteBlob(blobName));
+ }
+ }
+
+ public void testDeleteBlobIgnoringIfNotExists() throws IOException {
+ try (BlobStore store = newBlobStore()) {
+ BlobPath blobPath = new BlobPath();
+ if (randomBoolean()) {
+ blobPath = blobPath.add(randomAlphaOfLengthBetween(1, 10));
+ }
+
+ final BlobContainer container = store.blobContainer(blobPath);
+ container.deleteBlobIgnoringIfNotExists("does_not_exist");
}
}
diff --git a/test/framework/src/main/java/org/elasticsearch/test/tasks/MockTaskManager.java b/test/framework/src/main/java/org/elasticsearch/test/tasks/MockTaskManager.java
index dec204537b917..41cdaefe03575 100644
--- a/test/framework/src/main/java/org/elasticsearch/test/tasks/MockTaskManager.java
+++ b/test/framework/src/main/java/org/elasticsearch/test/tasks/MockTaskManager.java
@@ -57,7 +57,7 @@ public Task register(String type, String action, TaskAwareRequest request) {
} catch (Exception e) {
logger.warn(
(Supplier>) () -> new ParameterizedMessage(
- "failed to notify task manager listener about unregistering the task with id {}",
+ "failed to notify task manager listener about registering the task with id {}",
task.getId()),
e);
}
diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherLifeCycleService.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherLifeCycleService.java
index eef9e019b7a7e..620d575fc802c 100644
--- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherLifeCycleService.java
+++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherLifeCycleService.java
@@ -110,8 +110,7 @@ public void clusterChanged(ClusterChangedEvent event) {
// if this is not a data node, we need to start it ourselves possibly
if (event.state().nodes().getLocalNode().isDataNode() == false &&
isWatcherStoppedManually == false && this.state.get() == WatcherState.STOPPED) {
- watcherService.start(event.state());
- this.state.set(WatcherState.STARTED);
+ watcherService.start(event.state(), () -> this.state.set(WatcherState.STARTED));
return;
}
@@ -157,8 +156,8 @@ public void clusterChanged(ClusterChangedEvent event) {
if (state.get() == WatcherState.STARTED) {
watcherService.reload(event.state(), "new local watcher shard allocation ids");
} else if (state.get() == WatcherState.STOPPED) {
- watcherService.start(event.state());
- this.state.set(WatcherState.STARTED);
+ this.state.set(WatcherState.STARTING);
+ watcherService.start(event.state(), () -> this.state.set(WatcherState.STARTED));
}
} else {
clearAllocationIds();
diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherService.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherService.java
index dcfb713a66580..49915674fe9e2 100644
--- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherService.java
+++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherService.java
@@ -183,23 +183,40 @@ void reload(ClusterState state, String reason) {
// by checking the cluster state version before and after loading the watches we can potentially just exit without applying the
// changes
processedClusterStateVersion.set(state.getVersion());
- pauseExecution(reason);
triggerService.pauseExecution();
+ int cancelledTaskCount = executionService.clearExecutionsAndQueue();
+ logger.info("reloading watcher, reason [{}], cancelled [{}] queued tasks", reason, cancelledTaskCount);
executor.execute(wrapWatcherService(() -> reloadInner(state, reason, false),
e -> logger.error("error reloading watcher", e)));
}
- public void start(ClusterState state) {
+ /**
+ * start the watcher service, load watches in the background
+ *
+ * @param state the current cluster state
+ * @param postWatchesLoadedCallback the callback to be triggered, when watches where loaded successfully
+ */
+ public void start(ClusterState state, Runnable postWatchesLoadedCallback) {
+ executionService.unPause();
processedClusterStateVersion.set(state.getVersion());
- executor.execute(wrapWatcherService(() -> reloadInner(state, "starting", true),
+ executor.execute(wrapWatcherService(() -> {
+ if (reloadInner(state, "starting", true)) {
+ postWatchesLoadedCallback.run();
+ }
+ },
e -> logger.error("error starting watcher", e)));
}
/**
- * reload the watches and start scheduling them
+ * reload watches and start scheduling them
+ *
+ * @param state the current cluster state
+ * @param reason the reason for reloading, will be logged
+ * @param loadTriggeredWatches should triggered watches be loaded in this run, not needed for reloading, only for starting
+ * @return true if no other loading of a newer cluster state happened in parallel, false otherwise
*/
- private synchronized void reloadInner(ClusterState state, String reason, boolean loadTriggeredWatches) {
+ private synchronized boolean reloadInner(ClusterState state, String reason, boolean loadTriggeredWatches) {
// exit early if another thread has come in between
if (processedClusterStateVersion.get() != state.getVersion()) {
logger.debug("watch service has not been reloaded for state [{}], another reload for state [{}] in progress",
@@ -221,9 +238,11 @@ private synchronized void reloadInner(ClusterState state, String reason, boolean
executionService.executeTriggeredWatches(triggeredWatches);
}
logger.debug("watch service has been reloaded, reason [{}]", reason);
+ return true;
} else {
logger.debug("watch service has not been reloaded for state [{}], another reload for state [{}] in progress",
state.getVersion(), processedClusterStateVersion.get());
+ return false;
}
}
diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/execution/ExecutionService.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/execution/ExecutionService.java
index 6901adb0a6937..7b77afb225e4b 100644
--- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/execution/ExecutionService.java
+++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/execution/ExecutionService.java
@@ -121,11 +121,25 @@ public void unPause() {
}
/**
- * Pause the execution of the watcher executor
+ * Pause the execution of the watcher executor, and empty the state.
+ * Pausing means, that no new watch executions will be done unless this pausing is explicitely unset.
+ * This is important when watcher is stopped, so that scheduled watches do not accidentally get executed.
+ * This should not be used when we need to reload watcher based on some cluster state changes, then just calling
+ * {@link #clearExecutionsAndQueue()} is the way to go
+ *
* @return the number of tasks that have been removed
*/
public int pause() {
paused.set(true);
+ return clearExecutionsAndQueue();
+ }
+
+ /**
+ * Empty the currently queued tasks and wait for current executions to finish.
+ *
+ * @return the number of tasks that have been removed
+ */
+ public int clearExecutionsAndQueue() {
int cancelledTaskCount = executor.queue().drainTo(new ArrayList<>());
this.clearExecutions();
return cancelledTaskCount;
diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherLifeCycleServiceTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherLifeCycleServiceTests.java
index 316cb722f2f1e..700901753d4a1 100644
--- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherLifeCycleServiceTests.java
+++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherLifeCycleServiceTests.java
@@ -180,7 +180,7 @@ public void testManualStartStop() {
reset(watcherService);
when(watcherService.validate(clusterState)).thenReturn(true);
lifeCycleService.clusterChanged(new ClusterChangedEvent("any", clusterState, stoppedClusterState));
- verify(watcherService, times(1)).start(eq(clusterState));
+ verify(watcherService, times(1)).start(eq(clusterState), anyObject());
// no change, keep going
reset(watcherService);
@@ -423,7 +423,7 @@ public void testWatcherServiceDoesNotStartIfIndexTemplatesAreMissing() throws Ex
when(watcherService.validate(eq(state))).thenReturn(true);
lifeCycleService.clusterChanged(new ClusterChangedEvent("any", state, state));
- verify(watcherService, times(0)).start(any(ClusterState.class));
+ verify(watcherService, times(0)).start(any(ClusterState.class), anyObject());
}
public void testWatcherStopsWhenMasterNodeIsMissing() {
diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherServiceTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherServiceTests.java
index 5f815170215d3..73f9271e3efda 100644
--- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherServiceTests.java
+++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherServiceTests.java
@@ -68,6 +68,7 @@
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -199,7 +200,7 @@ void stopExecutor() {
when(client.clearScroll(any(ClearScrollRequest.class))).thenReturn(clearScrollFuture);
clearScrollFuture.onResponse(new ClearScrollResponse(true, 1));
- service.start(clusterState);
+ service.start(clusterState, () -> {});
ArgumentCaptor captor = ArgumentCaptor.forClass(List.class);
verify(triggerService).start(captor.capture());
@@ -238,6 +239,27 @@ void stopExecutor() {
verify(triggerEngine).pauseExecution();
}
+ // if we have to reload the watcher service, the execution service should not be paused, as this might
+ // result in missing executions
+ public void testReloadingWatcherDoesNotPauseExecutionService() {
+ ExecutionService executionService = mock(ExecutionService.class);
+ TriggerService triggerService = mock(TriggerService.class);
+ WatcherService service = new WatcherService(Settings.EMPTY, triggerService, mock(TriggeredWatchStore.class),
+ executionService, mock(WatchParser.class), mock(Client.class), executorService) {
+ @Override
+ void stopExecutor() {
+ }
+ };
+
+ ClusterState.Builder csBuilder = new ClusterState.Builder(new ClusterName("_name"));
+ csBuilder.metaData(MetaData.builder());
+
+ service.reload(csBuilder.build(), "whatever");
+ verify(executionService).clearExecutionsAndQueue();
+ verify(executionService, never()).pause();
+ verify(triggerService).pauseExecution();
+ }
+
private static DiscoveryNode newNode() {
return new DiscoveryNode("node", ESTestCase.buildNewFakeTransportAddress(), Collections.emptyMap(),
new HashSet<>(asList(DiscoveryNode.Role.values())), Version.CURRENT);
diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/integration/ExecutionVarsIntegrationTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/integration/ExecutionVarsIntegrationTests.java
index 85b0280588a6e..2f69cc95a50ef 100644
--- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/integration/ExecutionVarsIntegrationTests.java
+++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/integration/ExecutionVarsIntegrationTests.java
@@ -23,6 +23,7 @@
import java.util.Map;
import java.util.function.Function;
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
import static org.elasticsearch.xpack.watcher.actions.ActionBuilders.loggingAction;
import static org.elasticsearch.xpack.watcher.client.WatchSourceBuilders.watchBuilder;
import static org.elasticsearch.xpack.watcher.input.InputBuilders.simpleInput;
@@ -36,6 +37,8 @@
public class ExecutionVarsIntegrationTests extends AbstractWatcherIntegrationTestCase {
+ private String watchId = randomAlphaOfLength(20);
+
@Override
protected List> pluginTypes() {
List> types = super.pluginTypes();
@@ -107,7 +110,7 @@ protected Map, Object>> pluginScripts() {
public void testVars() throws Exception {
WatcherClient watcherClient = watcherClient();
- PutWatchResponse putWatchResponse = watcherClient.preparePutWatch("_id").setSource(watchBuilder()
+ PutWatchResponse putWatchResponse = watcherClient.preparePutWatch(watchId).setSource(watchBuilder()
.trigger(schedule(cron("0/1 * * * * ?")))
.input(simpleInput("value", 5))
.condition(new ScriptCondition(
@@ -126,7 +129,7 @@ public void testVars() throws Exception {
assertThat(putWatchResponse.isCreated(), is(true));
- timeWarp().trigger("_id");
+ timeWarp().trigger(watchId);
flush();
refresh();
@@ -135,11 +138,11 @@ public void testVars() throws Exception {
// defaults to match all;
});
- assertThat(searchResponse.getHits().getTotalHits(), is(1L));
+ assertHitCount(searchResponse, 1L);
Map source = searchResponse.getHits().getAt(0).getSourceAsMap();
- assertValue(source, "watch_id", is("_id"));
+ assertValue(source, "watch_id", is(watchId));
assertValue(source, "state", is("executed"));
// we don't store the computed vars in history
@@ -171,7 +174,7 @@ public void testVars() throws Exception {
public void testVarsManual() throws Exception {
WatcherClient watcherClient = watcherClient();
- PutWatchResponse putWatchResponse = watcherClient.preparePutWatch("_id").setSource(watchBuilder()
+ PutWatchResponse putWatchResponse = watcherClient.preparePutWatch(watchId).setSource(watchBuilder()
.trigger(schedule(cron("0/1 * * * * ? 2020")))
.input(simpleInput("value", 5))
.condition(new ScriptCondition(
@@ -193,13 +196,13 @@ public void testVarsManual() throws Exception {
boolean debug = randomBoolean();
ExecuteWatchResponse executeWatchResponse = watcherClient
- .prepareExecuteWatch("_id")
+ .prepareExecuteWatch(watchId)
.setDebug(debug)
.get();
assertThat(executeWatchResponse.getRecordId(), notNullValue());
XContentSource source = executeWatchResponse.getRecordSource();
- assertValue(source, "watch_id", is("_id"));
+ assertValue(source, "watch_id", is(watchId));
assertValue(source, "state", is("executed"));
if (debug) {