FullClusterRestartIT.java 56 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113
  1. /*
  2. * Licensed to Elasticsearch under one or more contributor
  3. * license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright
  5. * ownership. Elasticsearch licenses this file to you under
  6. * the Apache License, Version 2.0 (the "License"); you may
  7. * not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. package org.elasticsearch.upgrades;
  20. import org.apache.http.util.EntityUtils;
  21. import org.elasticsearch.Version;
  22. import org.elasticsearch.client.Request;
  23. import org.elasticsearch.client.Response;
  24. import org.elasticsearch.client.ResponseException;
  25. import org.elasticsearch.client.RestClient;
  26. import org.elasticsearch.cluster.metadata.IndexMetaData;
  27. import org.elasticsearch.common.Booleans;
  28. import org.elasticsearch.common.CheckedFunction;
  29. import org.elasticsearch.common.Strings;
  30. import org.elasticsearch.common.settings.Settings;
  31. import org.elasticsearch.common.xcontent.XContentBuilder;
  32. import org.elasticsearch.common.xcontent.json.JsonXContent;
  33. import org.elasticsearch.common.xcontent.support.XContentMapValues;
  34. import org.elasticsearch.test.NotEqualMessageBuilder;
  35. import org.elasticsearch.test.rest.ESRestTestCase;
  36. import org.elasticsearch.test.rest.yaml.ObjectPath;
  37. import org.junit.Before;
  38. import java.io.IOException;
  39. import java.util.ArrayList;
  40. import java.util.Base64;
  41. import java.util.HashMap;
  42. import java.util.HashSet;
  43. import java.util.List;
  44. import java.util.Locale;
  45. import java.util.Map;
  46. import java.util.Set;
  47. import java.util.regex.Matcher;
  48. import java.util.regex.Pattern;
  49. import static java.util.Collections.emptyMap;
  50. import static java.util.Collections.singletonList;
  51. import static java.util.Collections.singletonMap;
  52. import static org.elasticsearch.cluster.routing.UnassignedInfo.INDEX_DELAYED_NODE_LEFT_TIMEOUT_SETTING;
  53. import static org.elasticsearch.cluster.routing.allocation.decider.MaxRetryAllocationDecider.SETTING_ALLOCATION_MAX_RETRY;
  54. import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
  55. import static org.hamcrest.Matchers.containsString;
  56. import static org.hamcrest.Matchers.equalTo;
  57. import static org.hamcrest.Matchers.greaterThan;
  58. import static org.hamcrest.Matchers.notNullValue;
  59. /**
  60. * Tests to run before and after a full cluster restart. This is run twice,
  61. * one with {@code tests.is_old_cluster} set to {@code true} against a cluster
  62. * of an older version. The cluster is shutdown and a cluster of the new
  63. * version is started with the same data directories and then this is rerun
  64. * with {@code tests.is_old_cluster} set to {@code false}.
  65. */
  66. public class FullClusterRestartIT extends AbstractFullClusterRestartTestCase {
  67. private final boolean supportsLenientBooleans = getOldClusterVersion().before(Version.V_6_0_0_alpha1);
  68. private static final Version VERSION_5_1_0_UNRELEASED = Version.fromString("5.1.0");
  69. private String index;
  70. @Before
  71. public void setIndex() {
  72. index = getTestName().toLowerCase(Locale.ROOT);
  73. }
  74. public void testSearch() throws Exception {
  75. int count;
  76. if (isRunningAgainstOldCluster()) {
  77. XContentBuilder mappingsAndSettings = jsonBuilder();
  78. mappingsAndSettings.startObject();
  79. {
  80. mappingsAndSettings.startObject("settings");
  81. mappingsAndSettings.field("number_of_shards", 1);
  82. mappingsAndSettings.field("number_of_replicas", 0);
  83. mappingsAndSettings.endObject();
  84. }
  85. {
  86. mappingsAndSettings.startObject("mappings");
  87. mappingsAndSettings.startObject("doc");
  88. mappingsAndSettings.startObject("properties");
  89. {
  90. mappingsAndSettings.startObject("string");
  91. mappingsAndSettings.field("type", "text");
  92. mappingsAndSettings.endObject();
  93. }
  94. {
  95. mappingsAndSettings.startObject("dots_in_field_names");
  96. mappingsAndSettings.field("type", "text");
  97. mappingsAndSettings.endObject();
  98. }
  99. {
  100. mappingsAndSettings.startObject("binary");
  101. mappingsAndSettings.field("type", "binary");
  102. mappingsAndSettings.field("store", "true");
  103. mappingsAndSettings.endObject();
  104. }
  105. mappingsAndSettings.endObject();
  106. mappingsAndSettings.endObject();
  107. mappingsAndSettings.endObject();
  108. }
  109. mappingsAndSettings.endObject();
  110. Request createIndex = new Request("PUT", "/" + index);
  111. createIndex.setJsonEntity(Strings.toString(mappingsAndSettings));
  112. client().performRequest(createIndex);
  113. count = randomIntBetween(2000, 3000);
  114. byte[] randomByteArray = new byte[16];
  115. random().nextBytes(randomByteArray);
  116. indexRandomDocuments(count, true, true, i -> {
  117. return JsonXContent.contentBuilder().startObject()
  118. .field("string", randomAlphaOfLength(10))
  119. .field("int", randomInt(100))
  120. .field("float", randomFloat())
  121. // be sure to create a "proper" boolean (True, False) for the first document so that automapping is correct
  122. .field("bool", i > 0 && supportsLenientBooleans ? randomLenientBoolean() : randomBoolean())
  123. .field("field.with.dots", randomAlphaOfLength(10))
  124. .field("binary", Base64.getEncoder().encodeToString(randomByteArray))
  125. .endObject();
  126. });
  127. refresh();
  128. } else {
  129. count = countOfIndexedRandomDocuments();
  130. }
  131. ensureGreenLongWait(index);
  132. assertBasicSearchWorks(count);
  133. assertAllSearchWorks(count);
  134. assertBasicAggregationWorks();
  135. assertRealtimeGetWorks();
  136. assertStoredBinaryFields(count);
  137. }
  138. public void testNewReplicasWork() throws Exception {
  139. if (isRunningAgainstOldCluster()) {
  140. XContentBuilder mappingsAndSettings = jsonBuilder();
  141. mappingsAndSettings.startObject();
  142. {
  143. mappingsAndSettings.startObject("settings");
  144. mappingsAndSettings.field("number_of_shards", 1);
  145. mappingsAndSettings.field("number_of_replicas", 0);
  146. mappingsAndSettings.endObject();
  147. }
  148. {
  149. mappingsAndSettings.startObject("mappings");
  150. mappingsAndSettings.startObject("doc");
  151. mappingsAndSettings.startObject("properties");
  152. {
  153. mappingsAndSettings.startObject("field");
  154. mappingsAndSettings.field("type", "text");
  155. mappingsAndSettings.endObject();
  156. }
  157. mappingsAndSettings.endObject();
  158. mappingsAndSettings.endObject();
  159. mappingsAndSettings.endObject();
  160. }
  161. mappingsAndSettings.endObject();
  162. Request createIndex = new Request("PUT", "/" + index);
  163. createIndex.setJsonEntity(Strings.toString(mappingsAndSettings));
  164. client().performRequest(createIndex);
  165. int numDocs = randomIntBetween(2000, 3000);
  166. indexRandomDocuments(numDocs, true, false, i -> {
  167. return JsonXContent.contentBuilder().startObject()
  168. .field("field", "value")
  169. .endObject();
  170. });
  171. logger.info("Refreshing [{}]", index);
  172. client().performRequest(new Request("POST", "/" + index + "/_refresh"));
  173. } else {
  174. final int numReplicas = 1;
  175. final long startTime = System.currentTimeMillis();
  176. logger.debug("--> creating [{}] replicas for index [{}]", numReplicas, index);
  177. Request setNumberOfReplicas = new Request("PUT", "/" + index + "/_settings");
  178. setNumberOfReplicas.setJsonEntity("{ \"index\": { \"number_of_replicas\" : " + numReplicas + " }}");
  179. Response response = client().performRequest(setNumberOfReplicas);
  180. ensureGreenLongWait(index);
  181. logger.debug("--> index [{}] is green, took [{}] ms", index, (System.currentTimeMillis() - startTime));
  182. Map<String, Object> recoverRsp = entityAsMap(client().performRequest(new Request("GET", "/" + index + "/_recovery")));
  183. logger.debug("--> recovery status:\n{}", recoverRsp);
  184. Set<Integer> counts = new HashSet<>();
  185. for (String node : dataNodes(index, client())) {
  186. Request search = new Request("GET", "/" + index + "/_search");
  187. search.addParameter("preference", "_only_nodes:" + node);
  188. Map<String, Object> responseBody = entityAsMap(client().performRequest(search));
  189. assertNoFailures(responseBody);
  190. int hits = (int) XContentMapValues.extractValue("hits.total", responseBody);
  191. counts.add(hits);
  192. }
  193. assertEquals("All nodes should have a consistent number of documents", 1, counts.size());
  194. }
  195. }
  196. /**
  197. * Search on an alias that contains illegal characters that would prevent it from being created after 5.1.0. It should still be
  198. * search-able though.
  199. */
  200. public void testAliasWithBadName() throws Exception {
  201. assumeTrue("Can only test bad alias name if old cluster is on 5.1.0 or before",
  202. getOldClusterVersion().before(VERSION_5_1_0_UNRELEASED));
  203. int count;
  204. if (isRunningAgainstOldCluster()) {
  205. XContentBuilder mappingsAndSettings = jsonBuilder();
  206. mappingsAndSettings.startObject();
  207. {
  208. mappingsAndSettings.startObject("settings");
  209. mappingsAndSettings.field("number_of_shards", 1);
  210. mappingsAndSettings.field("number_of_replicas", 0);
  211. mappingsAndSettings.endObject();
  212. }
  213. {
  214. mappingsAndSettings.startObject("mappings");
  215. mappingsAndSettings.startObject("doc");
  216. mappingsAndSettings.startObject("properties");
  217. {
  218. mappingsAndSettings.startObject("key");
  219. mappingsAndSettings.field("type", "keyword");
  220. mappingsAndSettings.endObject();
  221. }
  222. mappingsAndSettings.endObject();
  223. mappingsAndSettings.endObject();
  224. mappingsAndSettings.endObject();
  225. }
  226. mappingsAndSettings.endObject();
  227. Request createIndex = new Request("PUT", "/" + index);
  228. createIndex.setJsonEntity(Strings.toString(mappingsAndSettings));
  229. client().performRequest(createIndex);
  230. String aliasName = "%23" + index; // %23 == #
  231. client().performRequest(new Request("PUT", "/" + index + "/_alias/" + aliasName));
  232. Response response = client().performRequest(new Request("HEAD", "/" + index + "/_alias/" + aliasName));
  233. assertEquals(200, response.getStatusLine().getStatusCode());
  234. count = randomIntBetween(32, 128);
  235. indexRandomDocuments(count, true, true, i -> {
  236. return JsonXContent.contentBuilder().startObject()
  237. .field("key", "value")
  238. .endObject();
  239. });
  240. refresh();
  241. } else {
  242. count = countOfIndexedRandomDocuments();
  243. }
  244. Request request = new Request("GET", "/_cluster/state");
  245. request.addParameter("metric", "metadata");
  246. logger.error("clusterState=" + entityAsMap(client().performRequest(request)));
  247. // We can read from the alias just like we can read from the index.
  248. String aliasName = "%23" + index; // %23 == #
  249. Map<String, Object> searchRsp = entityAsMap(client().performRequest(new Request("GET", "/" + aliasName + "/_search")));
  250. int totalHits = (int) XContentMapValues.extractValue("hits.total", searchRsp);
  251. assertEquals(count, totalHits);
  252. if (isRunningAgainstOldCluster() == false) {
  253. // We can remove the alias.
  254. Response response = client().performRequest(new Request("DELETE", "/" + index + "/_alias/" + aliasName));
  255. assertEquals(200, response.getStatusLine().getStatusCode());
  256. // and check that it is gone:
  257. response = client().performRequest(new Request("HEAD", "/" + index + "/_alias/" + aliasName));
  258. assertEquals(404, response.getStatusLine().getStatusCode());
  259. }
  260. }
  261. public void testClusterState() throws Exception {
  262. if (isRunningAgainstOldCluster()) {
  263. XContentBuilder mappingsAndSettings = jsonBuilder();
  264. mappingsAndSettings.startObject();
  265. mappingsAndSettings.field("template", index);
  266. {
  267. mappingsAndSettings.startObject("settings");
  268. mappingsAndSettings.field("number_of_shards", 1);
  269. mappingsAndSettings.field("number_of_replicas", 0);
  270. mappingsAndSettings.endObject();
  271. }
  272. mappingsAndSettings.endObject();
  273. Request createTemplate = new Request("PUT", "/_template/template_1");
  274. createTemplate.setJsonEntity(Strings.toString(mappingsAndSettings));
  275. client().performRequest(createTemplate);
  276. client().performRequest(new Request("PUT", "/" + index));
  277. }
  278. // verifying if we can still read some properties from cluster state api:
  279. Map<String, Object> clusterState = entityAsMap(client().performRequest(new Request("GET", "/_cluster/state")));
  280. // Check some global properties:
  281. String clusterName = (String) clusterState.get("cluster_name");
  282. assertEquals("full-cluster-restart", clusterName);
  283. String numberOfShards = (String) XContentMapValues.extractValue(
  284. "metadata.templates.template_1.settings.index.number_of_shards", clusterState);
  285. assertEquals("1", numberOfShards);
  286. String numberOfReplicas = (String) XContentMapValues.extractValue(
  287. "metadata.templates.template_1.settings.index.number_of_replicas", clusterState);
  288. assertEquals("0", numberOfReplicas);
  289. // Check some index properties:
  290. numberOfShards = (String) XContentMapValues.extractValue("metadata.indices." + index +
  291. ".settings.index.number_of_shards", clusterState);
  292. assertEquals("1", numberOfShards);
  293. numberOfReplicas = (String) XContentMapValues.extractValue("metadata.indices." + index +
  294. ".settings.index.number_of_replicas", clusterState);
  295. assertEquals("0", numberOfReplicas);
  296. Version version = Version.fromId(Integer.valueOf((String) XContentMapValues.extractValue("metadata.indices." + index +
  297. ".settings.index.version.created", clusterState)));
  298. assertEquals(getOldClusterVersion(), version);
  299. }
  300. public void testShrink() throws IOException {
  301. String shrunkenIndex = index + "_shrunk";
  302. int numDocs;
  303. if (isRunningAgainstOldCluster()) {
  304. XContentBuilder mappingsAndSettings = jsonBuilder();
  305. mappingsAndSettings.startObject();
  306. {
  307. mappingsAndSettings.startObject("mappings");
  308. mappingsAndSettings.startObject("doc");
  309. mappingsAndSettings.startObject("properties");
  310. {
  311. mappingsAndSettings.startObject("field");
  312. mappingsAndSettings.field("type", "text");
  313. mappingsAndSettings.endObject();
  314. }
  315. mappingsAndSettings.endObject();
  316. mappingsAndSettings.endObject();
  317. mappingsAndSettings.endObject();
  318. }
  319. mappingsAndSettings.endObject();
  320. Request createIndex = new Request("PUT", "/" + index);
  321. createIndex.setJsonEntity(Strings.toString(mappingsAndSettings));
  322. client().performRequest(createIndex);
  323. numDocs = randomIntBetween(512, 1024);
  324. indexRandomDocuments(numDocs, true, true, i -> {
  325. return JsonXContent.contentBuilder().startObject()
  326. .field("field", "value")
  327. .endObject();
  328. });
  329. ensureGreen(index); // wait for source index to be available on both nodes before starting shrink
  330. Request updateSettingsRequest = new Request("PUT", "/" + index + "/_settings");
  331. updateSettingsRequest.setJsonEntity("{\"settings\": {\"index.blocks.write\": true}}");
  332. client().performRequest(updateSettingsRequest);
  333. Request shrinkIndexRequest = new Request("PUT", "/" + index + "/_shrink/" + shrunkenIndex);
  334. shrinkIndexRequest.setJsonEntity("{\"settings\": {\"index.number_of_shards\": 1}}");
  335. client().performRequest(shrinkIndexRequest);
  336. client().performRequest(new Request("POST", "/_refresh"));
  337. } else {
  338. numDocs = countOfIndexedRandomDocuments();
  339. }
  340. Map<?, ?> response = entityAsMap(client().performRequest(new Request("GET", "/" + index + "/_search")));
  341. assertNoFailures(response);
  342. int totalShards = (int) XContentMapValues.extractValue("_shards.total", response);
  343. assertThat(totalShards, greaterThan(1));
  344. int successfulShards = (int) XContentMapValues.extractValue("_shards.successful", response);
  345. assertEquals(totalShards, successfulShards);
  346. int totalHits = (int) XContentMapValues.extractValue("hits.total", response);
  347. assertEquals(numDocs, totalHits);
  348. response = entityAsMap(client().performRequest(new Request("GET", "/" + shrunkenIndex+ "/_search")));
  349. assertNoFailures(response);
  350. totalShards = (int) XContentMapValues.extractValue("_shards.total", response);
  351. assertEquals(1, totalShards);
  352. successfulShards = (int) XContentMapValues.extractValue("_shards.successful", response);
  353. assertEquals(1, successfulShards);
  354. totalHits = (int) XContentMapValues.extractValue("hits.total", response);
  355. assertEquals(numDocs, totalHits);
  356. }
  357. public void testShrinkAfterUpgrade() throws IOException {
  358. String shrunkenIndex = index + "_shrunk";
  359. int numDocs;
  360. if (isRunningAgainstOldCluster()) {
  361. XContentBuilder mappingsAndSettings = jsonBuilder();
  362. mappingsAndSettings.startObject();
  363. {
  364. mappingsAndSettings.startObject("mappings");
  365. mappingsAndSettings.startObject("doc");
  366. mappingsAndSettings.startObject("properties");
  367. {
  368. mappingsAndSettings.startObject("field");
  369. mappingsAndSettings.field("type", "text");
  370. mappingsAndSettings.endObject();
  371. }
  372. mappingsAndSettings.endObject();
  373. mappingsAndSettings.endObject();
  374. mappingsAndSettings.endObject();
  375. }
  376. mappingsAndSettings.endObject();
  377. Request createIndex = new Request("PUT", "/" + index);
  378. createIndex.setJsonEntity(Strings.toString(mappingsAndSettings));
  379. client().performRequest(createIndex);
  380. numDocs = randomIntBetween(512, 1024);
  381. indexRandomDocuments(numDocs, true, true, i -> {
  382. return JsonXContent.contentBuilder().startObject()
  383. .field("field", "value")
  384. .endObject();
  385. });
  386. } else {
  387. ensureGreen(index); // wait for source index to be available on both nodes before starting shrink
  388. Request updateSettingsRequest = new Request("PUT", "/" + index + "/_settings");
  389. updateSettingsRequest.setJsonEntity("{\"settings\": {\"index.blocks.write\": true}}");
  390. client().performRequest(updateSettingsRequest);
  391. Request shrinkIndexRequest = new Request("PUT", "/" + index + "/_shrink/" + shrunkenIndex);
  392. shrinkIndexRequest.setJsonEntity("{\"settings\": {\"index.number_of_shards\": 1}}");
  393. client().performRequest(shrinkIndexRequest);
  394. numDocs = countOfIndexedRandomDocuments();
  395. }
  396. client().performRequest(new Request("POST", "/_refresh"));
  397. Map<?, ?> response = entityAsMap(client().performRequest(new Request("GET", "/" + index + "/_search")));
  398. assertNoFailures(response);
  399. int totalShards = (int) XContentMapValues.extractValue("_shards.total", response);
  400. assertThat(totalShards, greaterThan(1));
  401. int successfulShards = (int) XContentMapValues.extractValue("_shards.successful", response);
  402. assertEquals(totalShards, successfulShards);
  403. int totalHits = (int) XContentMapValues.extractValue("hits.total", response);
  404. assertEquals(numDocs, totalHits);
  405. if (isRunningAgainstOldCluster() == false) {
  406. response = entityAsMap(client().performRequest(new Request("GET", "/" + shrunkenIndex + "/_search")));
  407. assertNoFailures(response);
  408. totalShards = (int) XContentMapValues.extractValue("_shards.total", response);
  409. assertEquals(1, totalShards);
  410. successfulShards = (int) XContentMapValues.extractValue("_shards.successful", response);
  411. assertEquals(1, successfulShards);
  412. totalHits = (int) XContentMapValues.extractValue("hits.total", response);
  413. assertEquals(numDocs, totalHits);
  414. }
  415. }
  416. /**
  417. * Test upgrading after a rollover. Specifically:
  418. * <ol>
  419. * <li>Create an index with a write alias
  420. * <li>Write some documents to the write alias
  421. * <li>Roll over the index
  422. * <li>Make sure the document count is correct
  423. * <li>Upgrade
  424. * <li>Write some more documents to the write alias
  425. * <li>Make sure the document count is correct
  426. * </ol>
  427. */
  428. public void testRollover() throws IOException {
  429. if (isRunningAgainstOldCluster()) {
  430. Request createIndex = new Request("PUT", "/" + index + "-000001");
  431. createIndex.setJsonEntity("{"
  432. + " \"aliases\": {"
  433. + " \"" + index + "_write\": {}"
  434. + " }"
  435. + "}");
  436. client().performRequest(createIndex);
  437. }
  438. int bulkCount = 10;
  439. StringBuilder bulk = new StringBuilder();
  440. for (int i = 0; i < bulkCount; i++) {
  441. bulk.append("{\"index\":{}}\n");
  442. bulk.append("{\"test\":\"test\"}\n");
  443. }
  444. Request bulkRequest = new Request("POST", "/" + index + "_write/doc/_bulk");
  445. bulkRequest.setJsonEntity(bulk.toString());
  446. bulkRequest.addParameter("refresh", "");
  447. assertThat(EntityUtils.toString(client().performRequest(bulkRequest).getEntity()), containsString("\"errors\":false"));
  448. if (isRunningAgainstOldCluster()) {
  449. Request rolloverRequest = new Request("POST", "/" + index + "_write/_rollover");
  450. rolloverRequest.setJsonEntity("{"
  451. + " \"conditions\": {"
  452. + " \"max_docs\": 5"
  453. + " }"
  454. + "}");
  455. client().performRequest(rolloverRequest);
  456. assertThat(EntityUtils.toString(client().performRequest(new Request("GET", "/_cat/indices?v")).getEntity()),
  457. containsString("testrollover-000002"));
  458. }
  459. Request countRequest = new Request("POST", "/" + index + "-*/_search");
  460. countRequest.addParameter("size", "0");
  461. Map<String, Object> count = entityAsMap(client().performRequest(countRequest));
  462. assertNoFailures(count);
  463. int expectedCount = bulkCount + (isRunningAgainstOldCluster() ? 0 : bulkCount);
  464. assertEquals(expectedCount, (int) XContentMapValues.extractValue("hits.total", count));
  465. }
  466. void assertBasicSearchWorks(int count) throws IOException {
  467. logger.info("--> testing basic search");
  468. {
  469. Map<String, Object> response = entityAsMap(client().performRequest(new Request("GET", "/" + index + "/_search")));
  470. assertNoFailures(response);
  471. int numDocs = (int) XContentMapValues.extractValue("hits.total", response);
  472. logger.info("Found {} in old index", numDocs);
  473. assertEquals(count, numDocs);
  474. }
  475. logger.info("--> testing basic search with sort");
  476. {
  477. Request searchRequest = new Request("GET", "/" + index + "/_search");
  478. searchRequest.setJsonEntity("{ \"sort\": [{ \"int\" : \"asc\" }]}");
  479. Map<String, Object> response = entityAsMap(client().performRequest(searchRequest));
  480. assertNoFailures(response);
  481. assertTotalHits(count, response);
  482. }
  483. logger.info("--> testing exists filter");
  484. {
  485. Request searchRequest = new Request("GET", "/" + index + "/_search");
  486. searchRequest.setJsonEntity("{ \"query\": { \"exists\" : {\"field\": \"string\"} }}");
  487. Map<String, Object> response = entityAsMap(client().performRequest(searchRequest));
  488. assertNoFailures(response);
  489. assertTotalHits(count, response);
  490. }
  491. logger.info("--> testing field with dots in the name");
  492. {
  493. Request searchRequest = new Request("GET", "/" + index + "/_search");
  494. searchRequest.setJsonEntity("{ \"query\": { \"exists\" : {\"field\": \"field.with.dots\"} }}");
  495. Map<String, Object> response = entityAsMap(client().performRequest(searchRequest));
  496. assertNoFailures(response);
  497. assertTotalHits(count, response);
  498. }
  499. }
  500. void assertAllSearchWorks(int count) throws IOException {
  501. logger.info("--> testing _all search");
  502. Map<String, Object> response = entityAsMap(client().performRequest(new Request("GET", "/" + index + "/_search")));
  503. assertNoFailures(response);
  504. assertTotalHits(count, response);
  505. Map<?, ?> bestHit = (Map<?, ?>) ((List<?>) (XContentMapValues.extractValue("hits.hits", response))).get(0);
  506. // Make sure there are payloads and they are taken into account for the score
  507. // the 'string' field has a boost of 4 in the mappings so it should get a payload boost
  508. String stringValue = (String) XContentMapValues.extractValue("_source.string", bestHit);
  509. assertNotNull(stringValue);
  510. String type = (String) bestHit.get("_type");
  511. String id = (String) bestHit.get("_id");
  512. Request explanationRequest = new Request("GET", "/" + index + "/" + type + "/" + id + "/_explain");
  513. explanationRequest.setJsonEntity("{ \"query\": { \"match_all\" : {} }}");
  514. String explanation = toStr(client().performRequest(explanationRequest));
  515. assertFalse("Could not find payload boost in explanation\n" + explanation, explanation.contains("payloadBoost"));
  516. // Make sure the query can run on the whole index
  517. Request searchRequest = new Request("GET", "/" + index + "/_search");
  518. searchRequest.setEntity(explanationRequest.getEntity());
  519. searchRequest.addParameter("explain", "true");
  520. Map<?, ?> matchAllResponse = entityAsMap(client().performRequest(searchRequest));
  521. assertNoFailures(matchAllResponse);
  522. assertTotalHits(count, matchAllResponse);
  523. }
  524. void assertBasicAggregationWorks() throws IOException {
  525. // histogram on a long
  526. Request longHistogramRequest = new Request("GET", "/" + index + "/_search");
  527. longHistogramRequest.setJsonEntity("{ \"aggs\": { \"histo\" : {\"histogram\" : {\"field\": \"int\", \"interval\": 10}} }}");
  528. Map<?, ?> longHistogram = entityAsMap(client().performRequest(longHistogramRequest));
  529. assertNoFailures(longHistogram);
  530. List<?> histoBuckets = (List<?>) XContentMapValues.extractValue("aggregations.histo.buckets", longHistogram);
  531. int histoCount = 0;
  532. for (Object entry : histoBuckets) {
  533. Map<?, ?> bucket = (Map<?, ?>) entry;
  534. histoCount += (Integer) bucket.get("doc_count");
  535. }
  536. assertTotalHits(histoCount, longHistogram);
  537. // terms on a boolean
  538. Request boolTermsRequest = new Request("GET", "/" + index + "/_search");
  539. boolTermsRequest.setJsonEntity("{ \"aggs\": { \"bool_terms\" : {\"terms\" : {\"field\": \"bool\"}} }}");
  540. Map<?, ?> boolTerms = entityAsMap(client().performRequest(boolTermsRequest));
  541. List<?> termsBuckets = (List<?>) XContentMapValues.extractValue("aggregations.bool_terms.buckets", boolTerms);
  542. int termsCount = 0;
  543. for (Object entry : termsBuckets) {
  544. Map<?, ?> bucket = (Map<?, ?>) entry;
  545. termsCount += (Integer) bucket.get("doc_count");
  546. }
  547. assertTotalHits(termsCount, boolTerms);
  548. }
  549. void assertRealtimeGetWorks() throws IOException {
  550. Request disableAutoRefresh = new Request("PUT", "/" + index + "/_settings");
  551. disableAutoRefresh.setJsonEntity("{ \"index\": { \"refresh_interval\" : -1 }}");
  552. client().performRequest(disableAutoRefresh);
  553. Request searchRequest = new Request("GET", "/" + index + "/_search");
  554. searchRequest.setJsonEntity("{ \"query\": { \"match_all\" : {} }}");
  555. Map<?, ?> searchResponse = entityAsMap(client().performRequest(searchRequest));
  556. Map<?, ?> hit = (Map<?, ?>) ((List<?>)(XContentMapValues.extractValue("hits.hits", searchResponse))).get(0);
  557. String docId = (String) hit.get("_id");
  558. Request updateRequest = new Request("POST", "/" + index + "/doc/" + docId + "/_update");
  559. updateRequest.setJsonEntity("{ \"doc\" : { \"foo\": \"bar\"}}");
  560. client().performRequest(updateRequest);
  561. Map<String, Object> getRsp = entityAsMap(client().performRequest(new Request("GET", "/" + index + "/doc/" + docId)));
  562. Map<?, ?> source = (Map<?, ?>) getRsp.get("_source");
  563. assertTrue("doc does not contain 'foo' key: " + source, source.containsKey("foo"));
  564. Request enableAutoRefresh = new Request("PUT", "/" + index + "/_settings");
  565. enableAutoRefresh.setJsonEntity("{ \"index\": { \"refresh_interval\" : \"1s\" }}");
  566. client().performRequest(enableAutoRefresh);
  567. }
  568. void assertStoredBinaryFields(int count) throws Exception {
  569. Request request = new Request("GET", "/" + index + "/_search");
  570. request.setJsonEntity("{ \"query\": { \"match_all\" : {} }, \"size\": 100, \"stored_fields\": \"binary\"}");
  571. Map<String, Object> rsp = entityAsMap(client().performRequest(request));
  572. assertTotalHits(count, rsp);
  573. List<?> hits = (List<?>) XContentMapValues.extractValue("hits.hits", rsp);
  574. assertEquals(100, hits.size());
  575. for (Object hit : hits) {
  576. Map<?, ?> hitRsp = (Map<?, ?>) hit;
  577. List<?> values = (List<?>) XContentMapValues.extractValue("fields.binary", hitRsp);
  578. assertEquals(1, values.size());
  579. String value = (String) values.get(0);
  580. byte[] binaryValue = Base64.getDecoder().decode(value);
  581. assertEquals("Unexpected string length [" + value + "]", 16, binaryValue.length);
  582. }
  583. }
  584. static String toStr(Response response) throws IOException {
  585. return EntityUtils.toString(response.getEntity());
  586. }
  587. static void assertNoFailures(Map<?, ?> response) {
  588. int failed = (int) XContentMapValues.extractValue("_shards.failed", response);
  589. assertEquals(0, failed);
  590. }
  591. static void assertTotalHits(int expectedTotalHits, Map<?, ?> response) {
  592. int actualTotalHits = (Integer) XContentMapValues.extractValue("hits.total", response);
  593. assertEquals(expectedTotalHits, actualTotalHits);
  594. }
  595. /**
  596. * Tests that a single document survives. Super basic smoke test.
  597. */
  598. public void testSingleDoc() throws IOException {
  599. String docLocation = "/" + index + "/doc/1";
  600. String doc = "{\"test\": \"test\"}";
  601. if (isRunningAgainstOldCluster()) {
  602. Request createDoc = new Request("PUT", docLocation);
  603. createDoc.setJsonEntity(doc);
  604. client().performRequest(createDoc);
  605. }
  606. assertThat(toStr(client().performRequest(new Request("GET", docLocation))), containsString(doc));
  607. }
  608. /**
  609. * Tests that a single empty shard index is correctly recovered. Empty shards are often an edge case.
  610. */
  611. public void testEmptyShard() throws IOException {
  612. final String index = "test_empty_shard";
  613. if (isRunningAgainstOldCluster()) {
  614. Settings.Builder settings = Settings.builder()
  615. .put(IndexMetaData.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 1)
  616. .put(IndexMetaData.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 1)
  617. // if the node with the replica is the first to be restarted, while a replica is still recovering
  618. // then delayed allocation will kick in. When the node comes back, the master will search for a copy
  619. // but the recovering copy will be seen as invalid and the cluster health won't return to GREEN
  620. // before timing out
  621. .put(INDEX_DELAYED_NODE_LEFT_TIMEOUT_SETTING.getKey(), "100ms")
  622. .put(SETTING_ALLOCATION_MAX_RETRY.getKey(), "0"); // fail faster
  623. createIndex(index, settings.build());
  624. }
  625. ensureGreen(index);
  626. }
  627. /**
  628. * Tests recovery of an index with or without a translog and the
  629. * statistics we gather about that.
  630. */
  631. public void testRecovery() throws Exception {
  632. int count;
  633. boolean shouldHaveTranslog;
  634. if (isRunningAgainstOldCluster()) {
  635. count = between(200, 300);
  636. /* We've had bugs in the past where we couldn't restore
  637. * an index without a translog so we randomize whether
  638. * or not we have one. */
  639. shouldHaveTranslog = randomBoolean();
  640. indexRandomDocuments(count, true, true, i -> jsonBuilder().startObject().field("field", "value").endObject());
  641. // make sure all recoveries are done
  642. ensureGreen(index);
  643. // Recovering a synced-flush index from 5.x to 6.x might be subtle as a 5.x index commit does not have all 6.x commit tags.
  644. if (randomBoolean()) {
  645. // We have to spin synced-flush requests here because we fire the global checkpoint sync for the last write operation.
  646. // A synced-flush request considers the global checkpoint sync as an going operation because it acquires a shard permit.
  647. assertBusy(() -> {
  648. try {
  649. Response resp = client().performRequest(new Request("POST", index + "/_flush/synced"));
  650. Map<String, Object> result = ObjectPath.createFromResponse(resp).evaluate("_shards");
  651. assertThat(result.get("successful"), equalTo(result.get("total")));
  652. assertThat(result.get("failed"), equalTo(0));
  653. } catch (ResponseException ex) {
  654. throw new AssertionError(ex); // cause assert busy to retry
  655. }
  656. });
  657. } else {
  658. // Explicitly flush so we're sure to have a bunch of documents in the Lucene index
  659. assertOK(client().performRequest(new Request("POST", "/_flush")));
  660. }
  661. if (shouldHaveTranslog) {
  662. // Update a few documents so we are sure to have a translog
  663. indexRandomDocuments(count / 10, false /* Flushing here would invalidate the whole thing....*/, false,
  664. i -> jsonBuilder().startObject().field("field", "value").endObject());
  665. }
  666. saveInfoDocument("should_have_translog", Boolean.toString(shouldHaveTranslog));
  667. } else {
  668. count = countOfIndexedRandomDocuments();
  669. shouldHaveTranslog = Booleans.parseBoolean(loadInfoDocument("should_have_translog"));
  670. }
  671. // Count the documents in the index to make sure we have as many as we put there
  672. Request countRequest = new Request("GET", "/" + index + "/_search");
  673. countRequest.addParameter("size", "0");
  674. String countResponse = toStr(client().performRequest(countRequest));
  675. assertThat(countResponse, containsString("\"total\":" + count));
  676. if (false == isRunningAgainstOldCluster()) {
  677. boolean restoredFromTranslog = false;
  678. boolean foundPrimary = false;
  679. Request recoveryRequest = new Request("GET", "/_cat/recovery/" + index);
  680. recoveryRequest.addParameter("h", "index,shard,type,stage,translog_ops_recovered");
  681. recoveryRequest.addParameter("s", "index,shard,type");
  682. String recoveryResponse = toStr(client().performRequest(recoveryRequest));
  683. for (String line : recoveryResponse.split("\n")) {
  684. // Find the primaries
  685. foundPrimary = true;
  686. if (false == line.contains("done") && line.contains("existing_store")) {
  687. continue;
  688. }
  689. /* Mark if we see a primary that looked like it restored from the translog.
  690. * Not all primaries will look like this all the time because we modify
  691. * random documents when we want there to be a translog and they might
  692. * not be spread around all the shards. */
  693. Matcher m = Pattern.compile("(\\d+)$").matcher(line);
  694. assertTrue(line, m.find());
  695. int translogOps = Integer.parseInt(m.group(1));
  696. if (translogOps > 0) {
  697. restoredFromTranslog = true;
  698. }
  699. }
  700. assertTrue("expected to find a primary but didn't\n" + recoveryResponse, foundPrimary);
  701. assertEquals("mismatch while checking for translog recovery\n" + recoveryResponse, shouldHaveTranslog, restoredFromTranslog);
  702. String currentLuceneVersion = Version.CURRENT.luceneVersion.toString();
  703. String bwcLuceneVersion = getOldClusterVersion().luceneVersion.toString();
  704. if (shouldHaveTranslog && false == currentLuceneVersion.equals(bwcLuceneVersion)) {
  705. int numCurrentVersion = 0;
  706. int numBwcVersion = 0;
  707. Request segmentsRequest = new Request("GET", "/_cat/segments/" + index);
  708. segmentsRequest.addParameter("h", "prirep,shard,index,version");
  709. segmentsRequest.addParameter("s", "prirep,shard,index");
  710. String segmentsResponse = toStr(client().performRequest(segmentsRequest));
  711. for (String line : segmentsResponse.split("\n")) {
  712. if (false == line.startsWith("p")) {
  713. continue;
  714. }
  715. Matcher m = Pattern.compile("(\\d+\\.\\d+\\.\\d+)$").matcher(line);
  716. assertTrue(line, m.find());
  717. String version = m.group(1);
  718. if (currentLuceneVersion.equals(version)) {
  719. numCurrentVersion++;
  720. } else if (bwcLuceneVersion.equals(version)) {
  721. numBwcVersion++;
  722. } else {
  723. fail("expected version to be one of [" + currentLuceneVersion + "," + bwcLuceneVersion + "] but was " + line);
  724. }
  725. }
  726. assertNotEquals("expected at least 1 current segment after translog recovery. segments:\n" + segmentsResponse,
  727. 0, numCurrentVersion);
  728. assertNotEquals("expected at least 1 old segment. segments:\n" + segmentsResponse, 0, numBwcVersion);
  729. }
  730. }
  731. }
  732. /**
  733. * Tests snapshot/restore by creating a snapshot and restoring it. It takes
  734. * a snapshot on the old cluster and restores it on the old cluster as a
  735. * sanity check and on the new cluster as an upgrade test. It also takes a
  736. * snapshot on the new cluster and restores that on the new cluster as a
  737. * test that the repository is ok with containing snapshot from both the
  738. * old and new versions. All of the snapshots include an index, a template,
  739. * and some routing configuration.
  740. */
  741. public void testSnapshotRestore() throws IOException {
  742. int count;
  743. if (isRunningAgainstOldCluster()) {
  744. // Create the index
  745. count = between(200, 300);
  746. indexRandomDocuments(count, true, true, i -> jsonBuilder().startObject().field("field", "value").endObject());
  747. } else {
  748. count = countOfIndexedRandomDocuments();
  749. }
  750. // Refresh the index so the count doesn't fail
  751. refresh();
  752. // Count the documents in the index to make sure we have as many as we put there
  753. Request countRequest = new Request("GET", "/" + index + "/_search");
  754. countRequest.addParameter("size", "0");
  755. String countResponse = toStr(client().performRequest(countRequest));
  756. assertThat(countResponse, containsString("\"total\":" + count));
  757. // Stick a routing attribute into to cluster settings so we can see it after the restore
  758. Request addRoutingSettings = new Request("PUT", "/_cluster/settings");
  759. addRoutingSettings.setJsonEntity(
  760. "{\"persistent\": {\"cluster.routing.allocation.exclude.test_attr\": \"" + getOldClusterVersion() + "\"}}");
  761. client().performRequest(addRoutingSettings);
  762. // Stick a template into the cluster so we can see it after the restore
  763. XContentBuilder templateBuilder = JsonXContent.contentBuilder().startObject();
  764. templateBuilder.field("template", "evil_*"); // Don't confuse other tests by applying the template
  765. templateBuilder.startObject("settings"); {
  766. templateBuilder.field("number_of_shards", 1);
  767. }
  768. templateBuilder.endObject();
  769. templateBuilder.startObject("mappings"); {
  770. templateBuilder.startObject("doc"); {
  771. templateBuilder.startObject("_source"); {
  772. templateBuilder.field("enabled", true);
  773. }
  774. templateBuilder.endObject();
  775. }
  776. templateBuilder.endObject();
  777. }
  778. templateBuilder.endObject();
  779. templateBuilder.startObject("aliases"); {
  780. templateBuilder.startObject("alias1").endObject();
  781. templateBuilder.startObject("alias2"); {
  782. templateBuilder.startObject("filter"); {
  783. templateBuilder.startObject("term"); {
  784. templateBuilder.field("version", isRunningAgainstOldCluster() ? getOldClusterVersion() : Version.CURRENT);
  785. }
  786. templateBuilder.endObject();
  787. }
  788. templateBuilder.endObject();
  789. }
  790. templateBuilder.endObject();
  791. }
  792. templateBuilder.endObject().endObject();
  793. Request createTemplateRequest = new Request("PUT", "/_template/test_template");
  794. createTemplateRequest.setJsonEntity(Strings.toString(templateBuilder));
  795. client().performRequest(createTemplateRequest);
  796. if (isRunningAgainstOldCluster()) {
  797. // Create the repo
  798. XContentBuilder repoConfig = JsonXContent.contentBuilder().startObject(); {
  799. repoConfig.field("type", "fs");
  800. repoConfig.startObject("settings"); {
  801. repoConfig.field("compress", randomBoolean());
  802. repoConfig.field("location", System.getProperty("tests.path.repo"));
  803. }
  804. repoConfig.endObject();
  805. }
  806. repoConfig.endObject();
  807. Request createRepoRequest = new Request("PUT", "/_snapshot/repo");
  808. createRepoRequest.setJsonEntity(Strings.toString(repoConfig));
  809. client().performRequest(createRepoRequest);
  810. }
  811. Request createSnapshot = new Request("PUT", "/_snapshot/repo/" + (isRunningAgainstOldCluster() ? "old_snap" : "new_snap"));
  812. createSnapshot.addParameter("wait_for_completion", "true");
  813. createSnapshot.setJsonEntity("{\"indices\": \"" + index + "\"}");
  814. client().performRequest(createSnapshot);
  815. checkSnapshot("old_snap", count, getOldClusterVersion());
  816. if (false == isRunningAgainstOldCluster()) {
  817. checkSnapshot("new_snap", count, Version.CURRENT);
  818. }
  819. }
  820. public void testHistoryUUIDIsAdded() throws Exception {
  821. if (isRunningAgainstOldCluster()) {
  822. XContentBuilder mappingsAndSettings = jsonBuilder();
  823. mappingsAndSettings.startObject();
  824. {
  825. mappingsAndSettings.startObject("settings");
  826. mappingsAndSettings.field("number_of_shards", 1);
  827. mappingsAndSettings.field("number_of_replicas", 1);
  828. mappingsAndSettings.endObject();
  829. }
  830. mappingsAndSettings.endObject();
  831. Request createIndex = new Request("PUT", "/" + index);
  832. createIndex.setJsonEntity(Strings.toString(mappingsAndSettings));
  833. client().performRequest(createIndex);
  834. } else {
  835. Request statsRequest = new Request("GET", index + "/_stats");
  836. statsRequest.addParameter("level", "shards");
  837. Response response = client().performRequest(statsRequest);
  838. List<Object> shardStats = ObjectPath.createFromResponse(response).evaluate("indices." + index + ".shards.0");
  839. String globalHistoryUUID = null;
  840. for (Object shard : shardStats) {
  841. final String nodeId = ObjectPath.evaluate(shard, "routing.node");
  842. final Boolean primary = ObjectPath.evaluate(shard, "routing.primary");
  843. logger.info("evaluating: {} , {}", ObjectPath.evaluate(shard, "routing"), ObjectPath.evaluate(shard, "commit"));
  844. String historyUUID = ObjectPath.evaluate(shard, "commit.user_data.history_uuid");
  845. assertThat("no history uuid found on " + nodeId + " (primary: " + primary + ")", historyUUID, notNullValue());
  846. if (globalHistoryUUID == null) {
  847. globalHistoryUUID = historyUUID;
  848. } else {
  849. assertThat("history uuid mismatch on " + nodeId + " (primary: " + primary + ")", historyUUID,
  850. equalTo(globalHistoryUUID));
  851. }
  852. }
  853. }
  854. }
  855. private void checkSnapshot(String snapshotName, int count, Version tookOnVersion) throws IOException {
  856. // Check the snapshot metadata, especially the version
  857. Request listSnapshotRequest = new Request("GET", "/_snapshot/repo/" + snapshotName);
  858. Map<String, Object> listSnapshotResponse = entityAsMap(client().performRequest(listSnapshotRequest));
  859. assertEquals(singletonList(snapshotName), XContentMapValues.extractValue("snapshots.snapshot", listSnapshotResponse));
  860. assertEquals(singletonList("SUCCESS"), XContentMapValues.extractValue("snapshots.state", listSnapshotResponse));
  861. assertEquals(singletonList(tookOnVersion.toString()), XContentMapValues.extractValue("snapshots.version", listSnapshotResponse));
  862. // Remove the routing setting and template so we can test restoring them.
  863. Request clearRoutingFromSettings = new Request("PUT", "/_cluster/settings");
  864. clearRoutingFromSettings.setJsonEntity("{\"persistent\":{\"cluster.routing.allocation.exclude.test_attr\": null}}");
  865. client().performRequest(clearRoutingFromSettings);
  866. client().performRequest(new Request("DELETE", "/_template/test_template"));
  867. // Restore
  868. XContentBuilder restoreCommand = JsonXContent.contentBuilder().startObject();
  869. restoreCommand.field("include_global_state", true);
  870. restoreCommand.field("indices", index);
  871. restoreCommand.field("rename_pattern", index);
  872. restoreCommand.field("rename_replacement", "restored_" + index);
  873. restoreCommand.endObject();
  874. Request restoreRequest = new Request("POST", "/_snapshot/repo/" + snapshotName + "/_restore");
  875. restoreRequest.addParameter("wait_for_completion", "true");
  876. restoreRequest.setJsonEntity(Strings.toString(restoreCommand));
  877. client().performRequest(restoreRequest);
  878. // Make sure search finds all documents
  879. Request countRequest = new Request("GET", "/restored_" + index + "/_search");
  880. countRequest.addParameter("size", "0");
  881. String countResponse = toStr(client().performRequest(countRequest));
  882. assertThat(countResponse, containsString("\"total\":" + count));
  883. // Add some extra documents to the index to be sure we can still write to it after restoring it
  884. int extras = between(1, 100);
  885. StringBuilder bulk = new StringBuilder();
  886. for (int i = 0; i < extras; i++) {
  887. bulk.append("{\"index\":{\"_id\":\"").append(count + i).append("\"}}\n");
  888. bulk.append("{\"test\":\"test\"}\n");
  889. }
  890. Request writeToRestoredRequest = new Request("POST", "/restored_" + index + "/doc/_bulk");
  891. writeToRestoredRequest.addParameter("refresh", "true");
  892. writeToRestoredRequest.setJsonEntity(bulk.toString());
  893. assertThat(EntityUtils.toString(client().performRequest(writeToRestoredRequest).getEntity()), containsString("\"errors\":false"));
  894. // And count to make sure the add worked
  895. // Make sure search finds all documents
  896. Request countAfterWriteRequest = new Request("GET", "/restored_" + index + "/_search");
  897. countAfterWriteRequest.addParameter("size", "0");
  898. String countAfterWriteResponse = toStr(client().performRequest(countAfterWriteRequest));
  899. assertThat(countAfterWriteResponse, containsString("\"total\":" + (count + extras)));
  900. // Clean up the index for the next iteration
  901. client().performRequest(new Request("DELETE", "/restored_*"));
  902. // Check settings added by the restore process
  903. Request clusterSettingsRequest = new Request("GET", "/_cluster/settings");
  904. clusterSettingsRequest.addParameter("flat_settings", "true");
  905. Map<String, Object> clusterSettingsResponse = entityAsMap(client().performRequest(clusterSettingsRequest));
  906. @SuppressWarnings("unchecked") final Map<String, Object> persistentSettings =
  907. (Map<String, Object>)clusterSettingsResponse.get("persistent");
  908. assertThat(persistentSettings.get("cluster.routing.allocation.exclude.test_attr"), equalTo(getOldClusterVersion().toString()));
  909. // Check that the template was restored successfully
  910. Map<String, Object> getTemplateResponse = entityAsMap(client().performRequest(new Request("GET", "/_template/test_template")));
  911. Map<String, Object> expectedTemplate = new HashMap<>();
  912. if (isRunningAgainstOldCluster() && getOldClusterVersion().before(Version.V_6_0_0_beta1)) {
  913. expectedTemplate.put("template", "evil_*");
  914. } else {
  915. expectedTemplate.put("index_patterns", singletonList("evil_*"));
  916. }
  917. expectedTemplate.put("settings", singletonMap("index", singletonMap("number_of_shards", "1")));
  918. expectedTemplate.put("mappings", singletonMap("doc", singletonMap("_source", singletonMap("enabled", true))));
  919. expectedTemplate.put("order", 0);
  920. Map<String, Object> aliases = new HashMap<>();
  921. aliases.put("alias1", emptyMap());
  922. aliases.put("alias2", singletonMap("filter", singletonMap("term", singletonMap("version", tookOnVersion.toString()))));
  923. expectedTemplate.put("aliases", aliases);
  924. expectedTemplate = singletonMap("test_template", expectedTemplate);
  925. if (false == expectedTemplate.equals(getTemplateResponse)) {
  926. NotEqualMessageBuilder builder = new NotEqualMessageBuilder();
  927. builder.compareMaps(getTemplateResponse, expectedTemplate);
  928. fail("template doesn't match:\n" + builder.toString());
  929. }
  930. }
  931. // TODO tests for upgrades after shrink. We've had trouble with shrink in the past.
  932. private void indexRandomDocuments(int count, boolean flushAllowed, boolean saveInfo,
  933. CheckedFunction<Integer, XContentBuilder, IOException> docSupplier) throws IOException {
  934. logger.info("Indexing {} random documents", count);
  935. for (int i = 0; i < count; i++) {
  936. logger.debug("Indexing document [{}]", i);
  937. Request createDocument = new Request("POST", "/" + index + "/doc/" + i);
  938. createDocument.setJsonEntity(Strings.toString(docSupplier.apply(i)));
  939. client().performRequest(createDocument);
  940. if (rarely()) {
  941. refresh();
  942. }
  943. if (flushAllowed && rarely()) {
  944. logger.debug("Flushing [{}]", index);
  945. client().performRequest(new Request("POST", "/" + index + "/_flush"));
  946. }
  947. }
  948. if (saveInfo) {
  949. saveInfoDocument("count", Integer.toString(count));
  950. }
  951. }
  952. private int countOfIndexedRandomDocuments() throws IOException {
  953. return Integer.parseInt(loadInfoDocument("count"));
  954. }
  955. private void saveInfoDocument(String type, String value) throws IOException {
  956. XContentBuilder infoDoc = JsonXContent.contentBuilder().startObject();
  957. infoDoc.field("value", value);
  958. infoDoc.endObject();
  959. // Only create the first version so we know how many documents are created when the index is first created
  960. Request request = new Request("PUT", "/info/doc/" + index + "_" + type);
  961. request.addParameter("op_type", "create");
  962. request.setJsonEntity(Strings.toString(infoDoc));
  963. client().performRequest(request);
  964. }
  965. private String loadInfoDocument(String type) throws IOException {
  966. Request request = new Request("GET", "/info/doc/" + index + "_" + type);
  967. request.addParameter("filter_path", "_source");
  968. String doc = toStr(client().performRequest(request));
  969. Matcher m = Pattern.compile("\"value\":\"(.+)\"").matcher(doc);
  970. assertTrue(doc, m.find());
  971. return m.group(1);
  972. }
  973. private Object randomLenientBoolean() {
  974. return randomFrom(new Object[] {"off", "no", "0", 0, "false", false, "on", "yes", "1", 1, "true", true});
  975. }
  976. private void refresh() throws IOException {
  977. logger.debug("Refreshing [{}]", index);
  978. client().performRequest(new Request("POST", "/" + index + "/_refresh"));
  979. }
  980. private List<String> dataNodes(String index, RestClient client) throws IOException {
  981. Request request = new Request("GET", index + "/_stats");
  982. request.addParameter("level", "shards");
  983. Response response = client.performRequest(request);
  984. List<String> nodes = new ArrayList<>();
  985. List<Object> shardStats = ObjectPath.createFromResponse(response).evaluate("indices." + index + ".shards.0");
  986. for (Object shard : shardStats) {
  987. final String nodeId = ObjectPath.evaluate(shard, "routing.node");
  988. nodes.add(nodeId);
  989. }
  990. return nodes;
  991. }
  992. /**
  993. * Wait for an index to have green health, waiting longer than
  994. * {@link ESRestTestCase#ensureGreen}.
  995. */
  996. protected void ensureGreenLongWait(String index) throws IOException {
  997. Request request = new Request("GET", "/_cluster/health/" + index);
  998. request.addParameter("timeout", "2m");
  999. request.addParameter("wait_for_status", "green");
  1000. request.addParameter("wait_for_no_relocating_shards", "true");
  1001. request.addParameter("wait_for_events", "languid");
  1002. request.addParameter("level", "shards");
  1003. Map<String, Object> healthRsp = entityAsMap(client().performRequest(request));
  1004. logger.info("health api response: {}", healthRsp);
  1005. assertEquals("green", healthRsp.get("status"));
  1006. assertFalse((Boolean) healthRsp.get("timed_out"));
  1007. }
  1008. }