From 356a7200985d28164d51b94cc1768dab000ac869 Mon Sep 17 00:00:00 2001 From: Jamie White Date: Fri, 31 Jul 2015 13:02:29 -0400 Subject: [PATCH 01/23] Change capitalization of "as" The documentation has "Running As a Service on Linux" and "Running as a Service on Windows." The capitalization ought to be consistent. --- docs/reference/setup/as-a-service.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/setup/as-a-service.asciidoc b/docs/reference/setup/as-a-service.asciidoc index e325c1165f9..f603a13440b 100644 --- a/docs/reference/setup/as-a-service.asciidoc +++ b/docs/reference/setup/as-a-service.asciidoc @@ -1,5 +1,5 @@ [[setup-service]] -== Running As a Service on Linux +== Running as a Service on Linux In order to run elasticsearch as a service on your operating system, the provided packages try to make it as easy as possible for you to start and stop elasticsearch during reboot and upgrades. From 1b5bf17ae0487a6d528d3d1df1514dab6db59f8c Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Mon, 3 Aug 2015 15:40:18 -0400 Subject: [PATCH 02/23] Plugin script: Fix spaces Fixes ES_HOME with spaces and installing plugins from a local directory with spaces. Closes #12504 --- distribution/src/main/resources/bin/plugin | 12 +-- .../packaging/scripts/25_tar_plugins.bats | 98 +++++++++++++++++++ 2 files changed, 104 insertions(+), 6 deletions(-) diff --git a/distribution/src/main/resources/bin/plugin b/distribution/src/main/resources/bin/plugin index 15bf59ca7bb..797d8e3a8ff 100755 --- a/distribution/src/main/resources/bin/plugin +++ b/distribution/src/main/resources/bin/plugin @@ -69,15 +69,15 @@ fi while [ $# -gt 0 ]; do case $1 in -D*=*) - properties="$properties $1" + properties="$properties \"$1\"" ;; -D*) var=$1 shift - properties="$properties $var=$1" + properties="$properties \"$var\"=\"$1\"" ;; *) - args="$args $1" + args="$args \"$1\"" esac shift done @@ -88,7 +88,7 @@ if [ -e "$CONF_DIR" ]; then *-Des.default.path.conf=*|*-Des.path.conf=*) ;; *) - properties="$properties -Des.default.path.conf=$CONF_DIR" + properties="$properties -Des.default.path.conf=\"$CONF_DIR\"" ;; esac fi @@ -98,11 +98,11 @@ if [ -e "$CONF_FILE" ]; then *-Des.default.config=*|*-Des.config=*) ;; *) - properties="$properties -Des.default.config=$CONF_FILE" + properties="$properties -Des.default.config=\"$CONF_FILE\"" ;; esac fi export HOSTNAME=`hostname -s` -exec "$JAVA" $JAVA_OPTS $ES_JAVA_OPTS -Xmx64m -Xms16m -Delasticsearch -Des.path.home="$ES_HOME" $properties -cp "$ES_HOME/lib/*" org.elasticsearch.plugins.PluginManagerCliParser $args +eval "$JAVA" $JAVA_OPTS $ES_JAVA_OPTS -Xmx64m -Xms16m -Delasticsearch -Des.path.home="\"$ES_HOME\"" $properties -cp "\"$ES_HOME/lib/*\"" org.elasticsearch.plugins.PluginManagerCliParser $args diff --git a/distribution/src/test/resources/packaging/scripts/25_tar_plugins.bats b/distribution/src/test/resources/packaging/scripts/25_tar_plugins.bats index f8f1481699c..e4eccfaf1ec 100644 --- a/distribution/src/test/resources/packaging/scripts/25_tar_plugins.bats +++ b/distribution/src/test/resources/packaging/scripts/25_tar_plugins.bats @@ -257,3 +257,101 @@ setup() { run rm -rf "$TEMP_CONFIG_DIR" [ "$status" -eq 0 ] } + +@test "[TAR] install shield plugin to elasticsearch directory with a space" { + export ES_DIR="/tmp/elastic search" + + # Install the archive + install_archive + + # Checks that the archive is correctly installed + verify_archive_installation + + # Move the Elasticsearch installation to a directory with a space in it + rm -rf "$ES_DIR" + mv /tmp/elasticsearch "$ES_DIR" + + # Checks that plugin archive is available + [ -e "$SHIELD_ZIP" ] + + # Install Shield + run "$ES_DIR/bin/plugin" install elasticsearch/shield/latest -u "file://$SHIELD_ZIP" + [ "$status" -eq 0 ] + + # Checks that Shield is correctly installed + assert_file_exist "$ES_DIR/bin/shield" + assert_file_exist "$ES_DIR/bin/shield/esusers" + assert_file_exist "$ES_DIR/bin/shield/syskeygen" + assert_file_exist "$ES_DIR/config/shield" + assert_file_exist "$ES_DIR/config/shield/role_mapping.yml" + assert_file_exist "$ES_DIR/config/shield/roles.yml" + assert_file_exist "$ES_DIR/config/shield/users" + assert_file_exist "$ES_DIR/config/shield/users_roles" + assert_file_exist "$ES_DIR/plugins/shield" + + # Remove the plugin + run "$ES_DIR/bin/plugin" remove elasticsearch/shield/latest + [ "$status" -eq 0 ] + + # Checks that the plugin is correctly removed + assert_file_not_exist "$ES_DIR/bin/shield" + assert_file_exist "$ES_DIR/config/shield" + assert_file_exist "$ES_DIR/config/shield/role_mapping.yml" + assert_file_exist "$ES_DIR/config/shield/roles.yml" + assert_file_exist "$ES_DIR/config/shield/users" + assert_file_exist "$ES_DIR/config/shield/users_roles" + assert_file_not_exist "$ES_DIR/plugins/shield" + + #Cleanup our temporary Elasticsearch installation + rm -rf "$ES_DIR" +} + +@test "[TAR] install shield plugin from a directory with a space" { + + export SHIELD_ZIP_WITH_SPACE="/tmp/plugins with space/shield.zip" + + # Install the archive + install_archive + + # Checks that the archive is correctly installed + verify_archive_installation + + # Checks that plugin archive is available + [ -e "$SHIELD_ZIP" ] + + # Copy the shield plugin to a directory with a space in it + rm -f "$SHIELD_ZIP_WITH_SPACE" + mkdir -p "$(dirname "$SHIELD_ZIP_WITH_SPACE")" + cp $SHIELD_ZIP "$SHIELD_ZIP_WITH_SPACE" + + # Install Shield + run /tmp/elasticsearch/bin/plugin install elasticsearch/shield/latest -u "file://$SHIELD_ZIP_WITH_SPACE" + [ "$status" -eq 0 ] + + # Checks that Shield is correctly installed + assert_file_exist "/tmp/elasticsearch/bin/shield" + assert_file_exist "/tmp/elasticsearch/bin/shield/esusers" + assert_file_exist "/tmp/elasticsearch/bin/shield/syskeygen" + assert_file_exist "/tmp/elasticsearch/config/shield" + assert_file_exist "/tmp/elasticsearch/config/shield/role_mapping.yml" + assert_file_exist "/tmp/elasticsearch/config/shield/roles.yml" + assert_file_exist "/tmp/elasticsearch/config/shield/users" + assert_file_exist "/tmp/elasticsearch/config/shield/users_roles" + assert_file_exist "/tmp/elasticsearch/plugins/shield" + + # Remove the plugin + run /tmp/elasticsearch/bin/plugin remove elasticsearch/shield/latest + [ "$status" -eq 0 ] + + # Checks that the plugin is correctly removed + assert_file_not_exist "/tmp/elasticsearch/bin/shield" + assert_file_exist "/tmp/elasticsearch/config/shield" + assert_file_exist "/tmp/elasticsearch/config/shield/role_mapping.yml" + assert_file_exist "/tmp/elasticsearch/config/shield/roles.yml" + assert_file_exist "/tmp/elasticsearch/config/shield/users" + assert_file_exist "/tmp/elasticsearch/config/shield/users_roles" + assert_file_not_exist "/tmp/elasticsearch/plugins/shield" + + #Cleanup our plugin directory with a space + rm -rf "$SHIELD_ZIP_WITH_SPACE" +} From a74684473eeb6b924fd7cf88feb94a2973c6f6c0 Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Mon, 3 Aug 2015 12:18:51 +0200 Subject: [PATCH 03/23] Move Streams.copyTo(String|Bytes)FromClasspath() into StreamsUtils The Streams.copyTo(String|Bytes)FromClasspath() methods resolve resources using org.elasticsearch.io.Streams classloader. This is fine in elasticsearch core and when running tests but if used in a plugin this can lead to FileNotFoundExceptions at runtime because plugin are loaded in a dedicated classloader. --- .../main/java/org/elasticsearch/Build.java | 8 +-- .../org/elasticsearch/common/io/Streams.java | 29 --------- .../action/bulk/BulkIntegrationIT.java | 2 +- .../action/bulk/BulkRequestTests.java | 2 +- .../fieldstats/FieldStatsRequestTest.java | 4 +- .../MultiPercolatorRequestTests.java | 6 +- .../search/MultiSearchRequestTests.java | 10 +-- .../termvectors/TermVectorsUnitTests.java | 8 +-- .../common/cli/CliToolTestCase.java | 5 +- .../mapper/all/SimpleAllMapperTests.java | 5 +- .../GenericStoreDynamicTemplateTests.java | 4 +- .../PathMatchDynamicTemplateTests.java | 4 +- .../simple/SimpleDynamicTemplatesTests.java | 4 +- .../mapper/multifield/MultiFieldTests.java | 4 +- .../merge/JavaMultiFieldMergeTests.java | 2 +- .../index/mapper/path/PathMapperTests.java | 2 +- .../mapper/simple/SimpleMapperTests.java | 4 +- .../update/UpdateMappingOnClusterIT.java | 2 +- .../mapper/update/UpdateMappingTests.java | 2 +- ...QueryParserFilterDateRangeFormatTests.java | 4 +- ...eryParserFilterDateRangeTimezoneTests.java | 4 +- .../query/SimpleIndexQueryParserTests.java | 4 +- .../bucket/DedicatedAggregationIT.java | 2 +- .../search/child/ChildQuerySearchIT.java | 2 +- .../morelikethis/ItemSerializationTests.java | 2 +- .../org/elasticsearch/test/StreamsUtils.java | 61 +++++++++++++++++++ 26 files changed, 107 insertions(+), 79 deletions(-) create mode 100644 core/src/test/java/org/elasticsearch/test/StreamsUtils.java diff --git a/core/src/main/java/org/elasticsearch/Build.java b/core/src/main/java/org/elasticsearch/Build.java index cf3b7de05a6..508b4dc4375 100644 --- a/core/src/main/java/org/elasticsearch/Build.java +++ b/core/src/main/java/org/elasticsearch/Build.java @@ -19,14 +19,13 @@ package org.elasticsearch; -import org.elasticsearch.common.io.FastStringReader; -import org.elasticsearch.common.io.Streams; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.joda.time.DateTimeZone; import org.joda.time.format.ISODateTimeFormat; import java.io.IOException; +import java.io.InputStream; import java.util.Properties; /** @@ -40,10 +39,9 @@ public class Build { String hashShort = "NA"; String timestamp = "NA"; - try { - String properties = Streams.copyToStringFromClasspath("/es-build.properties"); + try (InputStream is = Build.class.getResourceAsStream("/es-build.properties")){ Properties props = new Properties(); - props.load(new FastStringReader(properties)); + props.load(is); hash = props.getProperty("hash", hash); if (!hash.equals("NA")) { hashShort = hash.substring(0, 7); diff --git a/core/src/main/java/org/elasticsearch/common/io/Streams.java b/core/src/main/java/org/elasticsearch/common/io/Streams.java index b5f224e72f0..166e7960b3c 100644 --- a/core/src/main/java/org/elasticsearch/common/io/Streams.java +++ b/core/src/main/java/org/elasticsearch/common/io/Streams.java @@ -22,7 +22,6 @@ package org.elasticsearch.common.io; import com.google.common.base.Charsets; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; -import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.util.Callback; import java.io.*; @@ -184,34 +183,6 @@ public abstract class Streams { return out.toString(); } - public static String copyToStringFromClasspath(ClassLoader classLoader, String path) throws IOException { - InputStream is = classLoader.getResourceAsStream(path); - if (is == null) { - throw new FileNotFoundException("Resource [" + path + "] not found in classpath with class loader [" + classLoader + "]"); - } - return copyToString(new InputStreamReader(is, Charsets.UTF_8)); - } - - public static String copyToStringFromClasspath(String path) throws IOException { - InputStream is = Streams.class.getResourceAsStream(path); - if (is == null) { - throw new FileNotFoundException("Resource [" + path + "] not found in classpath"); - } - return copyToString(new InputStreamReader(is, Charsets.UTF_8)); - } - - public static byte[] copyToBytesFromClasspath(String path) throws IOException { - try (InputStream is = Streams.class.getResourceAsStream(path)) { - if (is == null) { - throw new FileNotFoundException("Resource [" + path + "] not found in classpath"); - } - try (BytesStreamOutput out = new BytesStreamOutput()) { - copy(is, out); - return out.bytes().toBytes(); - } - } - } - public static int readFully(Reader reader, char[] dest) throws IOException { return readFully(reader, dest, 0, dest.length); } diff --git a/core/src/test/java/org/elasticsearch/action/bulk/BulkIntegrationIT.java b/core/src/test/java/org/elasticsearch/action/bulk/BulkIntegrationIT.java index fe5c0821330..ce2df6b3543 100644 --- a/core/src/test/java/org/elasticsearch/action/bulk/BulkIntegrationIT.java +++ b/core/src/test/java/org/elasticsearch/action/bulk/BulkIntegrationIT.java @@ -26,7 +26,7 @@ import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse; import org.elasticsearch.test.ESIntegTestCase; import org.junit.Test; -import static org.elasticsearch.common.io.Streams.copyToStringFromClasspath; +import static org.elasticsearch.test.StreamsUtils.copyToStringFromClasspath; public class BulkIntegrationIT extends ESIntegTestCase { diff --git a/core/src/test/java/org/elasticsearch/action/bulk/BulkRequestTests.java b/core/src/test/java/org/elasticsearch/action/bulk/BulkRequestTests.java index 61361d48fb0..3d462fd17ee 100644 --- a/core/src/test/java/org/elasticsearch/action/bulk/BulkRequestTests.java +++ b/core/src/test/java/org/elasticsearch/action/bulk/BulkRequestTests.java @@ -37,7 +37,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import static org.elasticsearch.common.io.Streams.copyToStringFromClasspath; +import static org.elasticsearch.test.StreamsUtils.copyToStringFromClasspath; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.notNullValue; diff --git a/core/src/test/java/org/elasticsearch/action/fieldstats/FieldStatsRequestTest.java b/core/src/test/java/org/elasticsearch/action/fieldstats/FieldStatsRequestTest.java index e2c98ce65a6..dd562a9bccd 100644 --- a/core/src/test/java/org/elasticsearch/action/fieldstats/FieldStatsRequestTest.java +++ b/core/src/test/java/org/elasticsearch/action/fieldstats/FieldStatsRequestTest.java @@ -20,8 +20,8 @@ package org.elasticsearch.action.fieldstats; import org.elasticsearch.common.bytes.BytesArray; -import org.elasticsearch.common.io.Streams; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.StreamsUtils; import static org.elasticsearch.action.fieldstats.IndexConstraint.Comparison.*; import static org.elasticsearch.action.fieldstats.IndexConstraint.Property.MAX; @@ -31,7 +31,7 @@ import static org.hamcrest.Matchers.equalTo; public class FieldStatsRequestTest extends ESTestCase { public void testFieldsParsing() throws Exception { - byte[] data = Streams.copyToBytesFromClasspath("/org/elasticsearch/action/fieldstats/fieldstats-index-constraints-request.json"); + byte[] data = StreamsUtils.copyToBytesFromClasspath("/org/elasticsearch/action/fieldstats/fieldstats-index-constraints-request.json"); FieldStatsRequest request = new FieldStatsRequest(); request.source(new BytesArray(data)); diff --git a/core/src/test/java/org/elasticsearch/action/percolate/MultiPercolatorRequestTests.java b/core/src/test/java/org/elasticsearch/action/percolate/MultiPercolatorRequestTests.java index 2f2a0728c67..48c75d8267b 100644 --- a/core/src/test/java/org/elasticsearch/action/percolate/MultiPercolatorRequestTests.java +++ b/core/src/test/java/org/elasticsearch/action/percolate/MultiPercolatorRequestTests.java @@ -20,7 +20,7 @@ package org.elasticsearch.action.percolate; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.common.collect.MapBuilder; -import org.elasticsearch.common.io.Streams; +import org.elasticsearch.test.StreamsUtils; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.test.ESTestCase; import org.junit.Test; @@ -35,7 +35,7 @@ public class MultiPercolatorRequestTests extends ESTestCase { @Test public void testParseBulkRequests() throws Exception { - byte[] data = Streams.copyToBytesFromClasspath("/org/elasticsearch/action/percolate/mpercolate1.json"); + byte[] data = StreamsUtils.copyToBytesFromClasspath("/org/elasticsearch/action/percolate/mpercolate1.json"); MultiPercolateRequest request = new MultiPercolateRequest().add(data, 0, data.length); assertThat(request.requests().size(), equalTo(8)); @@ -152,7 +152,7 @@ public class MultiPercolatorRequestTests extends ESTestCase { @Test public void testParseBulkRequests_defaults() throws Exception { - byte[] data = Streams.copyToBytesFromClasspath("/org/elasticsearch/action/percolate/mpercolate2.json"); + byte[] data = StreamsUtils.copyToBytesFromClasspath("/org/elasticsearch/action/percolate/mpercolate2.json"); MultiPercolateRequest request = new MultiPercolateRequest(); request.indices("my-index1").documentType("my-type1").indicesOptions(IndicesOptions.lenientExpandOpen()); request.add(data, 0, data.length); diff --git a/core/src/test/java/org/elasticsearch/action/search/MultiSearchRequestTests.java b/core/src/test/java/org/elasticsearch/action/search/MultiSearchRequestTests.java index 05f7d522346..97aee6ea3d6 100644 --- a/core/src/test/java/org/elasticsearch/action/search/MultiSearchRequestTests.java +++ b/core/src/test/java/org/elasticsearch/action/search/MultiSearchRequestTests.java @@ -20,7 +20,7 @@ package org.elasticsearch.action.search; import org.elasticsearch.action.support.IndicesOptions; -import org.elasticsearch.common.io.Streams; +import org.elasticsearch.test.StreamsUtils; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; @@ -38,7 +38,7 @@ public class MultiSearchRequestTests extends ESTestCase { @Test public void simpleAdd() throws Exception { - byte[] data = Streams.copyToBytesFromClasspath("/org/elasticsearch/action/search/simple-msearch1.json"); + byte[] data = StreamsUtils.copyToBytesFromClasspath("/org/elasticsearch/action/search/simple-msearch1.json"); MultiSearchRequest request = new MultiSearchRequest().add(data, 0, data.length, null, null, null); assertThat(request.requests().size(), equalTo(8)); assertThat(request.requests().get(0).indices()[0], equalTo("test")); @@ -64,7 +64,7 @@ public class MultiSearchRequestTests extends ESTestCase { @Test public void simpleAdd2() throws Exception { - byte[] data = Streams.copyToBytesFromClasspath("/org/elasticsearch/action/search/simple-msearch2.json"); + byte[] data = StreamsUtils.copyToBytesFromClasspath("/org/elasticsearch/action/search/simple-msearch2.json"); MultiSearchRequest request = new MultiSearchRequest().add(data, 0, data.length, null, null, null); assertThat(request.requests().size(), equalTo(5)); assertThat(request.requests().get(0).indices()[0], equalTo("test")); @@ -82,7 +82,7 @@ public class MultiSearchRequestTests extends ESTestCase { @Test public void simpleAdd3() throws Exception { - byte[] data = Streams.copyToBytesFromClasspath("/org/elasticsearch/action/search/simple-msearch3.json"); + byte[] data = StreamsUtils.copyToBytesFromClasspath("/org/elasticsearch/action/search/simple-msearch3.json"); MultiSearchRequest request = new MultiSearchRequest().add(data, 0, data.length, null, null, null); assertThat(request.requests().size(), equalTo(4)); assertThat(request.requests().get(0).indices()[0], equalTo("test0")); @@ -101,7 +101,7 @@ public class MultiSearchRequestTests extends ESTestCase { @Test public void simpleAdd4() throws Exception { - byte[] data = Streams.copyToBytesFromClasspath("/org/elasticsearch/action/search/simple-msearch4.json"); + byte[] data = StreamsUtils.copyToBytesFromClasspath("/org/elasticsearch/action/search/simple-msearch4.json"); MultiSearchRequest request = new MultiSearchRequest().add(data, 0, data.length, null, null, null); assertThat(request.requests().size(), equalTo(3)); assertThat(request.requests().get(0).indices()[0], equalTo("test0")); diff --git a/core/src/test/java/org/elasticsearch/action/termvectors/TermVectorsUnitTests.java b/core/src/test/java/org/elasticsearch/action/termvectors/TermVectorsUnitTests.java index 13d923a7901..82809d1c5cd 100644 --- a/core/src/test/java/org/elasticsearch/action/termvectors/TermVectorsUnitTests.java +++ b/core/src/test/java/org/elasticsearch/action/termvectors/TermVectorsUnitTests.java @@ -31,7 +31,6 @@ import org.apache.lucene.store.Directory; import org.elasticsearch.action.termvectors.TermVectorsRequest.Flag; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; -import org.elasticsearch.common.io.Streams; import org.elasticsearch.common.io.stream.InputStreamStreamInput; import org.elasticsearch.common.io.stream.OutputStreamStreamOutput; import org.elasticsearch.common.xcontent.XContentFactory; @@ -43,6 +42,7 @@ import org.elasticsearch.index.mapper.core.TypeParsers; import org.elasticsearch.index.mapper.internal.AllFieldMapper; import org.elasticsearch.rest.action.termvectors.RestTermVectorsAction; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.StreamsUtils; import org.hamcrest.Matchers; import org.junit.Test; @@ -292,13 +292,13 @@ public class TermVectorsUnitTests extends ESTestCase { @Test public void testMultiParser() throws Exception { - byte[] data = Streams.copyToBytesFromClasspath("/org/elasticsearch/action/termvectors/multiRequest1.json"); + byte[] data = StreamsUtils.copyToBytesFromClasspath("/org/elasticsearch/action/termvectors/multiRequest1.json"); BytesReference bytes = new BytesArray(data); MultiTermVectorsRequest request = new MultiTermVectorsRequest(); request.add(new TermVectorsRequest(), bytes); checkParsedParameters(request); - data = Streams.copyToBytesFromClasspath("/org/elasticsearch/action/termvectors/multiRequest2.json"); + data = StreamsUtils.copyToBytesFromClasspath("/org/elasticsearch/action/termvectors/multiRequest2.json"); bytes = new BytesArray(data); request = new MultiTermVectorsRequest(); request.add(new TermVectorsRequest(), bytes); @@ -328,7 +328,7 @@ public class TermVectorsUnitTests extends ESTestCase { @Test // issue #12311 public void testMultiParserFilter() throws Exception { - byte[] data = Streams.copyToBytesFromClasspath("/org/elasticsearch/action/termvectors/multiRequest3.json"); + byte[] data = StreamsUtils.copyToBytesFromClasspath("/org/elasticsearch/action/termvectors/multiRequest3.json"); BytesReference bytes = new BytesArray(data); MultiTermVectorsRequest request = new MultiTermVectorsRequest(); request.add(new TermVectorsRequest(), bytes); diff --git a/core/src/test/java/org/elasticsearch/common/cli/CliToolTestCase.java b/core/src/test/java/org/elasticsearch/common/cli/CliToolTestCase.java index fdfee87dfcc..b30788cf702 100644 --- a/core/src/test/java/org/elasticsearch/common/cli/CliToolTestCase.java +++ b/core/src/test/java/org/elasticsearch/common/cli/CliToolTestCase.java @@ -21,11 +21,10 @@ package org.elasticsearch.common.cli; import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.common.Strings; -import org.elasticsearch.common.io.Streams; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.StreamsUtils; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import java.io.IOException; import java.io.PrintWriter; @@ -161,7 +160,7 @@ public abstract class CliToolTestCase extends ESTestCase { } assertThat(nonEmptyLines, hasSize(greaterThan(0))); - String expectedDocs = Streams.copyToStringFromClasspath(classPath); + String expectedDocs = StreamsUtils.copyToStringFromClasspath(classPath); for (String nonEmptyLine : nonEmptyLines) { assertThat(expectedDocs, containsString(nonEmptyLine.replaceAll(System.lineSeparator(), ""))); } diff --git a/core/src/test/java/org/elasticsearch/index/mapper/all/SimpleAllMapperTests.java b/core/src/test/java/org/elasticsearch/index/mapper/all/SimpleAllMapperTests.java index e80dcec3e97..b50f2da849c 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/all/SimpleAllMapperTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/all/SimpleAllMapperTests.java @@ -56,15 +56,14 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import static org.elasticsearch.common.io.Streams.copyToBytesFromClasspath; -import static org.elasticsearch.common.io.Streams.copyToStringFromClasspath; +import static org.elasticsearch.test.StreamsUtils.copyToBytesFromClasspath; +import static org.elasticsearch.test.StreamsUtils.copyToStringFromClasspath; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; public class SimpleAllMapperTests extends ESSingleNodeTestCase { diff --git a/core/src/test/java/org/elasticsearch/index/mapper/dynamictemplate/genericstore/GenericStoreDynamicTemplateTests.java b/core/src/test/java/org/elasticsearch/index/mapper/dynamictemplate/genericstore/GenericStoreDynamicTemplateTests.java index 83a14687b32..76383408ed8 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/dynamictemplate/genericstore/GenericStoreDynamicTemplateTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/dynamictemplate/genericstore/GenericStoreDynamicTemplateTests.java @@ -29,8 +29,8 @@ import org.elasticsearch.index.mapper.ParsedDocument; import org.elasticsearch.test.ESSingleNodeTestCase; import org.junit.Test; -import static org.elasticsearch.common.io.Streams.copyToBytesFromClasspath; -import static org.elasticsearch.common.io.Streams.copyToStringFromClasspath; +import static org.elasticsearch.test.StreamsUtils.copyToBytesFromClasspath; +import static org.elasticsearch.test.StreamsUtils.copyToStringFromClasspath; import static org.hamcrest.Matchers.equalTo; /** diff --git a/core/src/test/java/org/elasticsearch/index/mapper/dynamictemplate/pathmatch/PathMatchDynamicTemplateTests.java b/core/src/test/java/org/elasticsearch/index/mapper/dynamictemplate/pathmatch/PathMatchDynamicTemplateTests.java index b4c1ba668f4..34c855f4f2e 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/dynamictemplate/pathmatch/PathMatchDynamicTemplateTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/dynamictemplate/pathmatch/PathMatchDynamicTemplateTests.java @@ -29,8 +29,8 @@ import org.elasticsearch.index.mapper.ParseContext.Document; import org.elasticsearch.test.ESSingleNodeTestCase; import org.junit.Test; -import static org.elasticsearch.common.io.Streams.copyToBytesFromClasspath; -import static org.elasticsearch.common.io.Streams.copyToStringFromClasspath; +import static org.elasticsearch.test.StreamsUtils.copyToBytesFromClasspath; +import static org.elasticsearch.test.StreamsUtils.copyToStringFromClasspath; import static org.hamcrest.Matchers.equalTo; /** diff --git a/core/src/test/java/org/elasticsearch/index/mapper/dynamictemplate/simple/SimpleDynamicTemplatesTests.java b/core/src/test/java/org/elasticsearch/index/mapper/dynamictemplate/simple/SimpleDynamicTemplatesTests.java index c136f4d989c..09358b5280c 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/dynamictemplate/simple/SimpleDynamicTemplatesTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/dynamictemplate/simple/SimpleDynamicTemplatesTests.java @@ -31,8 +31,8 @@ import org.elasticsearch.test.ESSingleNodeTestCase; import org.hamcrest.Matchers; import org.junit.Test; -import static org.elasticsearch.common.io.Streams.copyToBytesFromClasspath; -import static org.elasticsearch.common.io.Streams.copyToStringFromClasspath; +import static org.elasticsearch.test.StreamsUtils.copyToBytesFromClasspath; +import static org.elasticsearch.test.StreamsUtils.copyToStringFromClasspath; import static org.hamcrest.Matchers.equalTo; /** diff --git a/core/src/test/java/org/elasticsearch/index/mapper/multifield/MultiFieldTests.java b/core/src/test/java/org/elasticsearch/index/mapper/multifield/MultiFieldTests.java index 8511bf96c08..dc14ebecd5e 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/multifield/MultiFieldTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/multifield/MultiFieldTests.java @@ -47,8 +47,8 @@ import java.util.Collections; import java.util.Map; import java.util.TreeMap; -import static org.elasticsearch.common.io.Streams.copyToBytesFromClasspath; -import static org.elasticsearch.common.io.Streams.copyToStringFromClasspath; +import static org.elasticsearch.test.StreamsUtils.copyToBytesFromClasspath; +import static org.elasticsearch.test.StreamsUtils.copyToStringFromClasspath; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.index.mapper.MapperBuilders.doc; import static org.elasticsearch.index.mapper.MapperBuilders.rootObject; diff --git a/core/src/test/java/org/elasticsearch/index/mapper/multifield/merge/JavaMultiFieldMergeTests.java b/core/src/test/java/org/elasticsearch/index/mapper/multifield/merge/JavaMultiFieldMergeTests.java index 5405aee6ec8..655069ed9e2 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/multifield/merge/JavaMultiFieldMergeTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/multifield/merge/JavaMultiFieldMergeTests.java @@ -32,7 +32,7 @@ import org.junit.Test; import java.util.Arrays; -import static org.elasticsearch.common.io.Streams.copyToStringFromClasspath; +import static org.elasticsearch.test.StreamsUtils.copyToStringFromClasspath; import static org.hamcrest.Matchers.*; /** diff --git a/core/src/test/java/org/elasticsearch/index/mapper/path/PathMapperTests.java b/core/src/test/java/org/elasticsearch/index/mapper/path/PathMapperTests.java index 4bd12550d89..06ef922e94d 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/path/PathMapperTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/path/PathMapperTests.java @@ -25,7 +25,7 @@ import org.junit.Test; import java.io.IOException; -import static org.elasticsearch.common.io.Streams.copyToStringFromClasspath; +import static org.elasticsearch.test.StreamsUtils.copyToStringFromClasspath; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; diff --git a/core/src/test/java/org/elasticsearch/index/mapper/simple/SimpleMapperTests.java b/core/src/test/java/org/elasticsearch/index/mapper/simple/SimpleMapperTests.java index 5c4bd28a028..a08eacce647 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/simple/SimpleMapperTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/simple/SimpleMapperTests.java @@ -30,8 +30,8 @@ import org.elasticsearch.index.IndexService; import org.elasticsearch.test.ESSingleNodeTestCase; import org.junit.Test; -import static org.elasticsearch.common.io.Streams.copyToBytesFromClasspath; -import static org.elasticsearch.common.io.Streams.copyToStringFromClasspath; +import static org.elasticsearch.test.StreamsUtils.copyToBytesFromClasspath; +import static org.elasticsearch.test.StreamsUtils.copyToStringFromClasspath; import static org.elasticsearch.index.mapper.MapperBuilders.*; import static org.hamcrest.Matchers.equalTo; diff --git a/core/src/test/java/org/elasticsearch/index/mapper/update/UpdateMappingOnClusterIT.java b/core/src/test/java/org/elasticsearch/index/mapper/update/UpdateMappingOnClusterIT.java index 7478b79bf7b..4d4d06bd292 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/update/UpdateMappingOnClusterIT.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/update/UpdateMappingOnClusterIT.java @@ -30,7 +30,7 @@ import org.junit.Test; import java.util.HashMap; -import static org.elasticsearch.common.io.Streams.copyToStringFromClasspath; +import static org.elasticsearch.test.StreamsUtils.copyToStringFromClasspath; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.hamcrest.Matchers.containsString; diff --git a/core/src/test/java/org/elasticsearch/index/mapper/update/UpdateMappingTests.java b/core/src/test/java/org/elasticsearch/index/mapper/update/UpdateMappingTests.java index a45d4a34a13..4b136a78b65 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/update/UpdateMappingTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/update/UpdateMappingTests.java @@ -36,7 +36,7 @@ import org.junit.Test; import java.io.IOException; import java.util.LinkedHashMap; -import static org.elasticsearch.common.io.Streams.copyToStringFromClasspath; +import static org.elasticsearch.test.StreamsUtils.copyToStringFromClasspath; import static org.hamcrest.CoreMatchers.equalTo; diff --git a/core/src/test/java/org/elasticsearch/index/query/IndexQueryParserFilterDateRangeFormatTests.java b/core/src/test/java/org/elasticsearch/index/query/IndexQueryParserFilterDateRangeFormatTests.java index 4a60a2da464..8b4c40585c4 100644 --- a/core/src/test/java/org/elasticsearch/index/query/IndexQueryParserFilterDateRangeFormatTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/IndexQueryParserFilterDateRangeFormatTests.java @@ -38,8 +38,8 @@ import org.junit.Test; import java.io.IOException; -import static org.elasticsearch.common.io.Streams.copyToBytesFromClasspath; -import static org.elasticsearch.common.io.Streams.copyToStringFromClasspath; +import static org.elasticsearch.test.StreamsUtils.copyToBytesFromClasspath; +import static org.elasticsearch.test.StreamsUtils.copyToStringFromClasspath; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; diff --git a/core/src/test/java/org/elasticsearch/index/query/IndexQueryParserFilterDateRangeTimezoneTests.java b/core/src/test/java/org/elasticsearch/index/query/IndexQueryParserFilterDateRangeTimezoneTests.java index 5c7b122096a..d581aa6ae77 100644 --- a/core/src/test/java/org/elasticsearch/index/query/IndexQueryParserFilterDateRangeTimezoneTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/IndexQueryParserFilterDateRangeTimezoneTests.java @@ -38,8 +38,8 @@ import org.junit.Test; import java.io.IOException; -import static org.elasticsearch.common.io.Streams.copyToBytesFromClasspath; -import static org.elasticsearch.common.io.Streams.copyToStringFromClasspath; +import static org.elasticsearch.test.StreamsUtils.copyToBytesFromClasspath; +import static org.elasticsearch.test.StreamsUtils.copyToStringFromClasspath; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.lessThanOrEqualTo; diff --git a/core/src/test/java/org/elasticsearch/index/query/SimpleIndexQueryParserTests.java b/core/src/test/java/org/elasticsearch/index/query/SimpleIndexQueryParserTests.java index b17282ee412..b164c448b8a 100644 --- a/core/src/test/java/org/elasticsearch/index/query/SimpleIndexQueryParserTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/SimpleIndexQueryParserTests.java @@ -73,8 +73,8 @@ import java.util.Arrays; import java.util.EnumSet; import java.util.List; -import static org.elasticsearch.common.io.Streams.copyToBytesFromClasspath; -import static org.elasticsearch.common.io.Streams.copyToStringFromClasspath; +import static org.elasticsearch.test.StreamsUtils.copyToBytesFromClasspath; +import static org.elasticsearch.test.StreamsUtils.copyToStringFromClasspath; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.index.query.QueryBuilders.*; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertBooleanSubQuery; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DedicatedAggregationIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DedicatedAggregationIT.java index 941cf238d5a..0f22bb42d5f 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DedicatedAggregationIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/DedicatedAggregationIT.java @@ -26,7 +26,7 @@ import org.junit.Test; import java.io.IOException; -import static org.elasticsearch.common.io.Streams.copyToStringFromClasspath; +import static org.elasticsearch.test.StreamsUtils.copyToStringFromClasspath; import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; import static org.hamcrest.CoreMatchers.equalTo; diff --git a/core/src/test/java/org/elasticsearch/search/child/ChildQuerySearchIT.java b/core/src/test/java/org/elasticsearch/search/child/ChildQuerySearchIT.java index 5f89af38660..4a1eed7a457 100644 --- a/core/src/test/java/org/elasticsearch/search/child/ChildQuerySearchIT.java +++ b/core/src/test/java/org/elasticsearch/search/child/ChildQuerySearchIT.java @@ -55,7 +55,7 @@ import java.io.IOException; import java.util.*; import static com.google.common.collect.Maps.newHashMap; -import static org.elasticsearch.common.io.Streams.copyToStringFromClasspath; +import static org.elasticsearch.test.StreamsUtils.copyToStringFromClasspath; import static org.elasticsearch.common.settings.Settings.settingsBuilder; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.index.query.QueryBuilders.*; diff --git a/core/src/test/java/org/elasticsearch/search/morelikethis/ItemSerializationTests.java b/core/src/test/java/org/elasticsearch/search/morelikethis/ItemSerializationTests.java index 91a1731feda..ab06a3d5d51 100644 --- a/core/src/test/java/org/elasticsearch/search/morelikethis/ItemSerializationTests.java +++ b/core/src/test/java/org/elasticsearch/search/morelikethis/ItemSerializationTests.java @@ -37,7 +37,7 @@ import java.io.IOException; import java.util.List; import java.util.Random; -import static org.elasticsearch.common.io.Streams.copyToStringFromClasspath; +import static org.elasticsearch.test.StreamsUtils.copyToStringFromClasspath; import static org.hamcrest.Matchers.is; public class ItemSerializationTests extends ESTestCase { diff --git a/core/src/test/java/org/elasticsearch/test/StreamsUtils.java b/core/src/test/java/org/elasticsearch/test/StreamsUtils.java new file mode 100644 index 00000000000..ffc9537c3ee --- /dev/null +++ b/core/src/test/java/org/elasticsearch/test/StreamsUtils.java @@ -0,0 +1,61 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.test; + +import com.google.common.base.Charsets; +import org.elasticsearch.common.io.Streams; +import org.elasticsearch.common.io.stream.BytesStreamOutput; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +public class StreamsUtils { + + public static String copyToStringFromClasspath(ClassLoader classLoader, String path) throws IOException { + InputStream is = classLoader.getResourceAsStream(path); + if (is == null) { + throw new FileNotFoundException("Resource [" + path + "] not found in classpath with class loader [" + classLoader + "]"); + } + return Streams.copyToString(new InputStreamReader(is, Charsets.UTF_8)); + } + + public static String copyToStringFromClasspath(String path) throws IOException { + InputStream is = Streams.class.getResourceAsStream(path); + if (is == null) { + throw new FileNotFoundException("Resource [" + path + "] not found in classpath"); + } + return Streams.copyToString(new InputStreamReader(is, Charsets.UTF_8)); + } + + public static byte[] copyToBytesFromClasspath(String path) throws IOException { + try (InputStream is = Streams.class.getResourceAsStream(path)) { + if (is == null) { + throw new FileNotFoundException("Resource [" + path + "] not found in classpath"); + } + try (BytesStreamOutput out = new BytesStreamOutput()) { + Streams.copy(is, out); + return out.bytes().toBytes(); + } + } + } + +} From 6753f7f03e6e2d8abfc3a00ae8c3e0b8722e3215 Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Mon, 3 Aug 2015 17:38:30 +0200 Subject: [PATCH 04/23] Cut over master to 2.0.0-SNAPSHOT --- core/pom.xml | 2 +- core/src/main/java/org/elasticsearch/Version.java | 6 +++++- core/src/test/java/org/elasticsearch/VersionTests.java | 5 +++++ dev-tools/pom.xml | 2 +- distribution/deb/pom.xml | 2 +- distribution/fully-loaded/pom.xml | 2 +- distribution/pom.xml | 2 +- distribution/rpm/pom.xml | 2 +- distribution/shaded/pom.xml | 2 +- distribution/tar/pom.xml | 2 +- distribution/zip/pom.xml | 2 +- plugins/analysis-icu/pom.xml | 2 +- plugins/analysis-kuromoji/pom.xml | 2 +- plugins/analysis-phonetic/pom.xml | 2 +- plugins/analysis-smartcn/pom.xml | 2 +- plugins/analysis-stempel/pom.xml | 2 +- plugins/cloud-aws/pom.xml | 2 +- plugins/cloud-azure/pom.xml | 2 +- plugins/cloud-gce/pom.xml | 2 +- plugins/delete-by-query/pom.xml | 2 +- plugins/lang-javascript/pom.xml | 2 +- plugins/lang-python/pom.xml | 2 +- plugins/pom.xml | 4 ++-- plugins/site-example/pom.xml | 2 +- pom.xml | 2 +- rest-api-spec/pom.xml | 2 +- 26 files changed, 35 insertions(+), 26 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index d4fb7f95342..325232d087e 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -6,7 +6,7 @@ org.elasticsearch elasticsearch-parent - 2.0.0-beta1-SNAPSHOT + 2.0.0-SNAPSHOT org.elasticsearch diff --git a/core/src/main/java/org/elasticsearch/Version.java b/core/src/main/java/org/elasticsearch/Version.java index 5ba63c4811b..d820a1b2427 100644 --- a/core/src/main/java/org/elasticsearch/Version.java +++ b/core/src/main/java/org/elasticsearch/Version.java @@ -255,8 +255,10 @@ public class Version { public static final int V_2_0_0_beta1_ID = 2000001; public static final Version V_2_0_0_beta1 = new Version(V_2_0_0_beta1_ID, true, org.apache.lucene.util.Version.LUCENE_5_2_1); + public static final int V_2_0_0_ID = 2000099; + public static final Version V_2_0_0 = new Version(V_2_0_0_ID, true, org.apache.lucene.util.Version.LUCENE_5_2_1); - public static final Version CURRENT = V_2_0_0_beta1; + public static final Version CURRENT = V_2_0_0; static { assert CURRENT.luceneVersion.equals(Lucene.VERSION) : "Version must be upgraded to [" + Lucene.VERSION + "] is still set to [" + CURRENT.luceneVersion + "]"; @@ -268,6 +270,8 @@ public class Version { public static Version fromId(int id) { switch (id) { + case V_2_0_0_ID: + return V_2_0_0; case V_2_0_0_beta1_ID: return V_2_0_0_beta1; case V_1_7_2_ID: diff --git a/core/src/test/java/org/elasticsearch/VersionTests.java b/core/src/test/java/org/elasticsearch/VersionTests.java index 4161578a5a6..76c0d8d19d3 100644 --- a/core/src/test/java/org/elasticsearch/VersionTests.java +++ b/core/src/test/java/org/elasticsearch/VersionTests.java @@ -203,10 +203,15 @@ public class VersionTests extends ESTestCase { // only the latest version for a branch should be a snapshot (ie unreleased) String branchName = "" + v.major + "." + v.minor; + if (v.equals(Version.V_2_0_0_beta1)) { + assertTrue("Remove this once beta1 is released", v.snapshot()); + continue; // this is just a temporary fix until we have a snapshot for the beta since we now have 2 unreleased version of the same major.minor group + } Version maxBranchVersion = maxBranchVersions.get(branchName); if (maxBranchVersion == null) { maxBranchVersions.put(branchName, v); } else if (v.after(maxBranchVersion)) { + assertFalse("Version " + maxBranchVersion + " cannot be a snapshot because version " + v + " exists", maxBranchVersion.snapshot()); maxBranchVersions.put(branchName, v); } diff --git a/dev-tools/pom.xml b/dev-tools/pom.xml index 369c003beee..0daa4cbad51 100644 --- a/dev-tools/pom.xml +++ b/dev-tools/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.elasticsearch elasticsearch-dev-tools - 2.0.0-beta1-SNAPSHOT + 2.0.0-SNAPSHOT Elasticsearch Build Resources org.sonatype.oss diff --git a/distribution/deb/pom.xml b/distribution/deb/pom.xml index 6ccb9045d72..34fc6c95b8d 100644 --- a/distribution/deb/pom.xml +++ b/distribution/deb/pom.xml @@ -6,7 +6,7 @@ org.elasticsearch.distribution elasticsearch-distribution - 2.0.0-beta1-SNAPSHOT + 2.0.0-SNAPSHOT elasticsearch-deb diff --git a/distribution/fully-loaded/pom.xml b/distribution/fully-loaded/pom.xml index babca43359e..ff3d69a5ab7 100644 --- a/distribution/fully-loaded/pom.xml +++ b/distribution/fully-loaded/pom.xml @@ -6,7 +6,7 @@ org.elasticsearch.distribution elasticsearch-distribution - 2.0.0-beta1-SNAPSHOT + 2.0.0-SNAPSHOT elasticsearch-fully-loaded diff --git a/distribution/pom.xml b/distribution/pom.xml index 6e3ae4e78c7..0190c3b38c3 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -6,7 +6,7 @@ org.elasticsearch elasticsearch-parent - 2.0.0-beta1-SNAPSHOT + 2.0.0-SNAPSHOT org.elasticsearch.distribution diff --git a/distribution/rpm/pom.xml b/distribution/rpm/pom.xml index d596dbfcab8..8c80406f167 100644 --- a/distribution/rpm/pom.xml +++ b/distribution/rpm/pom.xml @@ -6,7 +6,7 @@ org.elasticsearch.distribution elasticsearch-distribution - 2.0.0-beta1-SNAPSHOT + 2.0.0-SNAPSHOT elasticsearch-rpm diff --git a/distribution/shaded/pom.xml b/distribution/shaded/pom.xml index f7704f94e9a..5426572f65d 100644 --- a/distribution/shaded/pom.xml +++ b/distribution/shaded/pom.xml @@ -6,7 +6,7 @@ org.elasticsearch.distribution elasticsearch-distribution - 2.0.0-beta1-SNAPSHOT + 2.0.0-SNAPSHOT elasticsearch-shaded diff --git a/distribution/tar/pom.xml b/distribution/tar/pom.xml index f62803ce499..aea4f4875d8 100644 --- a/distribution/tar/pom.xml +++ b/distribution/tar/pom.xml @@ -6,7 +6,7 @@ org.elasticsearch.distribution elasticsearch-distribution - 2.0.0-beta1-SNAPSHOT + 2.0.0-SNAPSHOT elasticsearch-tar diff --git a/distribution/zip/pom.xml b/distribution/zip/pom.xml index 4d3f19d93e1..c94a7bdd17d 100644 --- a/distribution/zip/pom.xml +++ b/distribution/zip/pom.xml @@ -6,7 +6,7 @@ org.elasticsearch.distribution elasticsearch-distribution - 2.0.0-beta1-SNAPSHOT + 2.0.0-SNAPSHOT elasticsearch-zip diff --git a/plugins/analysis-icu/pom.xml b/plugins/analysis-icu/pom.xml index 1214cbc82c0..d0679295787 100644 --- a/plugins/analysis-icu/pom.xml +++ b/plugins/analysis-icu/pom.xml @@ -7,7 +7,7 @@ org.elasticsearch.plugin elasticsearch-plugin - 2.0.0-beta1-SNAPSHOT + 2.0.0-SNAPSHOT elasticsearch-analysis-icu diff --git a/plugins/analysis-kuromoji/pom.xml b/plugins/analysis-kuromoji/pom.xml index 7cb0fa762ea..1ac11b858b6 100644 --- a/plugins/analysis-kuromoji/pom.xml +++ b/plugins/analysis-kuromoji/pom.xml @@ -7,7 +7,7 @@ org.elasticsearch.plugin elasticsearch-plugin - 2.0.0-beta1-SNAPSHOT + 2.0.0-SNAPSHOT elasticsearch-analysis-kuromoji diff --git a/plugins/analysis-phonetic/pom.xml b/plugins/analysis-phonetic/pom.xml index f5bafe2b865..f355a255a3f 100644 --- a/plugins/analysis-phonetic/pom.xml +++ b/plugins/analysis-phonetic/pom.xml @@ -7,7 +7,7 @@ org.elasticsearch.plugin elasticsearch-plugin - 2.0.0-beta1-SNAPSHOT + 2.0.0-SNAPSHOT elasticsearch-analysis-phonetic diff --git a/plugins/analysis-smartcn/pom.xml b/plugins/analysis-smartcn/pom.xml index 178d5f142a6..b0ed1943d23 100644 --- a/plugins/analysis-smartcn/pom.xml +++ b/plugins/analysis-smartcn/pom.xml @@ -7,7 +7,7 @@ org.elasticsearch.plugin elasticsearch-plugin - 2.0.0-beta1-SNAPSHOT + 2.0.0-SNAPSHOT elasticsearch-analysis-smartcn diff --git a/plugins/analysis-stempel/pom.xml b/plugins/analysis-stempel/pom.xml index 4f8f59c0611..febe5eb94ab 100644 --- a/plugins/analysis-stempel/pom.xml +++ b/plugins/analysis-stempel/pom.xml @@ -7,7 +7,7 @@ org.elasticsearch.plugin elasticsearch-plugin - 2.0.0-beta1-SNAPSHOT + 2.0.0-SNAPSHOT elasticsearch-analysis-stempel diff --git a/plugins/cloud-aws/pom.xml b/plugins/cloud-aws/pom.xml index 35edcf5d238..04417a9b724 100644 --- a/plugins/cloud-aws/pom.xml +++ b/plugins/cloud-aws/pom.xml @@ -7,7 +7,7 @@ org.elasticsearch.plugin elasticsearch-plugin - 2.0.0-beta1-SNAPSHOT + 2.0.0-SNAPSHOT elasticsearch-cloud-aws diff --git a/plugins/cloud-azure/pom.xml b/plugins/cloud-azure/pom.xml index c339c114cb1..ff67fc31600 100644 --- a/plugins/cloud-azure/pom.xml +++ b/plugins/cloud-azure/pom.xml @@ -18,7 +18,7 @@ governing permissions and limitations under the License. --> org.elasticsearch.plugin elasticsearch-plugin - 2.0.0-beta1-SNAPSHOT + 2.0.0-SNAPSHOT elasticsearch-cloud-azure diff --git a/plugins/cloud-gce/pom.xml b/plugins/cloud-gce/pom.xml index 48a7b26542e..f0133c8ef6e 100644 --- a/plugins/cloud-gce/pom.xml +++ b/plugins/cloud-gce/pom.xml @@ -18,7 +18,7 @@ governing permissions and limitations under the License. --> org.elasticsearch.plugin elasticsearch-plugin - 2.0.0-beta1-SNAPSHOT + 2.0.0-SNAPSHOT elasticsearch-cloud-gce diff --git a/plugins/delete-by-query/pom.xml b/plugins/delete-by-query/pom.xml index 62ee8fdb9b1..222d00585ff 100644 --- a/plugins/delete-by-query/pom.xml +++ b/plugins/delete-by-query/pom.xml @@ -18,7 +18,7 @@ governing permissions and limitations under the License. --> org.elasticsearch.plugin elasticsearch-plugin - 2.0.0-beta1-SNAPSHOT + 2.0.0-SNAPSHOT elasticsearch-delete-by-query diff --git a/plugins/lang-javascript/pom.xml b/plugins/lang-javascript/pom.xml index eb6e21cd704..e1ae181e7c1 100644 --- a/plugins/lang-javascript/pom.xml +++ b/plugins/lang-javascript/pom.xml @@ -7,7 +7,7 @@ org.elasticsearch.plugin elasticsearch-plugin - 2.0.0-beta1-SNAPSHOT + 2.0.0-SNAPSHOT elasticsearch-lang-javascript diff --git a/plugins/lang-python/pom.xml b/plugins/lang-python/pom.xml index b69a07b0ed7..a88bdd66ac0 100644 --- a/plugins/lang-python/pom.xml +++ b/plugins/lang-python/pom.xml @@ -7,7 +7,7 @@ org.elasticsearch.plugin elasticsearch-plugin - 2.0.0-beta1-SNAPSHOT + 2.0.0-SNAPSHOT elasticsearch-lang-python diff --git a/plugins/pom.xml b/plugins/pom.xml index b007dd7c72a..4bc761b14b5 100644 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -7,7 +7,7 @@ org.elasticsearch.plugin elasticsearch-plugin - 2.0.0-beta1-SNAPSHOT + 2.0.0-SNAPSHOT pom Elasticsearch Plugin POM 2009 @@ -15,7 +15,7 @@ org.elasticsearch elasticsearch-parent - 2.0.0-beta1-SNAPSHOT + 2.0.0-SNAPSHOT diff --git a/plugins/site-example/pom.xml b/plugins/site-example/pom.xml index 15060e0bd52..0a6beb1a0b0 100644 --- a/plugins/site-example/pom.xml +++ b/plugins/site-example/pom.xml @@ -7,7 +7,7 @@ org.elasticsearch.plugin elasticsearch-plugin - 2.0.0-beta1-SNAPSHOT + 2.0.0-SNAPSHOT elasticsearch-site-example diff --git a/pom.xml b/pom.xml index 501f5a457c0..366eb021b98 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.elasticsearch elasticsearch-parent - 2.0.0-beta1-SNAPSHOT + 2.0.0-SNAPSHOT pom Elasticsearch Parent POM Elasticsearch Parent POM diff --git a/rest-api-spec/pom.xml b/rest-api-spec/pom.xml index 3b14e1722cd..46f2e6e7213 100644 --- a/rest-api-spec/pom.xml +++ b/rest-api-spec/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.elasticsearch elasticsearch-rest-api-spec - 2.0.0-beta1-SNAPSHOT + 2.0.0-SNAPSHOT Elasticsearch Rest API Spec org.sonatype.oss From 20a5fc7e30e00c33012e108c84b5bb8aabb618ea Mon Sep 17 00:00:00 2001 From: Clinton Gormley Date: Tue, 4 Aug 2015 13:07:58 +0200 Subject: [PATCH 05/23] The check-license script now accepts either a directory or a .zip file only Called as: check_license_and_sha.pl --check path/to/licenses path/to/extracted/package/ or check_license_and_sha.pl --check path/to/licenses path/to/file.zip --- .../license-check/check_license_and_sha.pl | 123 +-- .../resources/license-check/lib/Archive/Ar.pm | 806 ------------------ 2 files changed, 37 insertions(+), 892 deletions(-) delete mode 100644 dev-tools/src/main/resources/license-check/lib/Archive/Ar.pm diff --git a/dev-tools/src/main/resources/license-check/check_license_and_sha.pl b/dev-tools/src/main/resources/license-check/check_license_and_sha.pl index 9648a1f21a8..6fc5d2dfa22 100755 --- a/dev-tools/src/main/resources/license-check/check_license_and_sha.pl +++ b/dev-tools/src/main/resources/license-check/check_license_and_sha.pl @@ -6,37 +6,29 @@ use v5.10; use FindBin qw($RealBin); use lib "$RealBin/lib"; -use Archive::Ar(); -use Cwd(); use File::Spec(); -use Digest::SHA qw(sha1); use File::Temp(); +use Digest::SHA qw(sha1); use File::Basename qw(basename); use Archive::Extract(); $Archive::Extract::PREFER_BIN = 1; -our %Extract_Package = ( - zip => \&extract_zip, - gz => \&extract_tar_gz, - rpm => \&extract_rpm, - deb => \&extract_deb -); - my $mode = shift(@ARGV) || ""; die usage() unless $mode =~ /^--(check|update)$/; my $License_Dir = shift(@ARGV) || die usage(); -my $Package = shift(@ARGV) || die usage(); +my $Source = shift(@ARGV) || die usage(); $License_Dir = File::Spec->rel2abs($License_Dir) . '/'; -$Package = File::Spec->rel2abs($Package); +$Source = File::Spec->rel2abs($Source); die "License dir is not a directory: $License_Dir\n" . usage() unless -d $License_Dir; -die "Package is not a file: $Package\n" . usage() - unless -f $Package; +my %shas + = -f $Source ? jars_from_zip($Source) + : -d $Source ? jars_from_dir($Source) + : die "Source is neither a directory nor a zip file: $Source" . usage(); -my %shas = get_shas_from_package($Package); $mode eq '--check' ? exit check_shas_and_licenses(%shas) : exit write_shas(%shas); @@ -109,12 +101,14 @@ sub check_shas_and_licenses { my @unused_licenses = grep { !$licenses{$_} } keys %licenses; if (@unused_licenses) { + $error++; say STDERR "Extra LICENCE file present: " . join ", ", sort @unused_licenses; } my @unused_notices = grep { !$notices{$_} } keys %notices; if (@unused_notices) { + $error++; say STDERR "Extra NOTICE file present: " . join ", ", sort @unused_notices; } @@ -124,7 +118,7 @@ sub check_shas_and_licenses { You can update the SHA files by running: -$0 --update $License_Dir $Package +$0 --update $License_Dir $Source SHAS } @@ -194,82 +188,37 @@ sub get_sha_files { } #=================================== -sub get_shas_from_package { +sub jars_from_zip { #=================================== - my $package = shift; - my ($type) = ( $package =~ /\.(\w+)$/ ); - die "Unrecognised package type: $package" - unless $type && $Extract_Package{$type}; - + my ($source) = @_; my $temp_dir = File::Temp->newdir; - my $files - = eval { $Extract_Package{$type}->( $package, $temp_dir->dirname ) } - or die "Couldn't extract $package: $@"; - - my @jars = map {"$temp_dir/$_"} - grep { /\.jar$/ && !/elasticsearch[^\/]*$/ } @$files; + my $dir_name = $temp_dir->dirname; + my $archive = Archive::Extract->new( archive => $source, type => 'zip' ); + $archive->extract( to => $dir_name ) || die $archive->error; + my @jars = map { File::Spec->rel2abs( $_, $dir_name ) } + grep { /\.jar$/ && !/elasticsearch[^\/]*$/ } @{ $archive->files }; + die "No JARS found in: $source\n" + unless @jars; return calculate_shas(@jars); } #=================================== -sub extract_zip { +sub jars_from_dir { #=================================== - my ( $package, $dir ) = @_; - my $archive = Archive::Extract->new( archive => $package, type => 'zip' ); - $archive->extract( to => $dir ) || die $archive->error; - return $archive->files; -} - -#=================================== -sub extract_tar_gz { -#=================================== - my ( $package, $dir ) = @_; - my $archive = Archive::Extract->new( archive => $package, type => 'tgz' ); - $archive->extract( to => $dir ) || die $archive->error; - return $archive->files; -} - -#=================================== -sub extract_rpm { -#=================================== - my ( $package, $dir ) = @_; - my $cwd = Cwd::cwd(); - my @files; - eval { - chdir $dir; - say "Trying with rpm2cpio"; - my $out = eval {`rpm2cpio '$package' | cpio -idmv --quiet`}; - unless ($out) { - say "Trying with rpm2cpio.pl"; - $out = eval {`rpm2cpio.pl '$package' | cpio -idmv --quiet`}; - } - @files = split "\n", $out if $out; - }; - chdir $cwd; - die $@ if $@; - die "Couldn't extract $package\n" unless @files; - return \@files; -} - -#=================================== -sub extract_deb { -#=================================== - my ( $package, $dir ) = @_; - my $archive = Archive::Ar->new; - $archive->read($package) || die $archive->error; - my $cwd = Cwd::cwd(); - eval { - chdir $dir; - $archive->extract('data.tar.gz') || die $archive->error; - }; - chdir $cwd; - die $@ if $@; - $archive = Archive::Extract->new( - archive => $dir . '/data.tar.gz', - type => 'tgz' + my $source = shift; + my @jars; + File::Find::find( + { wanted => sub { + push @jars, File::Spec->rel2abs( $_, $source ) + if /\.jar$/ && !/elasticsearch[^\/]*$/; + }, + no_chdir => 1 + }, + $source ); - $archive->extract( to => $dir ) || die $archive->error; - return $archive->files; + die "No JARS found in: $source\n" + unless @jars; + return calculate_shas(@jars); } #=================================== @@ -291,11 +240,13 @@ sub usage { USAGE: - # check the sha1 and LICENSE files for each jar in the zip|gz|deb|rpm + # check the sha1 and LICENSE files for each jar in the zip or directory $0 --check path/to/licenses/ path/to/package.zip + $0 --check path/to/licenses/ path/to/dir/ - # updates the sha1s for each jar in the zip|gz|deb|rpm + # updates the sha1s for each jar in the zip or directory $0 --update path/to/licenses/ path/to/package.zip + $0 --update path/to/licenses/ path/to/dir/ USAGE diff --git a/dev-tools/src/main/resources/license-check/lib/Archive/Ar.pm b/dev-tools/src/main/resources/license-check/lib/Archive/Ar.pm deleted file mode 100644 index 6d6439b37b4..00000000000 --- a/dev-tools/src/main/resources/license-check/lib/Archive/Ar.pm +++ /dev/null @@ -1,806 +0,0 @@ -########################################################### -# Archive::Ar - Pure perl module to handle ar achives -# -# Copyright 2003 - Jay Bonci -# Copyright 2014 - John Bazik -# Licensed under the same terms as perl itself -# -########################################################### -package Archive::Ar; - -use base qw(Exporter); -our @EXPORT_OK = qw(COMMON BSD GNU); - -use strict; -use File::Spec; -use Time::Local; -use Carp qw(carp longmess); - -use vars qw($VERSION); -$VERSION = '2.02'; - -use constant CAN_CHOWN => ($> == 0 and $^O ne 'MacOS' and $^O ne 'MSWin32'); - -use constant ARMAG => "!\n"; -use constant SARMAG => length(ARMAG); -use constant ARFMAG => "`\n"; -use constant AR_EFMT1 => "#1/"; - -use constant COMMON => 1; -use constant BSD => 2; -use constant GNU => 3; - -my $has_io_string; -BEGIN { - $has_io_string = eval { - require IO::String; - IO::String->import(); - 1; - } || 0; -} - -sub new { - my $class = shift; - my $file = shift; - my $opts = shift || 0; - my $self = bless {}, $class; - my $defopts = { - chmod => 1, - chown => 1, - same_perms => ($> == 0) ? 1:0, - symbols => undef, - }; - $opts = {warn => $opts} unless ref $opts; - - $self->clear(); - $self->{opts} = {(%$defopts, %{$opts})}; - if ($file) { - return unless $self->read($file); - } - return $self; -} - -sub set_opt { - my $self = shift; - my $name = shift; - my $val = shift; - - $self->{opts}->{$name} = $val; -} - -sub get_opt { - my $self = shift; - my $name = shift; - - return $self->{opts}->{$name}; -} - -sub type { - return shift->{type}; -} - -sub clear { - my $self = shift; - - $self->{names} = []; - $self->{files} = {}; - $self->{type} = undef; -} - -sub read { - my $self = shift; - my $file = shift; - - my $fh = $self->_get_handle($file); - local $/ = undef; - my $data = <$fh>; - close $fh; - - return $self->read_memory($data); -} - -sub read_memory { - my $self = shift; - my $data = shift; - - $self->clear(); - return unless $self->_parse($data); - return length($data); -} - -sub contains_file { - my $self = shift; - my $filename = shift; - - return unless defined $filename; - return exists $self->{files}->{$filename}; -} - -sub extract { - my $self = shift; - - for my $filename (@_ ? @_ : @{$self->{names}}) { - $self->extract_file($filename) or return; - } - return 1; -} - -sub extract_file { - my $self = shift; - my $filename = shift; - my $target = shift || $filename; - - my $meta = $self->{files}->{$filename}; - return $self->_error("$filename: not in archive") unless $meta; - open my $fh, '>', $target or return $self->_error("$target: $!"); - binmode $fh; - syswrite $fh, $meta->{data} or return $self->_error("$filename: $!"); - close $fh or return $self->_error("$filename: $!"); - if (CAN_CHOWN && $self->{opts}->{chown}) { - chown $meta->{uid}, $meta->{gid}, $filename or - return $self->_error("$filename: $!"); - } - if ($self->{opts}->{chmod}) { - my $mode = $meta->{mode}; - unless ($self->{opts}->{same_perms}) { - $mode &= ~(oct(7000) | (umask | 0)); - } - chmod $mode, $filename or return $self->_error("$filename: $!"); - } - utime $meta->{date}, $meta->{date}, $filename or - return $self->_error("$filename: $!"); - return 1; -} - -sub rename { - my $self = shift; - my $filename = shift; - my $target = shift; - - if ($self->{files}->{$filename}) { - $self->{files}->{$target} = $self->{files}->{$filename}; - delete $self->{files}->{$filename}; - for (@{$self->{names}}) { - if ($_ eq $filename) { - $_ = $target; - last; - } - } - } -} - -sub chmod { - my $self = shift; - my $filename = shift; - my $mode = shift; # octal string or numeric - - return unless $self->{files}->{$filename}; - $self->{files}->{$filename}->{mode} = - $mode + 0 eq $mode ? $mode : oct($mode); - return 1; -} - -sub chown { - my $self = shift; - my $filename = shift; - my $uid = shift; - my $gid = shift; - - return unless $self->{files}->{$filename}; - $self->{files}->{$filename}->{uid} = $uid if $uid >= 0; - $self->{files}->{$filename}->{gid} = $gid if defined $gid && $gid >= 0; - return 1; -} - -sub remove { - my $self = shift; - my $files = ref $_[0] ? shift : \@_; - - my $nfiles_orig = scalar @{$self->{names}}; - - for my $file (@$files) { - next unless $file; - if (exists($self->{files}->{$file})) { - delete $self->{files}->{$file}; - } - else { - $self->_error("$file: no such member") - } - } - @{$self->{names}} = grep($self->{files}->{$_}, @{$self->{names}}); - - return $nfiles_orig - scalar @{$self->{names}}; -} - -sub list_files { - my $self = shift; - - return wantarray ? @{$self->{names}} : $self->{names}; -} - -sub add_files { - my $self = shift; - my $files = ref $_[0] ? shift : \@_; - - for my $path (@$files) { - if (open my $fd, $path) { - my @st = stat $fd or return $self->_error("$path: $!"); - local $/ = undef; - binmode $fd; - my $content = <$fd>; - close $fd; - - my $filename = (File::Spec->splitpath($path))[2]; - - $self->_add_data($filename, $content, @st[9,4,5,2,7]); - } - else { - $self->_error("$path: $!"); - } - } - return scalar @{$self->{names}}; -} - -sub add_data { - my $self = shift; - my $path = shift; - my $content = shift; - my $params = shift || {}; - - return $self->_error("No filename given") unless $path; - - my $filename = (File::Spec->splitpath($path))[2]; - - $self->_add_data($filename, $content, - $params->{date} || timelocal(localtime()), - $params->{uid} || 0, - $params->{gid} || 0, - $params->{mode} || 0100644) or return; - - return $self->{files}->{$filename}->{size}; -} - -sub write { - my $self = shift; - my $filename = shift; - my $opts = {(%{$self->{opts}}, %{shift || {}})}; - my $type = $opts->{type} || $self->{type} || COMMON; - - my @body = ( ARMAG ); - - my %gnuindex; - my @filenames = @{$self->{names}}; - if ($type eq GNU) { - # - # construct extended filename index, if needed - # - if (my @longs = grep(length($_) > 15, @filenames)) { - my $ptr = 0; - for my $long (@longs) { - $gnuindex{$long} = $ptr; - $ptr += length($long) + 2; - } - push @body, pack('A16A32A10A2', '//', '', $ptr, ARFMAG), - join("/\n", @longs, ''); - push @body, "\n" if $ptr % 2; # padding - } - } - for my $fn (@filenames) { - my $meta = $self->{files}->{$fn}; - my $mode = sprintf('%o', $meta->{mode}); - my $size = $meta->{size}; - my $name; - - if ($type eq GNU) { - $fn = '' if defined $opts->{symbols} && $fn eq $opts->{symbols}; - $name = $fn . '/'; - } - else { - $name = $fn; - } - if (length($name) <= 16 || $type eq COMMON) { - push @body, pack('A16A12A6A6A8A10A2', $name, - @$meta{qw/date uid gid/}, $mode, $size, ARFMAG); - } - elsif ($type eq GNU) { - push @body, pack('A1A15A12A6A6A8A10A2', '/', $gnuindex{$fn}, - @$meta{qw/date uid gid/}, $mode, $size, ARFMAG); - } - elsif ($type eq BSD) { - $size += length($name); - push @body, pack('A3A13A12A6A6A8A10A2', AR_EFMT1, length($name), - @$meta{qw/date uid gid/}, $mode, $size, ARFMAG), - $name; - } - else { - return $self->_error("$type: unexpected ar type"); - } - push @body, $meta->{data}; - push @body, "\n" if $size % 2; # padding - } - if ($filename) { - my $fh = $self->_get_handle($filename, '>'); - print $fh @body; - close $fh; - my $len = 0; - $len += length($_) for @body; - return $len; - } - else { - return join '', @body; - } -} - -sub get_content { - my $self = shift; - my ($filename) = @_; - - unless ($filename) { - $self->_error("get_content can't continue without a filename"); - return; - } - - unless (exists($self->{files}->{$filename})) { - $self->_error( - "get_content failed because there is not a file named $filename"); - return; - } - - return $self->{files}->{$filename}; -} - -sub get_data { - my $self = shift; - my $filename = shift; - - return $self->_error("$filename: no such member") - unless exists $self->{files}->{$filename}; - return $self->{files}->{$filename}->{data}; -} - -sub get_handle { - my $self = shift; - my $filename = shift; - my $fh; - - return $self->_error("$filename: no such member") - unless exists $self->{files}->{$filename}; - if ($has_io_string) { - $fh = IO::String->new($self->{files}->{$filename}->{data}); - } - else { - my $data = $self->{files}->{$filename}->{data}; - open $fh, '<', \$data or return $self->_error("in-memory file: $!"); - } - return $fh; -} - -sub error { - my $self = shift; - - return shift() ? $self->{longmess} : $self->{error}; -} - -# -# deprecated -# -sub DEBUG { - my $self = shift; - my $debug = shift; - - $self->{opts}->{warn} = 1 unless (defined($debug) and int($debug) == 0); -} - -sub _parse { - my $self = shift; - my $data = shift; - - unless (substr($data, 0, SARMAG, '') eq ARMAG) { - return $self->_error("Bad magic number - not an ar archive"); - } - my $type; - my $names; - while ($data =~ /\S/) { - my ($name, $date, $uid, $gid, $mode, $size, $magic) = - unpack('A16A12A6A6A8A10a2', substr($data, 0, 60, '')); - unless ($magic eq "`\n") { - return $self->_error("Bad file header"); - } - if ($name =~ m|^/|) { - $type = GNU; - if ($name eq '//') { - $names = substr($data, 0, $size, ''); - substr($data, 0, $size % 2, ''); - next; - } - elsif ($name eq '/') { - $name = $self->{opts}->{symbols}; - unless (defined $name && $name) { - substr($data, 0, $size + $size % 2, ''); - next; - } - } - else { - $name = substr($names, int(substr($name, 1))); - $name =~ s/\n.*//; - chop $name; - } - } - elsif ($name =~ m|^#1/|) { - $type = BSD; - $name = substr($data, 0, int(substr($name, 3)), ''); - $size -= length($name); - } - else { - if ($name =~ m|/$|) { - $type ||= GNU; # only gnu has trailing slashes - chop $name; - } - } - $uid = int($uid); - $gid = int($gid); - $mode = oct($mode); - my $content = substr($data, 0, $size, ''); - substr($data, 0, $size % 2, ''); - - $self->_add_data($name, $content, $date, $uid, $gid, $mode, $size); - } - $self->{type} = $type || COMMON; - return scalar @{$self->{names}}; -} - -sub _add_data { - my $self = shift; - my $filename = shift; - my $content = shift || ''; - my $date = shift; - my $uid = shift; - my $gid = shift; - my $mode = shift; - my $size = shift; - - if (exists($self->{files}->{$filename})) { - return $self->_error("$filename: entry already exists"); - } - $self->{files}->{$filename} = { - name => $filename, - date => defined $date ? $date : timelocal(localtime()), - uid => defined $uid ? $uid : 0, - gid => defined $gid ? $gid : 0, - mode => defined $mode ? $mode : 0100644, - size => defined $size ? $size : length($content), - data => $content, - }; - push @{$self->{names}}, $filename; - return 1; -} - -sub _get_handle { - my $self = shift; - my $file = shift; - my $mode = shift || '<'; - - if (ref $file) { - return $file if eval{*$file{IO}} or $file->isa('IO::Handle'); - return $self->_error("Not a filehandle"); - } - else { - open my $fh, $mode, $file or return $self->_error("$file: $!"); - binmode $fh; - return $fh; - } -} - -sub _error { - my $self = shift; - my $msg = shift; - - $self->{error} = $msg; - $self->{longerror} = longmess($msg); - if ($self->{opts}->{warn} > 1) { - carp $self->{longerror}; - } - elsif ($self->{opts}->{warn}) { - carp $self->{error}; - } - return; -} - -1; - -__END__ - -=head1 NAME - -Archive::Ar - Interface for manipulating ar archives - -=head1 SYNOPSIS - - use Archive::Ar; - - my $ar = Archive::Ar->new; - - $ar->read('./foo.ar'); - $ar->extract; - - $ar->add_files('./bar.tar.gz', 'bat.pl') - $ar->add_data('newfile.txt','Some contents'); - - $ar->chmod('file1', 0644); - $ar->chown('file1', $uid, $gid); - - $ar->remove('file1', 'file2'); - - my $filehash = $ar->get_content('bar.tar.gz'); - my $data = $ar->get_data('bar.tar.gz'); - my $handle = $ar->get_handle('bar.tar.gz'); - - my @files = $ar->list_files(); - - my $archive = $ar->write; - my $size = $ar->write('outbound.ar'); - - $ar->error(); - - -=head1 DESCRIPTION - -Archive::Ar is a pure-perl way to handle standard ar archives. - -This is useful if you have those types of archives on the system, but it -is also useful because .deb packages for the Debian GNU/Linux distribution are -ar archives. This is one building block in a future chain of modules to build, -manipulate, extract, and test debian modules with no platform or architecture -dependence. - -You may notice that the API to Archive::Ar is similar to Archive::Tar, and -this was done intentionally to keep similarity between the Archive::* -modules. - -=head1 METHODS - -=head2 new - - $ar = Archive::Ar->new() - $ar = Archive::Ar->new($filename) - $ar = Archive::Ar->new($filehandle) - -Returns a new Archive::Ar object. Without an argument, it returns -an empty object. If passed a filename or an open filehandle, it will -read the referenced archive into memory. If the read fails for any -reason, returns undef. - -=head2 set_opt - - $ar->set_opt($name, $val) - -Assign option $name value $val. Possible options are: - -=over 4 - -=item * warn - -Warning level. Levels are zero for no warnings, 1 for brief warnings, -and 2 for warnings with a stack trace. Default is zero. - -=item * chmod - -Change the file permissions of files created when extracting. Default -is true (non-zero). - -=item * same_perms - -When setting file permissions, use the values in the archive unchanged. -If false, removes setuid bits and applies the user's umask. Default is -true for the root user, false otherwise. - -=item * chown - -Change the owners of extracted files, if possible. Default is true. - -=item * type - -Archive type. May be GNU, BSD or COMMON, or undef if no archive has -been read. Defaults to the type of the archive read, or undef. - -=item * symbols - -Provide a filename for the symbol table, if present. If set, the symbol -table is treated as a file that can be read from or written to an archive. -It is an error if the filename provided matches the name of a file in the -archive. If undefined, the symbol table is ignored. Defaults to undef. - -=back - -=head2 get_opt - - $val = $ar->get_opt($name) - -Returns the value of option $name. - -=head2 type - - $type = $ar->type() - -Returns the type of the ar archive. The type is undefined until an -archive is loaded. If the archive displays characteristics of a gnu-style -archive, GNU is returned. If it looks like a bsd-style archive, BSD -is returned. Otherwise, COMMON is returned. Note that unless filenames -exceed 16 characters in length, bsd archives look like the common format. - -=head2 clear - - $ar->clear() - -Clears the current in-memory archive. - -=head2 read - - $len = $ar->read($filename) - $len = $ar->read($filehandle) - -This reads a new file into the object, removing any ar archive already -represented in the object. The argument may be a filename, filehandle -or IO::Handle object. Returns the size of the file contents or undef -if it fails. - -=head2 read_memory - - $len = $ar->read_memory($data) - -Parses the string argument as an archive, reading it into memory. Replaces -any previously loaded archive. Returns the number of bytes read, or undef -if it fails. - -=head2 contains_file - - $bool = $ar->contains_file($filename) - -Returns true if the archive contains a file with $filename. Returns -undef otherwise. - -=head2 extract - - $ar->extract() - $ar->extract_file($filename) - -Extracts files from the archive. The first form extracts all files, the -latter extracts just the named file. Extracted files are assigned the -permissions and modification time stored in the archive, and, if possible, -the user and group ownership. Returns non-zero upon success, or undef if -failure. - -=head2 rename - - $ar->rename($filename, $newname) - -Changes the name of a file in the in-memory archive. - -=head2 chmod - - $ar->chmod($filename, $mode); - -Change the mode of the member to C<$mode>. - -=head2 chown - - $ar->chown($filename, $uid, $gid); - $ar->chown($filename, $uid); - -Change the ownership of the member to user id C<$uid> and (optionally) -group id C<$gid>. Negative id values are ignored. - -=head2 remove - - $ar->remove(@filenames) - $ar->remove($arrayref) - -Removes files from the in-memory archive. Returns the number of files -removed. - -=head2 list_files - - @filenames = $ar->list_files() - -Returns a list of the names of all the files in the archive. -If called in a scalar context, returns a reference to an array. - -=head2 add_files - - $ar->add_files(@filenames) - $ar->add_files($arrayref) - -Adds files to the archive. The arguments can be paths, but only the -filenames are stored in the archive. Stores the uid, gid, mode, size, -and modification timestamp of the file as returned by C. - -Returns the number of files successfully added, or undef if failure. - -=head2 add_data - - $ar->add_data("filename", $data) - $ar->add_data("filename", $data, $options) - -Adds a file to the in-memory archive with name $filename and content -$data. File properties can be set with $optional_hashref: - - $options = { - 'data' => $data, - 'uid' => $uid, #defaults to zero - 'gid' => $gid, #defaults to zero - 'date' => $date, #date in epoch seconds. Defaults to now. - 'mode' => $mode, #defaults to 0100644; - } - -You cannot add_data over another file however. This returns the file length in -bytes if it is successful, undef otherwise. - -=head2 write - - $data = $ar->write() - $len = $ar->write($filename) - -Returns the archive as a string, or writes it to disk as $filename. -Returns the archive size upon success when writing to disk. Returns -undef if failure. - -=head2 get_content - - $content = $ar->get_content($filename) - -This returns a hash with the file content in it, including the data -that the file would contain. If the file does not exist or no filename -is given, this returns undef. On success, a hash is returned: - - $content = { - 'name' => $filename, - 'date' => $mtime, - 'uid' => $uid, - 'gid' => $gid, - 'mode' => $mode, - 'size' => $size, - 'data' => $file_contents, - } - -=head2 get_data - - $data = $ar->get_data("filename") - -Returns a scalar containing the file data of the given archive -member. Upon error, returns undef. - -=head2 get_handle - - $handle = $ar->get_handle("filename")> - -Returns a file handle to the in-memory file data of the given archive member. -Upon error, returns undef. This can be useful for unpacking nested archives. -Uses IO::String if it's loaded. - -=head2 error - - $errstr = $ar->error($trace) - -Returns the current error string, which is usually the last error reported. -If a true value is provided, returns the error message and stack trace. - -=head1 BUGS - -See https://github.com/jbazik/Archive-Ar/issues/ to report and view bugs. - -=head1 SOURCE - -The source code repository for Archive::Ar can be found at http://github.com/jbazik/Archive-Ar/. - -=head1 COPYRIGHT - -Copyright 2009-2014 John Bazik Ejbazik@cpan.orgE. - -Copyright 2003 Jay Bonci Ejaybonci@cpan.orgE. - -This program is free software; you can redistribute it and/or modify it under -the same terms as Perl itself. - -See http://www.perl.com/perl/misc/Artistic.html - -=cut From 5059c978e36eca0e9be23e8f5c3716e427afc59b Mon Sep 17 00:00:00 2001 From: Robert Muir Date: Tue, 4 Aug 2015 07:54:55 -0400 Subject: [PATCH 06/23] Fix distribution checks to pass unzipped directory to license checker --- distribution/deb/pom.xml | 23 ----------------------- distribution/pom.xml | 28 ++++++++++++++++++++++++++-- distribution/rpm/pom.xml | 25 ------------------------- distribution/tar/pom.xml | 23 ----------------------- distribution/zip/pom.xml | 23 ----------------------- 5 files changed, 26 insertions(+), 96 deletions(-) diff --git a/distribution/deb/pom.xml b/distribution/deb/pom.xml index 34fc6c95b8d..cbe2631cf8a 100644 --- a/distribution/deb/pom.xml +++ b/distribution/deb/pom.xml @@ -256,30 +256,7 @@ org.apache.maven.plugins maven-antrun-plugin - 1.8 - - check-license - verify - - run - - - - - - - Running license check - - - - - - - - - - integ-setup diff --git a/distribution/pom.xml b/distribution/pom.xml index 0190c3b38c3..082d207855a 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -97,8 +97,32 @@ org.apache.maven.plugins maven-antrun-plugin - - + + + + check-license + verify + + run + + + ${skip.integ.tests} + + + + + Running license check + + + + + + + + + + org.apache.maven.plugins diff --git a/distribution/rpm/pom.xml b/distribution/rpm/pom.xml index 8c80406f167..2e71b8011d9 100644 --- a/distribution/rpm/pom.xml +++ b/distribution/rpm/pom.xml @@ -311,32 +311,7 @@ org.apache.maven.plugins maven-antrun-plugin - 1.8 - - check-license - verify - - run - - - - true - - - - - Running license check - - - - - - - - - - integ-setup diff --git a/distribution/tar/pom.xml b/distribution/tar/pom.xml index aea4f4875d8..641de8577c2 100644 --- a/distribution/tar/pom.xml +++ b/distribution/tar/pom.xml @@ -49,30 +49,7 @@ org.apache.maven.plugins maven-antrun-plugin - 1.8 - - check-license - verify - - run - - - - - - - Running license check - - - - - - - - - - diff --git a/distribution/zip/pom.xml b/distribution/zip/pom.xml index c94a7bdd17d..b6b784a97fe 100644 --- a/distribution/zip/pom.xml +++ b/distribution/zip/pom.xml @@ -49,30 +49,7 @@ org.apache.maven.plugins maven-antrun-plugin - 1.8 - - check-license - verify - - run - - - - - - - Running license check - - - - - - - - - - From 2b2657279d040d4d69a81bb03a049c9400ce003d Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Tue, 4 Aug 2015 14:38:23 +0200 Subject: [PATCH 07/23] Use UTC instead of default timezone for creation date in CAT endpoint this change was added recently which uses default timezone for the creation date on CAT endpoints. We should be consistent and use UTC across the board. This commit adds #getDefaultTimzone() to forbidden API and fixes the REST tests. Relates to #11688 --- .../org/elasticsearch/rest/action/cat/RestIndicesAction.java | 2 +- dev-tools/src/main/resources/forbidden/all-signatures.txt | 3 ++- .../resources/rest-api-spec/test/cat.indices/10_basic.yaml | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/rest/action/cat/RestIndicesAction.java b/core/src/main/java/org/elasticsearch/rest/action/cat/RestIndicesAction.java index c3aa077b63d..3a5ea802b28 100644 --- a/core/src/main/java/org/elasticsearch/rest/action/cat/RestIndicesAction.java +++ b/core/src/main/java/org/elasticsearch/rest/action/cat/RestIndicesAction.java @@ -326,7 +326,7 @@ public class RestIndicesAction extends AbstractCatAction { table.addCell(indexStats == null ? null : indexStats.getPrimaries().getDocs().getDeleted()); table.addCell(indexMetaData.creationDate()); - table.addCell(new DateTime(indexMetaData.creationDate(), DateTimeZone.getDefault())); + table.addCell(new DateTime(indexMetaData.creationDate(), DateTimeZone.UTC)); table.addCell(indexStats == null ? null : indexStats.getTotal().getStore().size()); table.addCell(indexStats == null ? null : indexStats.getPrimaries().getStore().size()); diff --git a/dev-tools/src/main/resources/forbidden/all-signatures.txt b/dev-tools/src/main/resources/forbidden/all-signatures.txt index d33c4ee9b8b..985cdd5c5c0 100644 --- a/dev-tools/src/main/resources/forbidden/all-signatures.txt +++ b/dev-tools/src/main/resources/forbidden/all-signatures.txt @@ -46,13 +46,14 @@ java.nio.file.FileSystems#getDefault() @ use PathUtils.getDefault instead. java.nio.file.Files#createTempDirectory(java.lang.String,java.nio.file.attribute.FileAttribute[]) java.nio.file.Files#createTempFile(java.lang.String,java.lang.String,java.nio.file.attribute.FileAttribute[]) -@defaultMessage Constructing a DateTime without a time zone is dangerous - use DateTime(DateTimeZone.getDefault()) if you really want the default timezone +@defaultMessage Constructing a DateTime without a time zone is dangerous org.joda.time.DateTime#() org.joda.time.DateTime#(long) org.joda.time.DateTime#(int, int, int, int, int) org.joda.time.DateTime#(int, int, int, int, int, int) org.joda.time.DateTime#(int, int, int, int, int, int, int) org.joda.time.DateTime#now() +org.joda.time.DateTime#getDefault() com.google.common.collect.Iterators#emptyIterator() @ Use Collections.emptyIterator instead diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.indices/10_basic.yaml b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.indices/10_basic.yaml index 0d955852c07..d8beff155c7 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.indices/10_basic.yaml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.indices/10_basic.yaml @@ -46,8 +46,8 @@ /^( index1 \s+ (\d+) \s+ - (\d\d\d\d\-\d\d\-\d\dT\d\d:\d\d:\d\d.\d\d\d[+-]\d\d:\d\d) \s+ + (\d\d\d\d\-\d\d\-\d\dT\d\d:\d\d:\d\d.\d\d\dZ) \s+ (\d+) \s+ - (\d\d\d\d\-\d\d\-\d\dT\d\d:\d\d:\d\d.\d\d\d[+-]\d\d:\d\d) \s* + (\d\d\d\d\-\d\d\-\d\dT\d\d:\d\d:\d\d.\d\d\dZ) \s* ) $/ From d0bd769b85abaddfb8d432298326d6d8c1e01b79 Mon Sep 17 00:00:00 2001 From: Clinton Gormley Date: Tue, 4 Aug 2015 15:00:14 +0200 Subject: [PATCH 08/23] Added missing dependency to check-license script --- .../src/main/resources/license-check/check_license_and_sha.pl | 1 + 1 file changed, 1 insertion(+) diff --git a/dev-tools/src/main/resources/license-check/check_license_and_sha.pl b/dev-tools/src/main/resources/license-check/check_license_and_sha.pl index 6fc5d2dfa22..349ead39d3b 100755 --- a/dev-tools/src/main/resources/license-check/check_license_and_sha.pl +++ b/dev-tools/src/main/resources/license-check/check_license_and_sha.pl @@ -8,6 +8,7 @@ use FindBin qw($RealBin); use lib "$RealBin/lib"; use File::Spec(); use File::Temp(); +use File::Find(); use Digest::SHA qw(sha1); use File::Basename qw(basename); use Archive::Extract(); From cf6fe6cdcd7510cb88512404800aadb1d2eb8e53 Mon Sep 17 00:00:00 2001 From: Robert Muir Date: Tue, 4 Aug 2015 09:25:44 -0400 Subject: [PATCH 09/23] Don't use port 9200/9300 for integration tests --- dev-tools/src/main/resources/ant/integration-tests.xml | 3 ++- distribution/pom.xml | 2 +- distribution/zip/pom.xml | 6 +++++- plugins/pom.xml | 2 +- pom.xml | 2 ++ 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/dev-tools/src/main/resources/ant/integration-tests.xml b/dev-tools/src/main/resources/ant/integration-tests.xml index fa454983130..1ec1fdf0a1d 100644 --- a/dev-tools/src/main/resources/ant/integration-tests.xml +++ b/dev-tools/src/main/resources/ant/integration-tests.xml @@ -16,6 +16,7 @@ @@ -122,7 +123,7 @@ args="@{args} -Des.path.repo=@{home}/repo" /> - + diff --git a/distribution/pom.xml b/distribution/pom.xml index 082d207855a..62a764829c8 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -167,7 +167,7 @@ 1 - 127.0.0.1:9300 + 127.0.0.1:${integ.transport.port} diff --git a/distribution/zip/pom.xml b/distribution/zip/pom.xml index b6b784a97fe..9e754309f08 100644 --- a/distribution/zip/pom.xml +++ b/distribution/zip/pom.xml @@ -60,7 +60,11 @@ - + + + + + diff --git a/plugins/pom.xml b/plugins/pom.xml index 4bc761b14b5..960b6e3e867 100644 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -411,7 +411,7 @@ 1 - 127.0.0.1:9300 + 127.0.0.1:${integ.transport.port} diff --git a/pom.xml b/pom.xml index 366eb021b98..046f7b7f626 100644 --- a/pom.xml +++ b/pom.xml @@ -109,6 +109,8 @@ ${project.build.directory}/integ-tests ${project.build.directory}/integ-deps ${integ.scratch}/temp + 9400 + 9500 \bno(n|)commit\b From c61dccd1895db96a58429558b4175d027828b7dd Mon Sep 17 00:00:00 2001 From: David Pilato Date: Tue, 4 Aug 2015 12:30:39 +0200 Subject: [PATCH 10/23] Packaging: mvn install renames artifacts when copying MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR: * renames all distribution artifacts to `elasticsearch` so maven plugins will pick up the correct finalName without needing any hack. * changes the groupId for every single distribution module as we can't have more than one module using the same groupId:artifactId * does not attach anymore empty jar files for tar/zip/... modules as they don't contain any `src/main/java` stuff. When you build it, you end up with: ``` $ tree ~/.m2/repository/org/elasticsearch/distribution distribution ├── deb │   └── elasticsearch │   ├── 2.0.0-beta1-SNAPSHOT │   │   ├── elasticsearch-2.0.0-beta1-SNAPSHOT.deb │   │   ├── elasticsearch-2.0.0-beta1-SNAPSHOT.deb.md5 │   │   ├── elasticsearch-2.0.0-beta1-SNAPSHOT.deb.sha1 │   │   ├── elasticsearch-2.0.0-beta1-SNAPSHOT.pom │   │   ├── elasticsearch-2.0.0-beta1-SNAPSHOT.pom.md5 │   │   └── elasticsearch-2.0.0-beta1-SNAPSHOT.pom.sha1 ├── elasticsearch-distribution │   ├── 2.0.0-beta1-SNAPSHOT │   │   ├── elasticsearch-distribution-2.0.0-beta1-SNAPSHOT.pom │   │   ├── elasticsearch-distribution-2.0.0-beta1-SNAPSHOT.pom.md5 │   │   └── elasticsearch-distribution-2.0.0-beta1-SNAPSHOT.pom.sha1 ├── fully-loaded │   └── elasticsearch │   ├── 2.0.0-beta1-SNAPSHOT │   │   ├── elasticsearch-2.0.0-beta1-SNAPSHOT.pom │   │   ├── elasticsearch-2.0.0-beta1-SNAPSHOT.pom.md5 │   │   └── elasticsearch-2.0.0-beta1-SNAPSHOT.pom.sha1 ├── rpm │   └── elasticsearch │   ├── 2.0.0-beta1-SNAPSHOT │   │   ├── elasticsearch-2.0.0-beta1-SNAPSHOT.pom │   │   ├── elasticsearch-2.0.0-beta1-SNAPSHOT.pom.md5 │   │   ├── elasticsearch-2.0.0-beta1-SNAPSHOT.pom.sha1 │   │   ├── elasticsearch-2.0.0-beta1-SNAPSHOT.rpm │   │   ├── elasticsearch-2.0.0-beta1-SNAPSHOT.rpm.md5 │   │   └── elasticsearch-2.0.0-beta1-SNAPSHOT.rpm.sha1 ├── shaded │   └── elasticsearch │   ├── 2.0.0-beta1-SNAPSHOT │   │   ├── elasticsearch-2.0.0-beta1-SNAPSHOT.jar │   │   ├── elasticsearch-2.0.0-beta1-SNAPSHOT.jar.md5 │   │   ├── elasticsearch-2.0.0-beta1-SNAPSHOT.jar.sha1 │   │   ├── elasticsearch-2.0.0-beta1-SNAPSHOT.pom │   │   ├── elasticsearch-2.0.0-beta1-SNAPSHOT.pom.md5 │   │   └── elasticsearch-2.0.0-beta1-SNAPSHOT.pom.sha1 ├── tar │   └── elasticsearch │   ├── 2.0.0-beta1-SNAPSHOT │   │   ├── elasticsearch-2.0.0-beta1-SNAPSHOT.pom │   │   ├── elasticsearch-2.0.0-beta1-SNAPSHOT.pom.md5 │   │   ├── elasticsearch-2.0.0-beta1-SNAPSHOT.pom.sha1 │   │   ├── elasticsearch-2.0.0-beta1-SNAPSHOT.tar.gz │   │   ├── elasticsearch-2.0.0-beta1-SNAPSHOT.tar.gz.md5 │   │   └── elasticsearch-2.0.0-beta1-SNAPSHOT.tar.gz.sha1 └── zip └── elasticsearch └── 2.0.0-beta1-SNAPSHOT    ├── elasticsearch-2.0.0-beta1-SNAPSHOT.pom    ├── elasticsearch-2.0.0-beta1-SNAPSHOT.pom.md5    ├── elasticsearch-2.0.0-beta1-SNAPSHOT.pom.sha1    ├── elasticsearch-2.0.0-beta1-SNAPSHOT.zip    ├── elasticsearch-2.0.0-beta1-SNAPSHOT.zip.md5    ├── elasticsearch-2.0.0-beta1-SNAPSHOT.zip.sha1       └── ``` Closes #12536 --- distribution/deb/pom.xml | 14 ++++++++++---- distribution/fully-loaded/pom.xml | 3 ++- distribution/pom.xml | 9 ++++++++- distribution/rpm/pom.xml | 8 ++++---- distribution/shaded/pom.xml | 5 +++-- distribution/src/main/assemblies/common-bin.xml | 3 ++- distribution/tar/pom.xml | 12 +++++++++--- distribution/zip/pom.xml | 12 +++++++++--- pom.xml | 2 +- 9 files changed, 48 insertions(+), 20 deletions(-) diff --git a/distribution/deb/pom.xml b/distribution/deb/pom.xml index cbe2631cf8a..5706f8f55dc 100644 --- a/distribution/deb/pom.xml +++ b/distribution/deb/pom.xml @@ -9,8 +9,14 @@ 2.0.0-SNAPSHOT - elasticsearch-deb + org.elasticsearch.distribution.deb + elasticsearch Elasticsearch DEB Distribution + + false @@ -19,8 +25,8 @@ - org.elasticsearch.distribution - elasticsearch-fully-loaded + org.elasticsearch.distribution.fully-loaded + elasticsearch ${elasticsearch.version} pom @@ -90,7 +96,6 @@ jdeb org.vafer - 1.4 ${project.build.directory}/releases/elasticsearch-${project.version}.deb @@ -253,6 +258,7 @@ + org.apache.maven.plugins maven-antrun-plugin diff --git a/distribution/fully-loaded/pom.xml b/distribution/fully-loaded/pom.xml index ff3d69a5ab7..91988f187ab 100644 --- a/distribution/fully-loaded/pom.xml +++ b/distribution/fully-loaded/pom.xml @@ -9,7 +9,8 @@ 2.0.0-SNAPSHOT - elasticsearch-fully-loaded + org.elasticsearch.distribution.fully-loaded + elasticsearch Elasticsearch with all optional dependencies pom diff --git a/distribution/pom.xml b/distribution/pom.xml index 082d207855a..81e59786c64 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -71,7 +71,6 @@ - elasticsearch-${project.version} @@ -156,6 +155,14 @@ + + org.apache.maven.plugins + maven-jar-plugin + + + true + + com.carrotsearch.randomizedtesting junit4-maven-plugin diff --git a/distribution/rpm/pom.xml b/distribution/rpm/pom.xml index 2e71b8011d9..d48aca8a125 100644 --- a/distribution/rpm/pom.xml +++ b/distribution/rpm/pom.xml @@ -9,14 +9,15 @@ 2.0.0-SNAPSHOT - elasticsearch-rpm + org.elasticsearch.distribution.rpm + elasticsearch Elasticsearch RPM Distribution rpm - org.elasticsearch.distribution - elasticsearch-fully-loaded + org.elasticsearch.distribution.fully-loaded + elasticsearch ${elasticsearch.version} pom @@ -301,7 +302,6 @@ ${project.packaging} true ${project.build.directory}/releases/ - elasticsearch-${project.version}.rpm diff --git a/distribution/shaded/pom.xml b/distribution/shaded/pom.xml index 5426572f65d..698b0907d66 100644 --- a/distribution/shaded/pom.xml +++ b/distribution/shaded/pom.xml @@ -9,7 +9,8 @@ 2.0.0-SNAPSHOT - elasticsearch-shaded + org.elasticsearch.distribution.shaded + elasticsearch Elasticsearch Shaded Distribution @@ -21,13 +22,13 @@ - ${project.artifactId}-${project.version} org.apache.maven.plugins maven-jar-plugin + false true diff --git a/distribution/src/main/assemblies/common-bin.xml b/distribution/src/main/assemblies/common-bin.xml index 868521b28eb..f95368d3572 100644 --- a/distribution/src/main/assemblies/common-bin.xml +++ b/distribution/src/main/assemblies/common-bin.xml @@ -4,10 +4,11 @@ /lib true false + false - org.elasticsearch.distribution:* + *:pom diff --git a/distribution/tar/pom.xml b/distribution/tar/pom.xml index 641de8577c2..c0ebae4b760 100644 --- a/distribution/tar/pom.xml +++ b/distribution/tar/pom.xml @@ -9,13 +9,19 @@ 2.0.0-SNAPSHOT - elasticsearch-tar + org.elasticsearch.distribution.tar + elasticsearch Elasticsearch TAR Distribution + + - org.elasticsearch.distribution - elasticsearch-fully-loaded + org.elasticsearch.distribution.fully-loaded + elasticsearch ${elasticsearch.version} pom diff --git a/distribution/zip/pom.xml b/distribution/zip/pom.xml index b6b784a97fe..5ac05c16360 100644 --- a/distribution/zip/pom.xml +++ b/distribution/zip/pom.xml @@ -9,13 +9,19 @@ 2.0.0-SNAPSHOT - elasticsearch-zip + org.elasticsearch.distribution.zip + elasticsearch Elasticsearch ZIP Distribution + + - org.elasticsearch.distribution - elasticsearch-fully-loaded + org.elasticsearch.distribution.fully-loaded + elasticsearch ${elasticsearch.version} pom diff --git a/pom.xml b/pom.xml index 366eb021b98..1558e1c9903 100644 --- a/pom.xml +++ b/pom.xml @@ -905,7 +905,7 @@ org.vafer jdeb - 1.3 + 1.4 true From 96ad1911fd3264d47ed7778b0e94d08524483519 Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Tue, 4 Aug 2015 15:02:39 +0200 Subject: [PATCH 11/23] Use correct classname in all-signatures.txt --- dev-tools/src/main/resources/forbidden/all-signatures.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-tools/src/main/resources/forbidden/all-signatures.txt b/dev-tools/src/main/resources/forbidden/all-signatures.txt index 985cdd5c5c0..b03cd14731f 100644 --- a/dev-tools/src/main/resources/forbidden/all-signatures.txt +++ b/dev-tools/src/main/resources/forbidden/all-signatures.txt @@ -53,7 +53,7 @@ org.joda.time.DateTime#(int, int, int, int, int) org.joda.time.DateTime#(int, int, int, int, int, int) org.joda.time.DateTime#(int, int, int, int, int, int, int) org.joda.time.DateTime#now() -org.joda.time.DateTime#getDefault() +org.joda.time.DateTimeZone#getDefault() com.google.common.collect.Iterators#emptyIterator() @ Use Collections.emptyIterator instead From 8f6e75b158724eee9b08313c072adcbb1579ffd3 Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Tue, 4 Aug 2015 15:43:17 +0200 Subject: [PATCH 12/23] Ignore EngineClosedException on IndexShard#sync This method syncs the translog unless it's already synced. If the engine is alreayd closed we are guaranteed to be synced already such that we can just ignore this exception. Closes #12603 --- .../elasticsearch/action/index/TransportIndexAction.java | 7 +------ .../java/org/elasticsearch/index/shard/IndexShard.java | 4 +++- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/action/index/TransportIndexAction.java b/core/src/main/java/org/elasticsearch/action/index/TransportIndexAction.java index b1371e18a32..97620829f3a 100644 --- a/core/src/main/java/org/elasticsearch/action/index/TransportIndexAction.java +++ b/core/src/main/java/org/elasticsearch/action/index/TransportIndexAction.java @@ -211,12 +211,7 @@ public class TransportIndexAction extends TransportReplicationAction Date: Tue, 4 Aug 2015 09:59:19 -0400 Subject: [PATCH 13/23] Fix plugins integ dependencies and apply cleanups --- .../src/main/resources/ant/integration-tests.xml | 12 ++++++------ plugins/pom.xml | 8 +++----- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/dev-tools/src/main/resources/ant/integration-tests.xml b/dev-tools/src/main/resources/ant/integration-tests.xml index fa454983130..45b8db019e6 100644 --- a/dev-tools/src/main/resources/ant/integration-tests.xml +++ b/dev-tools/src/main/resources/ant/integration-tests.xml @@ -143,9 +143,9 @@ - + - + @@ -185,7 +185,7 @@ - @@ -200,7 +200,7 @@ - @@ -217,7 +217,7 @@ - + @@ -244,7 +244,7 @@ - + diff --git a/plugins/pom.xml b/plugins/pom.xml index 4bc761b14b5..ef4620b3f30 100644 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -385,16 +385,14 @@ ${skip.integ.tests} - org.elasticsearch.distribution - elasticsearch-zip + org.elasticsearch.distribution.zip + elasticsearch ${elasticsearch.version} zip true - - elasticsearch-${elasticsearch.version}.zip + true ${integ.deps} From 5de2044c5b1ca5a3905368e9f91ba48ee587acdb Mon Sep 17 00:00:00 2001 From: loopmachine Date: Tue, 4 Aug 2015 14:02:03 -0400 Subject: [PATCH 14/23] Update nested-type.asciidoc mapping example --- docs/reference/mapping/types/nested-type.asciidoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/reference/mapping/types/nested-type.asciidoc b/docs/reference/mapping/types/nested-type.asciidoc index d507f535af8..1427c93b8f3 100644 --- a/docs/reference/mapping/types/nested-type.asciidoc +++ b/docs/reference/mapping/types/nested-type.asciidoc @@ -76,7 +76,7 @@ uses type `nested`: { "type1" : { "properties" : { - "users" : { + "user" : { "type" : "nested", "properties": { "first" : {"type": "string" }, @@ -99,7 +99,7 @@ You may want to index inner objects both as `nested` fields *and* as flattened { "type1" : { "properties" : { - "users" : { + "user" : { "type" : "nested", "include_in_parent": true, "properties": { From 17922d25b883fcbf7974ed1430755dc4ac6947d9 Mon Sep 17 00:00:00 2001 From: Clinton Gormley Date: Tue, 4 Aug 2015 21:31:17 +0200 Subject: [PATCH 15/23] Change license-check script to work with perl 5.8 --- .../license-check/check_license_and_sha.pl | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/dev-tools/src/main/resources/license-check/check_license_and_sha.pl b/dev-tools/src/main/resources/license-check/check_license_and_sha.pl index 349ead39d3b..5af5b6b18ed 100755 --- a/dev-tools/src/main/resources/license-check/check_license_and_sha.pl +++ b/dev-tools/src/main/resources/license-check/check_license_and_sha.pl @@ -2,7 +2,6 @@ use strict; use warnings; -use v5.10; use FindBin qw($RealBin); use lib "$RealBin/lib"; @@ -49,15 +48,15 @@ sub check_shas_and_licenses { for my $jar ( sort keys %new ) { my $old_sha = delete $old{$jar}; unless ($old_sha) { - say STDERR "$jar: SHA is missing"; + print STDERR "$jar: SHA is missing\n"; $error++; $sha_error++; next; } unless ( $old_sha eq $new{$jar} ) { - say STDERR - "$jar: SHA has changed, expected $old_sha but found $new{$jar}"; + print STDERR + "$jar: SHA has changed, expected $old_sha but found $new{$jar}\n"; $error++; $sha_error++; next; @@ -85,37 +84,41 @@ sub check_shas_and_licenses { } } unless ($license_found) { - say STDERR "$jar: LICENSE is missing"; + print STDERR "$jar: LICENSE is missing\n"; $error++; $sha_error++; } unless ($notice_found) { - say STDERR "$jar: NOTICE is missing"; + print STDERR "$jar: NOTICE is missing\n"; $error++; } } if ( keys %old ) { - say STDERR "Extra SHA files present for: " . join ", ", sort keys %old; + print STDERR "Extra SHA files present for: " . join ", ", + sort keys %old; + print "\n"; $error++; } my @unused_licenses = grep { !$licenses{$_} } keys %licenses; if (@unused_licenses) { $error++; - say STDERR "Extra LICENCE file present: " . join ", ", + print STDERR "Extra LICENCE file present: " . join ", ", sort @unused_licenses; + print "\n"; } my @unused_notices = grep { !$notices{$_} } keys %notices; if (@unused_notices) { $error++; - say STDERR "Extra NOTICE file present: " . join ", ", + print STDERR "Extra NOTICE file present: " . join ", ", sort @unused_notices; + print "\n"; } if ($sha_error) { - say STDERR <<"SHAS" + print STDERR <<"SHAS" You can update the SHA files by running: @@ -123,7 +126,7 @@ $0 --update $License_Dir $Source SHAS } - say "All SHAs and licenses OK" unless $error; + print("All SHAs and licenses OK\n") unless $error; return $error; } @@ -136,13 +139,13 @@ sub write_shas { for my $jar ( sort keys %new ) { if ( $old{$jar} ) { next if $old{$jar} eq $new{$jar}; - say "Updating $jar"; + print "Updating $jar\n"; } else { - say "Adding $jar"; + print "Adding $jar\n"; } open my $fh, '>', $License_Dir . $jar or die $!; - say $fh $new{$jar} or die $!; + print $fh $new{$jar} . "\n" or die $!; close $fh or die $!; } continue { @@ -150,10 +153,10 @@ sub write_shas { } for my $jar ( sort keys %old ) { - say "Deleting $jar"; + print "Deleting $jar\n"; unlink $License_Dir . $jar or die $!; } - say "SHAs updated"; + print "SHAs updated\n"; return 0; } From 6f9a06719793e97ec3c18a8c617ed76610d67e08 Mon Sep 17 00:00:00 2001 From: Robert Muir Date: Tue, 4 Aug 2015 15:38:21 -0400 Subject: [PATCH 16/23] Change master branch back to 2.0-beta1 --- core/pom.xml | 2 +- core/src/main/java/org/elasticsearch/Version.java | 6 +----- core/src/test/java/org/elasticsearch/VersionTests.java | 4 ---- dev-tools/pom.xml | 2 +- distribution/deb/pom.xml | 2 +- distribution/fully-loaded/pom.xml | 2 +- distribution/pom.xml | 2 +- distribution/rpm/pom.xml | 2 +- distribution/shaded/pom.xml | 2 +- distribution/tar/pom.xml | 2 +- distribution/zip/pom.xml | 2 +- plugins/analysis-icu/pom.xml | 2 +- plugins/analysis-kuromoji/pom.xml | 2 +- plugins/analysis-phonetic/pom.xml | 2 +- plugins/analysis-smartcn/pom.xml | 2 +- plugins/analysis-stempel/pom.xml | 2 +- plugins/cloud-aws/pom.xml | 2 +- plugins/cloud-azure/pom.xml | 2 +- plugins/cloud-gce/pom.xml | 2 +- plugins/delete-by-query/pom.xml | 2 +- plugins/lang-javascript/pom.xml | 2 +- plugins/lang-python/pom.xml | 2 +- plugins/pom.xml | 4 ++-- plugins/site-example/pom.xml | 2 +- pom.xml | 2 +- rest-api-spec/pom.xml | 4 ++-- 26 files changed, 27 insertions(+), 35 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 325232d087e..d4fb7f95342 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -6,7 +6,7 @@ org.elasticsearch elasticsearch-parent - 2.0.0-SNAPSHOT + 2.0.0-beta1-SNAPSHOT org.elasticsearch diff --git a/core/src/main/java/org/elasticsearch/Version.java b/core/src/main/java/org/elasticsearch/Version.java index d820a1b2427..5ba63c4811b 100644 --- a/core/src/main/java/org/elasticsearch/Version.java +++ b/core/src/main/java/org/elasticsearch/Version.java @@ -255,10 +255,8 @@ public class Version { public static final int V_2_0_0_beta1_ID = 2000001; public static final Version V_2_0_0_beta1 = new Version(V_2_0_0_beta1_ID, true, org.apache.lucene.util.Version.LUCENE_5_2_1); - public static final int V_2_0_0_ID = 2000099; - public static final Version V_2_0_0 = new Version(V_2_0_0_ID, true, org.apache.lucene.util.Version.LUCENE_5_2_1); - public static final Version CURRENT = V_2_0_0; + public static final Version CURRENT = V_2_0_0_beta1; static { assert CURRENT.luceneVersion.equals(Lucene.VERSION) : "Version must be upgraded to [" + Lucene.VERSION + "] is still set to [" + CURRENT.luceneVersion + "]"; @@ -270,8 +268,6 @@ public class Version { public static Version fromId(int id) { switch (id) { - case V_2_0_0_ID: - return V_2_0_0; case V_2_0_0_beta1_ID: return V_2_0_0_beta1; case V_1_7_2_ID: diff --git a/core/src/test/java/org/elasticsearch/VersionTests.java b/core/src/test/java/org/elasticsearch/VersionTests.java index 76c0d8d19d3..3f4891cd825 100644 --- a/core/src/test/java/org/elasticsearch/VersionTests.java +++ b/core/src/test/java/org/elasticsearch/VersionTests.java @@ -203,10 +203,6 @@ public class VersionTests extends ESTestCase { // only the latest version for a branch should be a snapshot (ie unreleased) String branchName = "" + v.major + "." + v.minor; - if (v.equals(Version.V_2_0_0_beta1)) { - assertTrue("Remove this once beta1 is released", v.snapshot()); - continue; // this is just a temporary fix until we have a snapshot for the beta since we now have 2 unreleased version of the same major.minor group - } Version maxBranchVersion = maxBranchVersions.get(branchName); if (maxBranchVersion == null) { maxBranchVersions.put(branchName, v); diff --git a/dev-tools/pom.xml b/dev-tools/pom.xml index 0daa4cbad51..369c003beee 100644 --- a/dev-tools/pom.xml +++ b/dev-tools/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.elasticsearch elasticsearch-dev-tools - 2.0.0-SNAPSHOT + 2.0.0-beta1-SNAPSHOT Elasticsearch Build Resources org.sonatype.oss diff --git a/distribution/deb/pom.xml b/distribution/deb/pom.xml index 5706f8f55dc..c944dcc7c04 100644 --- a/distribution/deb/pom.xml +++ b/distribution/deb/pom.xml @@ -6,7 +6,7 @@ org.elasticsearch.distribution elasticsearch-distribution - 2.0.0-SNAPSHOT + 2.0.0-beta1-SNAPSHOT org.elasticsearch.distribution.deb diff --git a/distribution/fully-loaded/pom.xml b/distribution/fully-loaded/pom.xml index 91988f187ab..9fef31640d9 100644 --- a/distribution/fully-loaded/pom.xml +++ b/distribution/fully-loaded/pom.xml @@ -6,7 +6,7 @@ org.elasticsearch.distribution elasticsearch-distribution - 2.0.0-SNAPSHOT + 2.0.0-beta1-SNAPSHOT org.elasticsearch.distribution.fully-loaded diff --git a/distribution/pom.xml b/distribution/pom.xml index d3b518fd1a1..9a01416ba30 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -6,7 +6,7 @@ org.elasticsearch elasticsearch-parent - 2.0.0-SNAPSHOT + 2.0.0-beta1-SNAPSHOT org.elasticsearch.distribution diff --git a/distribution/rpm/pom.xml b/distribution/rpm/pom.xml index d48aca8a125..698c310ecc3 100644 --- a/distribution/rpm/pom.xml +++ b/distribution/rpm/pom.xml @@ -6,7 +6,7 @@ org.elasticsearch.distribution elasticsearch-distribution - 2.0.0-SNAPSHOT + 2.0.0-beta1-SNAPSHOT org.elasticsearch.distribution.rpm diff --git a/distribution/shaded/pom.xml b/distribution/shaded/pom.xml index 698b0907d66..3163a8053b3 100644 --- a/distribution/shaded/pom.xml +++ b/distribution/shaded/pom.xml @@ -6,7 +6,7 @@ org.elasticsearch.distribution elasticsearch-distribution - 2.0.0-SNAPSHOT + 2.0.0-beta1-SNAPSHOT org.elasticsearch.distribution.shaded diff --git a/distribution/tar/pom.xml b/distribution/tar/pom.xml index c0ebae4b760..3c41c5d87e5 100644 --- a/distribution/tar/pom.xml +++ b/distribution/tar/pom.xml @@ -6,7 +6,7 @@ org.elasticsearch.distribution elasticsearch-distribution - 2.0.0-SNAPSHOT + 2.0.0-beta1-SNAPSHOT org.elasticsearch.distribution.tar diff --git a/distribution/zip/pom.xml b/distribution/zip/pom.xml index 3749e9087c6..afa3dc286eb 100644 --- a/distribution/zip/pom.xml +++ b/distribution/zip/pom.xml @@ -6,7 +6,7 @@ org.elasticsearch.distribution elasticsearch-distribution - 2.0.0-SNAPSHOT + 2.0.0-beta1-SNAPSHOT org.elasticsearch.distribution.zip diff --git a/plugins/analysis-icu/pom.xml b/plugins/analysis-icu/pom.xml index d0679295787..1214cbc82c0 100644 --- a/plugins/analysis-icu/pom.xml +++ b/plugins/analysis-icu/pom.xml @@ -7,7 +7,7 @@ org.elasticsearch.plugin elasticsearch-plugin - 2.0.0-SNAPSHOT + 2.0.0-beta1-SNAPSHOT elasticsearch-analysis-icu diff --git a/plugins/analysis-kuromoji/pom.xml b/plugins/analysis-kuromoji/pom.xml index 1ac11b858b6..7cb0fa762ea 100644 --- a/plugins/analysis-kuromoji/pom.xml +++ b/plugins/analysis-kuromoji/pom.xml @@ -7,7 +7,7 @@ org.elasticsearch.plugin elasticsearch-plugin - 2.0.0-SNAPSHOT + 2.0.0-beta1-SNAPSHOT elasticsearch-analysis-kuromoji diff --git a/plugins/analysis-phonetic/pom.xml b/plugins/analysis-phonetic/pom.xml index f355a255a3f..f5bafe2b865 100644 --- a/plugins/analysis-phonetic/pom.xml +++ b/plugins/analysis-phonetic/pom.xml @@ -7,7 +7,7 @@ org.elasticsearch.plugin elasticsearch-plugin - 2.0.0-SNAPSHOT + 2.0.0-beta1-SNAPSHOT elasticsearch-analysis-phonetic diff --git a/plugins/analysis-smartcn/pom.xml b/plugins/analysis-smartcn/pom.xml index b0ed1943d23..178d5f142a6 100644 --- a/plugins/analysis-smartcn/pom.xml +++ b/plugins/analysis-smartcn/pom.xml @@ -7,7 +7,7 @@ org.elasticsearch.plugin elasticsearch-plugin - 2.0.0-SNAPSHOT + 2.0.0-beta1-SNAPSHOT elasticsearch-analysis-smartcn diff --git a/plugins/analysis-stempel/pom.xml b/plugins/analysis-stempel/pom.xml index febe5eb94ab..4f8f59c0611 100644 --- a/plugins/analysis-stempel/pom.xml +++ b/plugins/analysis-stempel/pom.xml @@ -7,7 +7,7 @@ org.elasticsearch.plugin elasticsearch-plugin - 2.0.0-SNAPSHOT + 2.0.0-beta1-SNAPSHOT elasticsearch-analysis-stempel diff --git a/plugins/cloud-aws/pom.xml b/plugins/cloud-aws/pom.xml index 04417a9b724..35edcf5d238 100644 --- a/plugins/cloud-aws/pom.xml +++ b/plugins/cloud-aws/pom.xml @@ -7,7 +7,7 @@ org.elasticsearch.plugin elasticsearch-plugin - 2.0.0-SNAPSHOT + 2.0.0-beta1-SNAPSHOT elasticsearch-cloud-aws diff --git a/plugins/cloud-azure/pom.xml b/plugins/cloud-azure/pom.xml index ff67fc31600..c339c114cb1 100644 --- a/plugins/cloud-azure/pom.xml +++ b/plugins/cloud-azure/pom.xml @@ -18,7 +18,7 @@ governing permissions and limitations under the License. --> org.elasticsearch.plugin elasticsearch-plugin - 2.0.0-SNAPSHOT + 2.0.0-beta1-SNAPSHOT elasticsearch-cloud-azure diff --git a/plugins/cloud-gce/pom.xml b/plugins/cloud-gce/pom.xml index f0133c8ef6e..48a7b26542e 100644 --- a/plugins/cloud-gce/pom.xml +++ b/plugins/cloud-gce/pom.xml @@ -18,7 +18,7 @@ governing permissions and limitations under the License. --> org.elasticsearch.plugin elasticsearch-plugin - 2.0.0-SNAPSHOT + 2.0.0-beta1-SNAPSHOT elasticsearch-cloud-gce diff --git a/plugins/delete-by-query/pom.xml b/plugins/delete-by-query/pom.xml index 222d00585ff..62ee8fdb9b1 100644 --- a/plugins/delete-by-query/pom.xml +++ b/plugins/delete-by-query/pom.xml @@ -18,7 +18,7 @@ governing permissions and limitations under the License. --> org.elasticsearch.plugin elasticsearch-plugin - 2.0.0-SNAPSHOT + 2.0.0-beta1-SNAPSHOT elasticsearch-delete-by-query diff --git a/plugins/lang-javascript/pom.xml b/plugins/lang-javascript/pom.xml index e1ae181e7c1..eb6e21cd704 100644 --- a/plugins/lang-javascript/pom.xml +++ b/plugins/lang-javascript/pom.xml @@ -7,7 +7,7 @@ org.elasticsearch.plugin elasticsearch-plugin - 2.0.0-SNAPSHOT + 2.0.0-beta1-SNAPSHOT elasticsearch-lang-javascript diff --git a/plugins/lang-python/pom.xml b/plugins/lang-python/pom.xml index a88bdd66ac0..b69a07b0ed7 100644 --- a/plugins/lang-python/pom.xml +++ b/plugins/lang-python/pom.xml @@ -7,7 +7,7 @@ org.elasticsearch.plugin elasticsearch-plugin - 2.0.0-SNAPSHOT + 2.0.0-beta1-SNAPSHOT elasticsearch-lang-python diff --git a/plugins/pom.xml b/plugins/pom.xml index 77278b0d7fb..2d8e9387d9c 100644 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -7,7 +7,7 @@ org.elasticsearch.plugin elasticsearch-plugin - 2.0.0-SNAPSHOT + 2.0.0-beta1-SNAPSHOT pom Elasticsearch Plugin POM 2009 @@ -15,7 +15,7 @@ org.elasticsearch elasticsearch-parent - 2.0.0-SNAPSHOT + 2.0.0-beta1-SNAPSHOT diff --git a/plugins/site-example/pom.xml b/plugins/site-example/pom.xml index 0a6beb1a0b0..15060e0bd52 100644 --- a/plugins/site-example/pom.xml +++ b/plugins/site-example/pom.xml @@ -7,7 +7,7 @@ org.elasticsearch.plugin elasticsearch-plugin - 2.0.0-SNAPSHOT + 2.0.0-beta1-SNAPSHOT elasticsearch-site-example diff --git a/pom.xml b/pom.xml index 2262f68dfa9..188ed85015e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.elasticsearch elasticsearch-parent - 2.0.0-SNAPSHOT + 2.0.0-beta1-SNAPSHOT pom Elasticsearch Parent POM Elasticsearch Parent POM diff --git a/rest-api-spec/pom.xml b/rest-api-spec/pom.xml index 46f2e6e7213..178c55b8770 100644 --- a/rest-api-spec/pom.xml +++ b/rest-api-spec/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.elasticsearch elasticsearch-rest-api-spec - 2.0.0-SNAPSHOT + 2.0.0-beta1-SNAPSHOT Elasticsearch Rest API Spec org.sonatype.oss @@ -34,4 +34,4 @@ - \ No newline at end of file + From cf6acbd7c202427c22578f70b9cfec78c9ba0ff2 Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Tue, 4 Aug 2015 15:08:50 +0200 Subject: [PATCH 17/23] Remove obsolete plugins.info_refresh_interval setting This setting has been removed in #12367 --- docs/reference/cluster/nodes-info.asciidoc | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/docs/reference/cluster/nodes-info.asciidoc b/docs/reference/cluster/nodes-info.asciidoc index 512de738e1d..215e8449f37 100644 --- a/docs/reference/cluster/nodes-info.asciidoc +++ b/docs/reference/cluster/nodes-info.asciidoc @@ -117,19 +117,3 @@ The result will look similar to: } } -------------------------------------------------- - -if your `plugin` data is subject to change use -`plugins.info_refresh_interval` to change or disable the caching -interval: - -[source,js] --------------------------------------------------- -# Change cache to 20 seconds -plugins.info_refresh_interval: 20s - -# Infinite cache -plugins.info_refresh_interval: -1 - -# Disable cache -plugins.info_refresh_interval: 0 --------------------------------------------------- From 8fe5d903b74bc443adb45524cdc727503279be42 Mon Sep 17 00:00:00 2001 From: Igor Motov Date: Fri, 31 Jul 2015 12:33:57 -0400 Subject: [PATCH 18/23] Add support for bulk delete operation in snapshot repository Currently when we delete files belonging to deleted snapshots we issue one delete command to underlying snapshot store at a time. Some repositories can benefit from bulk deletes of multiple files. Closes #12533 --- .../common/blobstore/BlobContainer.java | 10 +++++- .../support/AbstractBlobContainer.java | 8 +++++ .../BlobStoreIndexShardRepository.java | 31 +++++++++++-------- .../mockstore/BlobContainerWrapper.java | 6 ++++ 4 files changed, 41 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/common/blobstore/BlobContainer.java b/core/src/main/java/org/elasticsearch/common/blobstore/BlobContainer.java index 72630e6d955..ce36c243035 100644 --- a/core/src/main/java/org/elasticsearch/common/blobstore/BlobContainer.java +++ b/core/src/main/java/org/elasticsearch/common/blobstore/BlobContainer.java @@ -24,6 +24,7 @@ import com.google.common.collect.ImmutableMap; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.Collection; import java.util.Map; /** @@ -48,10 +49,17 @@ public interface BlobContainer { /** * Deletes a blob with giving name. * - * If blob exist but cannot be deleted an exception has to be thrown. + * If a blob exists but cannot be deleted an exception has to be thrown. */ void deleteBlob(String blobName) throws IOException; + /** + * Deletes blobs with giving names. + * + * If a blob exists but cannot be deleted an exception has to be thrown. + */ + void deleteBlobs(Collection blobNames) throws IOException; + /** * Deletes all blobs in the container that match the specified prefix. */ diff --git a/core/src/main/java/org/elasticsearch/common/blobstore/support/AbstractBlobContainer.java b/core/src/main/java/org/elasticsearch/common/blobstore/support/AbstractBlobContainer.java index 01ad86133aa..44f44f23ffd 100644 --- a/core/src/main/java/org/elasticsearch/common/blobstore/support/AbstractBlobContainer.java +++ b/core/src/main/java/org/elasticsearch/common/blobstore/support/AbstractBlobContainer.java @@ -25,6 +25,7 @@ import org.elasticsearch.common.blobstore.BlobMetaData; import org.elasticsearch.common.blobstore.BlobPath; import java.io.IOException; +import java.util.Collection; import java.util.Map; /** @@ -50,4 +51,11 @@ public abstract class AbstractBlobContainer implements BlobContainer { deleteBlob(blob.name()); } } + + @Override + public void deleteBlobs(Collection blobNames) throws IOException { + for(String blob: blobNames) { + deleteBlob(blob); + } + } } diff --git a/core/src/main/java/org/elasticsearch/index/snapshots/blobstore/BlobStoreIndexShardRepository.java b/core/src/main/java/org/elasticsearch/index/snapshots/blobstore/BlobStoreIndexShardRepository.java index cce7e44d0f9..042f3c3f5ef 100644 --- a/core/src/main/java/org/elasticsearch/index/snapshots/blobstore/BlobStoreIndexShardRepository.java +++ b/core/src/main/java/org/elasticsearch/index/snapshots/blobstore/BlobStoreIndexShardRepository.java @@ -352,33 +352,38 @@ public class BlobStoreIndexShardRepository extends AbstractComponent implements */ protected void finalize(List snapshots, int fileListGeneration, Map blobs) { BlobStoreIndexShardSnapshots newSnapshots = new BlobStoreIndexShardSnapshots(snapshots); + List blobsToDelete = newArrayList(); // delete old index files first for (String blobName : blobs.keySet()) { // delete old file lists 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 [{}] during cleanup", e); - } + blobsToDelete.add(blobName); } } + try { + blobContainer.deleteBlobs(blobsToDelete); + } 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 files during cleanup, reason: " + e.getMessage(), e); + } + + blobsToDelete = newArrayList(); // now go over all the blobs, and if they don't exists in a snapshot, delete them for (String blobName : blobs.keySet()) { - // delete old file lists + // delete unused files if (blobName.startsWith(DATA_BLOB_PREFIX)) { if (newSnapshots.findNameFile(FileInfo.canonicalName(blobName)) == null) { - try { - blobContainer.deleteBlob(blobName); - } catch (IOException e) { - logger.debug("[{}] [{}] error deleting blob [{}] during cleanup", e, snapshotId, shardId, blobName); - } + blobsToDelete.add(blobName); } } } + try { + blobContainer.deleteBlobs(blobsToDelete); + } catch (IOException e) { + logger.debug("[{}] [{}] error deleting some of the blobs [{}] during cleanup", e, snapshotId, shardId, blobsToDelete); + } // If we deleted all snapshots - we don't need to create the index file if (snapshots.size() > 0) { diff --git a/core/src/test/java/org/elasticsearch/snapshots/mockstore/BlobContainerWrapper.java b/core/src/test/java/org/elasticsearch/snapshots/mockstore/BlobContainerWrapper.java index 6ab2cdf782b..e45d4169995 100644 --- a/core/src/test/java/org/elasticsearch/snapshots/mockstore/BlobContainerWrapper.java +++ b/core/src/test/java/org/elasticsearch/snapshots/mockstore/BlobContainerWrapper.java @@ -26,6 +26,7 @@ import org.elasticsearch.common.blobstore.BlobPath; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.Collection; import java.util.Map; /** @@ -63,6 +64,11 @@ public class BlobContainerWrapper implements BlobContainer { delegate.deleteBlob(blobName); } + @Override + public void deleteBlobs(Collection blobNames) throws IOException { + delegate.deleteBlobs(blobNames); + } + @Override public void deleteBlobsByPrefix(String blobNamePrefix) throws IOException { delegate.deleteBlobsByPrefix(blobNamePrefix); From 333ca689d3fab71aa263267c0e6ef3142adc3539 Mon Sep 17 00:00:00 2001 From: xuzha Date: Tue, 4 Aug 2015 00:25:04 -0700 Subject: [PATCH 19/23] Add retry when checking s3 bucket --- .../cloud/aws/blobstore/S3BlobStore.java | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/plugins/cloud-aws/src/main/java/org/elasticsearch/cloud/aws/blobstore/S3BlobStore.java b/plugins/cloud-aws/src/main/java/org/elasticsearch/cloud/aws/blobstore/S3BlobStore.java index 1e891836a07..91f06b67c00 100644 --- a/plugins/cloud-aws/src/main/java/org/elasticsearch/cloud/aws/blobstore/S3BlobStore.java +++ b/plugins/cloud-aws/src/main/java/org/elasticsearch/cloud/aws/blobstore/S3BlobStore.java @@ -77,11 +77,24 @@ public class S3BlobStore extends AbstractComponent implements BlobStore { // Also, if invalid security credentials are used to execute this method, the // client is not able to distinguish between bucket permission errors and // invalid credential errors, and this method could return an incorrect result. - if (!client.doesBucketExist(bucket)) { - if (region != null) { - client.createBucket(bucket, region); - } else { - client.createBucket(bucket); + int retry = 0; + while (retry <= maxRetries) { + try { + if (!client.doesBucketExist(bucket)) { + if (region != null) { + client.createBucket(bucket, region); + } else { + client.createBucket(bucket); + } + } + break; + } catch (AmazonClientException e) { + if (shouldRetry(e) && retry < maxRetries) { + retry++; + } else { + logger.debug("S3 client create bucket failed"); + throw e; + } } } } From f71c9a25a182f05a7b54f3b773921b763981c2a5 Mon Sep 17 00:00:00 2001 From: Igor Motov Date: Wed, 22 Jul 2015 17:23:45 -0400 Subject: [PATCH 20/23] Check for incompatible mappings while upgrading old indices Conflicting mappings that were allowed before v2.0 can cause runaway shard failures on upgrade. This commit adds a check that prevents a cluster from starting if it contains such indices as well as restoring such indices from a snapshot into already running cluster. Closes #11857 --- .../upgrade/post/ShardUpgradeResponse.java | 27 +++-- .../upgrade/post/TransportUpgradeAction.java | 37 +++++-- .../indices/upgrade/post/UpgradeResponse.java | 22 +++-- ...radeSettingsClusterStateUpdateRequest.java | 8 +- .../upgrade/post/UpgradeSettingsRequest.java | 23 +++-- .../post/UpgradeSettingsRequestBuilder.java | 4 +- .../metadata/MetaDataIndexUpgradeService.java | 87 +++++++++++++++- .../MetaDataUpdateSettingsService.java | 7 +- .../index/mapper/MapperService.java | 3 +- .../indices/upgrade/RestUpgradeAction.java | 9 +- .../admin/indices/upgrade/UpgradeIT.java | 2 +- .../upgrade/UpgradeReallyOldIndexIT.java | 20 +++- .../OldIndexBackwardsCompatibilityIT.java | 2 +- .../admin/indices/upgrade/index-0.90.6.zip | Bin .../index-conflicting-mappings-1.7.0.zip | Bin 0 -> 92022 bytes ...eate_bwc_index_with_conficting_mappings.py | 93 ++++++++++++++++++ ...te_bwc_index_with_some_ancient_segments.py | 8 +- .../test/indices.upgrade/10_basic.yaml | 3 +- 18 files changed, 301 insertions(+), 54 deletions(-) rename core/src/test/java/org/elasticsearch/{rest => }/action/admin/indices/upgrade/UpgradeIT.java (99%) rename core/src/test/java/org/elasticsearch/{rest => }/action/admin/indices/upgrade/UpgradeReallyOldIndexIT.java (71%) rename core/src/test/resources/org/elasticsearch/{rest => }/action/admin/indices/upgrade/index-0.90.6.zip (100%) create mode 100644 core/src/test/resources/org/elasticsearch/action/admin/indices/upgrade/index-conflicting-mappings-1.7.0.zip create mode 100644 dev-tools/create_bwc_index_with_conficting_mappings.py diff --git a/core/src/main/java/org/elasticsearch/action/admin/indices/upgrade/post/ShardUpgradeResponse.java b/core/src/main/java/org/elasticsearch/action/admin/indices/upgrade/post/ShardUpgradeResponse.java index efbb19142c3..d3942038164 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/indices/upgrade/post/ShardUpgradeResponse.java +++ b/core/src/main/java/org/elasticsearch/action/admin/indices/upgrade/post/ShardUpgradeResponse.java @@ -19,6 +19,7 @@ package org.elasticsearch.action.admin.indices.upgrade.post; +import org.elasticsearch.Version; import org.elasticsearch.action.support.broadcast.BroadcastShardResponse; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; @@ -32,7 +33,9 @@ import java.text.ParseException; */ class ShardUpgradeResponse extends BroadcastShardResponse { - private org.apache.lucene.util.Version version; + private org.apache.lucene.util.Version oldestLuceneSegment; + + private Version upgradeVersion; private boolean primary; @@ -40,14 +43,19 @@ class ShardUpgradeResponse extends BroadcastShardResponse { ShardUpgradeResponse() { } - ShardUpgradeResponse(ShardId shardId, boolean primary, org.apache.lucene.util.Version version) { + ShardUpgradeResponse(ShardId shardId, boolean primary, Version upgradeVersion, org.apache.lucene.util.Version oldestLuceneSegment) { super(shardId); this.primary = primary; - this.version = version; + this.upgradeVersion = upgradeVersion; + this.oldestLuceneSegment = oldestLuceneSegment; } - public org.apache.lucene.util.Version version() { - return this.version; + public org.apache.lucene.util.Version oldestLuceneSegment() { + return this.oldestLuceneSegment; + } + + public Version upgradeVersion() { + return this.upgradeVersion; } public boolean primary() { @@ -59,18 +67,21 @@ class ShardUpgradeResponse extends BroadcastShardResponse { public void readFrom(StreamInput in) throws IOException { super.readFrom(in); primary = in.readBoolean(); + upgradeVersion = Version.readVersion(in); try { - version = org.apache.lucene.util.Version.parse(in.readString()); + oldestLuceneSegment = org.apache.lucene.util.Version.parse(in.readString()); } catch (ParseException ex) { - throw new IOException("failed to parse lucene version [" + version + "]", ex); + throw new IOException("failed to parse lucene version [" + oldestLuceneSegment + "]", ex); } + } @Override public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); out.writeBoolean(primary); - out.writeString(version.toString()); + Version.writeVersion(upgradeVersion, out); + out.writeString(oldestLuceneSegment.toString()); } } \ No newline at end of file diff --git a/core/src/main/java/org/elasticsearch/action/admin/indices/upgrade/post/TransportUpgradeAction.java b/core/src/main/java/org/elasticsearch/action/admin/indices/upgrade/post/TransportUpgradeAction.java index 9d3c346eca9..39a736019f4 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/indices/upgrade/post/TransportUpgradeAction.java +++ b/core/src/main/java/org/elasticsearch/action/admin/indices/upgrade/post/TransportUpgradeAction.java @@ -19,7 +19,7 @@ package org.elasticsearch.action.admin.indices.upgrade.post; -import org.apache.lucene.util.Version; +import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.PrimaryMissingActionException; import org.elasticsearch.action.ShardOperationFailedException; @@ -34,6 +34,7 @@ import org.elasticsearch.cluster.block.ClusterBlockLevel; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.routing.*; +import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.shard.IndexShard; @@ -75,7 +76,7 @@ public class TransportUpgradeAction extends TransportBroadcastAction shardFailures = null; Map successfulPrimaryShards = newHashMap(); - Map versions = newHashMap(); + Map> versions = newHashMap(); for (int i = 0; i < shardsResponses.length(); i++) { Object shardResponse = shardsResponses.get(i); if (shardResponse == null) { @@ -94,20 +95,35 @@ public class TransportUpgradeAction extends TransportBroadcastAction versionTuple = versions.get(index); + if (versionTuple == null) { + versions.put(index, new Tuple<>(shardUpgradeResponse.upgradeVersion(), shardUpgradeResponse.oldestLuceneSegment())); + } else { + // We already have versions for this index - let's see if we need to update them based on the current shard + Version version = versionTuple.v1(); + org.apache.lucene.util.Version luceneVersion = versionTuple.v2(); + // For the metadata we are interested in the _latest_ elasticsearch version that was processing the metadata + // Since we rewrite the mapping during upgrade the metadata is always rewritten by the latest version + if (shardUpgradeResponse.upgradeVersion().after(versionTuple.v1())) { + version = shardUpgradeResponse.upgradeVersion(); + } + // For the lucene version we are interested in the _oldest_ lucene version since it determines the + // oldest version that we need to support + if (shardUpgradeResponse.oldestLuceneSegment().onOrAfter(versionTuple.v2()) == false) { + luceneVersion = shardUpgradeResponse.oldestLuceneSegment(); + } + versions.put(index, new Tuple<>(version, luceneVersion)); } } } - Map updatedVersions = newHashMap(); + Map> updatedVersions = newHashMap(); MetaData metaData = clusterState.metaData(); - for (Map.Entry versionEntry : versions.entrySet()) { + for (Map.Entry> versionEntry : versions.entrySet()) { String index = versionEntry.getKey(); Integer primaryCount = successfulPrimaryShards.get(index); int expectedPrimaryCount = metaData.index(index).getNumberOfShards(); if (primaryCount == metaData.index(index).getNumberOfShards()) { - updatedVersions.put(index, versionEntry.getValue().toString()); + updatedVersions.put(index, new Tuple<>(versionEntry.getValue().v1(), versionEntry.getValue().v2().toString())); } else { logger.warn("Not updating settings for the index [{}] because upgraded of some primary shards failed - expected[{}], received[{}]", index, expectedPrimaryCount, primaryCount == null ? 0 : primaryCount); @@ -130,8 +146,9 @@ public class TransportUpgradeAction extends TransportBroadcastAction versions; + private Map> versions; UpgradeResponse() { } - UpgradeResponse(Map versions, int totalShards, int successfulShards, int failedShards, List shardFailures) { + UpgradeResponse(Map> versions, int totalShards, int successfulShards, int failedShards, List shardFailures) { super(totalShards, successfulShards, failedShards, shardFailures); this.versions = versions; } @@ -55,8 +57,9 @@ public class UpgradeResponse extends BroadcastResponse { versions = newHashMap(); for (int i=0; i(upgradeVersion, oldestLuceneSegment)); } } @@ -64,13 +67,18 @@ public class UpgradeResponse extends BroadcastResponse { public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); out.writeVInt(versions.size()); - for(Map.Entry entry : versions.entrySet()) { + for(Map.Entry> entry : versions.entrySet()) { out.writeString(entry.getKey()); - out.writeString(entry.getValue()); + Version.writeVersion(entry.getValue().v1(), out); + out.writeString(entry.getValue().v2()); } } - public Map versions() { + /** + * Returns the highest upgrade version of the node that performed metadata upgrade and the + * the version of the oldest lucene segment for each index that was upgraded. + */ + public Map> versions() { return versions; } } \ No newline at end of file diff --git a/core/src/main/java/org/elasticsearch/action/admin/indices/upgrade/post/UpgradeSettingsClusterStateUpdateRequest.java b/core/src/main/java/org/elasticsearch/action/admin/indices/upgrade/post/UpgradeSettingsClusterStateUpdateRequest.java index 7067f2f61ec..7df42951d08 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/indices/upgrade/post/UpgradeSettingsClusterStateUpdateRequest.java +++ b/core/src/main/java/org/elasticsearch/action/admin/indices/upgrade/post/UpgradeSettingsClusterStateUpdateRequest.java @@ -19,7 +19,9 @@ package org.elasticsearch.action.admin.indices.upgrade.post; +import org.elasticsearch.Version; import org.elasticsearch.cluster.ack.ClusterStateUpdateRequest; +import org.elasticsearch.common.collect.Tuple; import java.util.Map; @@ -28,7 +30,7 @@ import java.util.Map; */ public class UpgradeSettingsClusterStateUpdateRequest extends ClusterStateUpdateRequest { - private Map versions; + private Map> versions; public UpgradeSettingsClusterStateUpdateRequest() { @@ -37,14 +39,14 @@ public class UpgradeSettingsClusterStateUpdateRequest extends ClusterStateUpdate /** * Returns the index to version map for indices that should be updated */ - public Map versions() { + public Map> versions() { return versions; } /** * Sets the index to version map for indices that should be updated */ - public UpgradeSettingsClusterStateUpdateRequest versions(Map versions) { + public UpgradeSettingsClusterStateUpdateRequest versions(Map> versions) { this.versions = versions; return this; } diff --git a/core/src/main/java/org/elasticsearch/action/admin/indices/upgrade/post/UpgradeSettingsRequest.java b/core/src/main/java/org/elasticsearch/action/admin/indices/upgrade/post/UpgradeSettingsRequest.java index b191fa53539..7c3676f66e2 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/indices/upgrade/post/UpgradeSettingsRequest.java +++ b/core/src/main/java/org/elasticsearch/action/admin/indices/upgrade/post/UpgradeSettingsRequest.java @@ -19,8 +19,10 @@ package org.elasticsearch.action.admin.indices.upgrade.post; +import org.elasticsearch.Version; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.support.master.AcknowledgedRequest; +import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; @@ -35,16 +37,17 @@ import static org.elasticsearch.action.ValidateActions.addValidationError; */ public class UpgradeSettingsRequest extends AcknowledgedRequest { - - private Map versions; + private Map> versions; UpgradeSettingsRequest() { } /** * Constructs a new request to update minimum compatible version settings for one or more indices + * + * @param versions a map from index name to elasticsearch version, oldest lucene segment version tuple */ - public UpgradeSettingsRequest(Map versions) { + public UpgradeSettingsRequest(Map> versions) { this.versions = versions; } @@ -59,14 +62,14 @@ public class UpgradeSettingsRequest extends AcknowledgedRequest versions() { + Map> versions() { return versions; } /** * Sets the index versions to be updated */ - public UpgradeSettingsRequest versions(Map versions) { + public UpgradeSettingsRequest versions(Map> versions) { this.versions = versions; return this; } @@ -79,8 +82,9 @@ public class UpgradeSettingsRequest extends AcknowledgedRequest(upgradeVersion, oldestLuceneSegment)); } readTimeout(in); } @@ -89,9 +93,10 @@ public class UpgradeSettingsRequest extends AcknowledgedRequest entry : versions.entrySet()) { + for(Map.Entry> entry : versions.entrySet()) { out.writeString(entry.getKey()); - out.writeString(entry.getValue()); + Version.writeVersion(entry.getValue().v1(), out); + out.writeString(entry.getValue().v2()); } writeTimeout(out); } diff --git a/core/src/main/java/org/elasticsearch/action/admin/indices/upgrade/post/UpgradeSettingsRequestBuilder.java b/core/src/main/java/org/elasticsearch/action/admin/indices/upgrade/post/UpgradeSettingsRequestBuilder.java index 74c42a5fe80..9ce5aeb2d2d 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/indices/upgrade/post/UpgradeSettingsRequestBuilder.java +++ b/core/src/main/java/org/elasticsearch/action/admin/indices/upgrade/post/UpgradeSettingsRequestBuilder.java @@ -19,8 +19,10 @@ package org.elasticsearch.action.admin.indices.upgrade.post; +import org.elasticsearch.Version; import org.elasticsearch.action.support.master.AcknowledgedRequestBuilder; import org.elasticsearch.client.ElasticsearchClient; +import org.elasticsearch.common.collect.Tuple; import java.util.Map; @@ -36,7 +38,7 @@ public class UpgradeSettingsRequestBuilder extends AcknowledgedRequestBuilder versions) { + public UpgradeSettingsRequestBuilder setVersions(Map> versions) { request.versions(versions); return this; } diff --git a/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexUpgradeService.java b/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexUpgradeService.java index 6d02325bfca..6b2f501dc68 100644 --- a/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexUpgradeService.java +++ b/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexUpgradeService.java @@ -18,6 +18,8 @@ */ package org.elasticsearch.cluster.metadata; +import com.carrotsearch.hppc.cursors.ObjectCursor; +import org.apache.lucene.analysis.Analyzer; import org.elasticsearch.Version; import org.elasticsearch.cluster.routing.DjbHashFunction; import org.elasticsearch.cluster.routing.HashFunction; @@ -27,6 +29,12 @@ import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import com.google.common.collect.ImmutableSet; +import org.elasticsearch.index.Index; +import org.elasticsearch.index.analysis.AnalysisService; +import org.elasticsearch.index.analysis.NamedAnalyzer; +import org.elasticsearch.index.mapper.MapperService; +import org.elasticsearch.index.similarity.SimilarityLookupService; +import org.elasticsearch.script.ScriptService; import java.util.Set; @@ -45,11 +53,12 @@ public class MetaDataIndexUpgradeService extends AbstractComponent { private final Class pre20HashFunction; private final Boolean pre20UseType; + private final ScriptService scriptService; @Inject - public MetaDataIndexUpgradeService(Settings settings) { + public MetaDataIndexUpgradeService(Settings settings, ScriptService scriptService) { super(settings); - + this.scriptService = scriptService; final String pre20HashFunctionName = settings.get(DEPRECATED_SETTING_ROUTING_HASH_FUNCTION, null); final boolean hasCustomPre20HashFunction = pre20HashFunctionName != null; // the hash function package has changed we replace the two hash functions if their fully qualified name is used. @@ -83,12 +92,24 @@ public class MetaDataIndexUpgradeService extends AbstractComponent { */ public IndexMetaData upgradeIndexMetaData(IndexMetaData indexMetaData) { // Throws an exception if there are too-old segments: + if (isUpgraded(indexMetaData)) { + return indexMetaData; + } checkSupportedVersion(indexMetaData); IndexMetaData newMetaData = upgradeLegacyRoutingSettings(indexMetaData); newMetaData = addDefaultUnitsIfNeeded(newMetaData); + checkMappingsCompatibility(newMetaData); + newMetaData = markAsUpgraded(newMetaData); return newMetaData; } + /** + * Checks if the index was already opened by this version of Elasticsearch and doesn't require any additional checks. + */ + private boolean isUpgraded(IndexMetaData indexMetaData) { + return indexMetaData.upgradeVersion().onOrAfter(Version.V_2_0_0_beta1); + } + /** * Elasticsearch 2.0 no longer supports indices with pre Lucene v4.0 (Elasticsearch v 0.90.0) segments. All indices * that were created before Elasticsearch v0.90.0 should be upgraded using upgrade plugin before they can @@ -239,4 +260,66 @@ public class MetaDataIndexUpgradeService extends AbstractComponent { // No changes: return indexMetaData; } + + + /** + * Checks the mappings for compatibility with the current version + */ + private void checkMappingsCompatibility(IndexMetaData indexMetaData) { + Index index = new Index(indexMetaData.getIndex()); + Settings settings = indexMetaData.settings(); + try { + SimilarityLookupService similarityLookupService = new SimilarityLookupService(index, settings); + // We cannot instantiate real analysis server at this point because the node might not have + // been started yet. However, we don't really need real analyzers at this stage - so we can fake it + try (AnalysisService analysisService = new FakeAnalysisService(index, settings)) { + try (MapperService mapperService = new MapperService(index, settings, analysisService, similarityLookupService, scriptService)) { + for (ObjectCursor cursor : indexMetaData.getMappings().values()) { + MappingMetaData mappingMetaData = cursor.value; + mapperService.merge(mappingMetaData.type(), mappingMetaData.source(), false, false); + } + } + } + } catch (Exception ex) { + // Wrap the inner exception so we have the index name in the exception message + throw new IllegalStateException("unable to upgrade the mappings for the index [" + indexMetaData.getIndex() + "], reason: [" + ex.getMessage() + "]", ex); + } + } + + /** + * Marks index as upgraded so we don't have to test it again + */ + private IndexMetaData markAsUpgraded(IndexMetaData indexMetaData) { + Settings settings = Settings.builder().put(indexMetaData.settings()).put(IndexMetaData.SETTING_VERSION_UPGRADED, Version.CURRENT).build(); + return IndexMetaData.builder(indexMetaData).settings(settings).build(); + } + + /** + * A fake analysis server that returns the same keyword analyzer for all requests + */ + private static class FakeAnalysisService extends AnalysisService { + + private Analyzer fakeAnalyzer = new Analyzer() { + @Override + protected TokenStreamComponents createComponents(String fieldName) { + throw new UnsupportedOperationException("shouldn't be here"); + } + }; + + public FakeAnalysisService(Index index, Settings indexSettings) { + super(index, indexSettings); + } + + @Override + public NamedAnalyzer analyzer(String name) { + return new NamedAnalyzer(name, fakeAnalyzer); + } + + @Override + public void close() { + fakeAnalyzer.close(); + super.close(); + } + } + } diff --git a/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataUpdateSettingsService.java b/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataUpdateSettingsService.java index d23c571faa7..9fbe2c5e06c 100644 --- a/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataUpdateSettingsService.java +++ b/core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataUpdateSettingsService.java @@ -34,6 +34,7 @@ import org.elasticsearch.cluster.routing.allocation.RoutingAllocation; import org.elasticsearch.cluster.settings.DynamicSettings; import org.elasticsearch.common.Booleans; import org.elasticsearch.common.Priority; +import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; @@ -334,7 +335,7 @@ public class MetaDataUpdateSettingsService extends AbstractComponent implements @Override public ClusterState execute(ClusterState currentState) { MetaData.Builder metaDataBuilder = MetaData.builder(currentState.metaData()); - for (Map.Entry entry : request.versions().entrySet()) { + for (Map.Entry> entry : request.versions().entrySet()) { String index = entry.getKey(); IndexMetaData indexMetaData = metaDataBuilder.get(index); if (indexMetaData != null) { @@ -342,8 +343,8 @@ public class MetaDataUpdateSettingsService extends AbstractComponent implements // No reason to pollute the settings, we didn't really upgrade anything metaDataBuilder.put(IndexMetaData.builder(indexMetaData) .settings(settingsBuilder().put(indexMetaData.settings()) - .put(IndexMetaData.SETTING_VERSION_MINIMUM_COMPATIBLE, entry.getValue()) - .put(IndexMetaData.SETTING_VERSION_UPGRADED, Version.CURRENT) + .put(IndexMetaData.SETTING_VERSION_MINIMUM_COMPATIBLE, entry.getValue().v2()) + .put(IndexMetaData.SETTING_VERSION_UPGRADED, entry.getValue().v1()) ) ); } diff --git a/core/src/main/java/org/elasticsearch/index/mapper/MapperService.java b/core/src/main/java/org/elasticsearch/index/mapper/MapperService.java index 0423f5520b2..7f01a1e98e6 100755 --- a/core/src/main/java/org/elasticsearch/index/mapper/MapperService.java +++ b/core/src/main/java/org/elasticsearch/index/mapper/MapperService.java @@ -63,6 +63,7 @@ import org.elasticsearch.indices.TypeMissingException; import org.elasticsearch.percolator.PercolatorService; import org.elasticsearch.script.ScriptService; +import java.io.Closeable; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; @@ -78,7 +79,7 @@ import static org.elasticsearch.common.collect.MapBuilder.newMapBuilder; /** * */ -public class MapperService extends AbstractIndexComponent { +public class MapperService extends AbstractIndexComponent implements Closeable { public static final String DEFAULT_MAPPING = "_default_"; private static ObjectHashSet META_FIELDS = ObjectHashSet.from( diff --git a/core/src/main/java/org/elasticsearch/rest/action/admin/indices/upgrade/RestUpgradeAction.java b/core/src/main/java/org/elasticsearch/rest/action/admin/indices/upgrade/RestUpgradeAction.java index a1c9c0b3ed6..6a554db60fe 100644 --- a/core/src/main/java/org/elasticsearch/rest/action/admin/indices/upgrade/RestUpgradeAction.java +++ b/core/src/main/java/org/elasticsearch/rest/action/admin/indices/upgrade/RestUpgradeAction.java @@ -19,11 +19,13 @@ package org.elasticsearch.rest.action.admin.indices.upgrade; +import org.elasticsearch.Version; import org.elasticsearch.action.admin.indices.upgrade.get.UpgradeStatusResponse; import org.elasticsearch.action.admin.indices.upgrade.post.UpgradeRequest; import org.elasticsearch.action.admin.indices.upgrade.post.UpgradeResponse; import org.elasticsearch.client.Client; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; @@ -86,8 +88,11 @@ public class RestUpgradeAction extends BaseRestHandler { builder.startObject(); buildBroadcastShardsHeader(builder, request, response); builder.startObject("upgraded_indices"); - for (Map.Entry entry : response.versions().entrySet()) { - builder.field(entry.getKey(), entry.getValue(), XContentBuilder.FieldCaseConversion.NONE); + for (Map.Entry> entry : response.versions().entrySet()) { + builder.startObject(entry.getKey(), XContentBuilder.FieldCaseConversion.NONE); + builder.field("upgrade_version", entry.getValue().v1()); + builder.field("oldest_lucene_segment_version", entry.getValue().v2()); + builder.endObject(); } builder.endObject(); builder.endObject(); diff --git a/core/src/test/java/org/elasticsearch/rest/action/admin/indices/upgrade/UpgradeIT.java b/core/src/test/java/org/elasticsearch/action/admin/indices/upgrade/UpgradeIT.java similarity index 99% rename from core/src/test/java/org/elasticsearch/rest/action/admin/indices/upgrade/UpgradeIT.java rename to core/src/test/java/org/elasticsearch/action/admin/indices/upgrade/UpgradeIT.java index 82d88aeb950..89d2a25f078 100644 --- a/core/src/test/java/org/elasticsearch/rest/action/admin/indices/upgrade/UpgradeIT.java +++ b/core/src/test/java/org/elasticsearch/action/admin/indices/upgrade/UpgradeIT.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.rest.action.admin.indices.upgrade; +package org.elasticsearch.action.admin.indices.upgrade; import com.google.common.base.Predicate; import org.elasticsearch.ExceptionsHelper; diff --git a/core/src/test/java/org/elasticsearch/rest/action/admin/indices/upgrade/UpgradeReallyOldIndexIT.java b/core/src/test/java/org/elasticsearch/action/admin/indices/upgrade/UpgradeReallyOldIndexIT.java similarity index 71% rename from core/src/test/java/org/elasticsearch/rest/action/admin/indices/upgrade/UpgradeReallyOldIndexIT.java rename to core/src/test/java/org/elasticsearch/action/admin/indices/upgrade/UpgradeReallyOldIndexIT.java index 68777f107e3..4ada599f7d3 100644 --- a/core/src/test/java/org/elasticsearch/rest/action/admin/indices/upgrade/UpgradeReallyOldIndexIT.java +++ b/core/src/test/java/org/elasticsearch/action/admin/indices/upgrade/UpgradeReallyOldIndexIT.java @@ -17,15 +17,17 @@ * under the License. */ -package org.elasticsearch.rest.action.admin.indices.upgrade; +package org.elasticsearch.action.admin.indices.upgrade; import org.elasticsearch.Version; import org.elasticsearch.bwcompat.StaticIndexBackwardCompatibilityIT; import org.elasticsearch.cluster.metadata.IndexMetaData; +import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.IndexService; import org.elasticsearch.indices.IndicesService; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; +import static org.hamcrest.Matchers.containsString; public class UpgradeReallyOldIndexIT extends StaticIndexBackwardCompatibilityIT { @@ -38,11 +40,25 @@ public class UpgradeReallyOldIndexIT extends StaticIndexBackwardCompatibilityIT assertTrue(UpgradeIT.hasAncientSegments(client(), indexName)); assertNoFailures(client().admin().indices().prepareUpgrade(indexName).setUpgradeOnlyAncientSegments(true).get()); - assertFalse(UpgradeIT.hasAncientSegments(client(), "index-0.90.6")); + assertFalse(UpgradeIT.hasAncientSegments(client(), indexName)); // This index has only ancient segments, so it should now be fully upgraded: UpgradeIT.assertUpgraded(client(), indexName); assertEquals(Version.CURRENT.luceneVersion.toString(), client().admin().indices().prepareGetSettings(indexName).get().getSetting(indexName, IndexMetaData.SETTING_VERSION_MINIMUM_COMPATIBLE)); assertMinVersion(indexName, Version.CURRENT.luceneVersion); + + assertEquals(client().admin().indices().prepareGetSettings(indexName).get().getSetting(indexName, IndexMetaData.SETTING_VERSION_UPGRADED), Integer.toString(Version.CURRENT.id)); + } + + public void testUpgradeConflictingMapping() throws Exception { + String indexName = "index-conflicting-mappings-1.7.0"; + logger.info("Checking static index " + indexName); + Settings nodeSettings = prepareBackwardsDataDir(getDataPath(indexName + ".zip")); + try { + internalCluster().startNode(nodeSettings); + fail("Should have failed to start the node"); + } catch (Exception ex) { + assertThat(ex.getMessage(), containsString("conflicts with existing mapping in other types")); + } } private void assertMinVersion(String index, org.apache.lucene.util.Version version) { diff --git a/core/src/test/java/org/elasticsearch/bwcompat/OldIndexBackwardsCompatibilityIT.java b/core/src/test/java/org/elasticsearch/bwcompat/OldIndexBackwardsCompatibilityIT.java index c2ba057f9e9..db464c31516 100644 --- a/core/src/test/java/org/elasticsearch/bwcompat/OldIndexBackwardsCompatibilityIT.java +++ b/core/src/test/java/org/elasticsearch/bwcompat/OldIndexBackwardsCompatibilityIT.java @@ -26,6 +26,7 @@ import org.apache.lucene.util.LuceneTestCase; import org.apache.lucene.util.TestUtil; import org.elasticsearch.Version; import org.elasticsearch.action.admin.indices.get.GetIndexResponse; +import org.elasticsearch.action.admin.indices.upgrade.UpgradeIT; import org.elasticsearch.action.get.GetResponse; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.action.search.SearchResponse; @@ -42,7 +43,6 @@ import org.elasticsearch.index.engine.EngineConfig; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.shard.MergePolicyConfig; import org.elasticsearch.indices.recovery.RecoverySettings; -import org.elasticsearch.rest.action.admin.indices.upgrade.UpgradeIT; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.aggregations.AggregationBuilders; import org.elasticsearch.search.aggregations.bucket.histogram.Histogram; diff --git a/core/src/test/resources/org/elasticsearch/rest/action/admin/indices/upgrade/index-0.90.6.zip b/core/src/test/resources/org/elasticsearch/action/admin/indices/upgrade/index-0.90.6.zip similarity index 100% rename from core/src/test/resources/org/elasticsearch/rest/action/admin/indices/upgrade/index-0.90.6.zip rename to core/src/test/resources/org/elasticsearch/action/admin/indices/upgrade/index-0.90.6.zip diff --git a/core/src/test/resources/org/elasticsearch/action/admin/indices/upgrade/index-conflicting-mappings-1.7.0.zip b/core/src/test/resources/org/elasticsearch/action/admin/indices/upgrade/index-conflicting-mappings-1.7.0.zip new file mode 100644 index 0000000000000000000000000000000000000000..559aeb78e7ea04b847ba5e752e24e0bd4e0cce04 GIT binary patch literal 92022 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�O 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 Date: Tue, 4 Aug 2015 22:16:30 -0400 Subject: [PATCH 21/23] improve integration tests output when ES cannot be started. This can happen for a number of reasons, including bugs. Today you will get a super crappy failure, telling you a .pid file was not found... you can go look in target/integ-tests/elasticsearch-xxx/logs and examine the log file, but thats kinda a pain and not easy if its a jenkins server. Instead we can fail like this: ``` start-external-cluster-with-plugin: [echo] Installing plugin elasticsearch-example-jvm-plugin... [mkdir] Created dir: /home/rmuir/workspace/elasticsearch/plugins/jvm-example/target/integ-tests/temp [exec] -> Installing elasticsearch-example-jvm-plugin... [exec] Plugins directory [/home/rmuir/workspace/elasticsearch/plugins/jvm-example/target/integ-tests/elasticsearch-2.0.0-SNAPSHOT/plugins] does not exist. Creating... [exec] Trying file:/home/rmuir/workspace/elasticsearch/plugins/jvm-example/target/releases/elasticsearch-example-jvm-plugin-2.0.0-SNAPSHOT.zip ... [exec] Downloading ...........DONE [exec] PluginInfo{name='example-jvm-plugin', description='Demonstrates all the pluggable Java entry points in Elasticsearch', site=false, jvm=true, classname=org.elasticsearch.plugin.example.ExampleJvmPlugin, isolated=true, version='2.0.0-SNAPSHOT'} [exec] Installed example-jvm-plugin into /home/rmuir/workspace/elasticsearch/plugins/jvm-example/target/integ-tests/elasticsearch-2.0.0-SNAPSHOT/plugins/example-jvm-plugin [echo] Starting up external cluster... [echo] [2015-08-04 22:02:55,130][INFO ][org.elasticsearch.node ] [smoke_tester] version[2.0.0-SNAPSHOT], pid[4321], build[e2a47d8/2015-08-05T00:50:08Z] [echo] [2015-08-04 22:02:55,130][INFO ][org.elasticsearch.node ] [smoke_tester] initializing ... [echo] [2015-08-04 22:02:55,259][INFO ][org.elasticsearch.plugins] [smoke_tester] loaded [uber-plugin], sites [] [echo] [2015-08-04 22:02:55,260][ERROR][org.elasticsearch.bootstrap] Exception [echo] java.lang.NullPointerException [echo] at org.elasticsearch.common.settings.Settings$Builder.put(Settings.java:1051) [echo] at org.elasticsearch.plugins.PluginsService.updatedSettings(PluginsService.java:208) [echo] at org.elasticsearch.node.Node.(Node.java:148) [echo] at org.elasticsearch.node.NodeBuilder.build(NodeBuilder.java:157) [echo] at org.elasticsearch.bootstrap.Bootstrap.setup(Bootstrap.java:177) [echo] at org.elasticsearch.bootstrap.Bootstrap.main(Bootstrap.java:272) [echo] at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:28) [INFO] ------------------------------------------------------------------------ [INFO] BUILD FAILURE [INFO] ------------------------------------------------------------------------ [INFO] Total time: 25.178 s [INFO] Finished at: 2015-08-04T22:03:14-05:00 [INFO] Final Memory: 32M/515M [INFO] ------------------------------------------------------------------------ [ERROR] Failed to execute goal org.apache.maven.plugins:maven-antrun-plugin:1.8:run (integ-setup) on project elasticsearch-example-jvm-plugin: An Ant BuildException has occured: The following error occurred while executing this line: [ERROR] /home/rmuir/workspace/elasticsearch/plugins/jvm-example/target/dev-tools/ant/integration-tests.xml:176: The following error occurred while executing this line: [ERROR] /home/rmuir/workspace/elasticsearch/plugins/jvm-example/target/dev-tools/ant/integration-tests.xml:142: ES instance did not start ``` --- .../main/resources/ant/integration-tests.xml | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/dev-tools/src/main/resources/ant/integration-tests.xml b/dev-tools/src/main/resources/ant/integration-tests.xml index 191b0c049f7..4cd6b4728f8 100644 --- a/dev-tools/src/main/resources/ant/integration-tests.xml +++ b/dev-tools/src/main/resources/ant/integration-tests.xml @@ -12,9 +12,12 @@ + + + @@ -120,12 +123,24 @@ Starting up external cluster... + args="@{args} -Des.path.repo=@{home}/repo"/> - + + + + + + + + + External cluster started PID ${integ.pid} From 9028fb8fbe1bf18fdce7c01c715d9e43fee01454 Mon Sep 17 00:00:00 2001 From: Alexander Reelsen Date: Wed, 5 Aug 2015 09:36:34 +0200 Subject: [PATCH 22/23] Release: Update build release script to reflect latest changes As the script now deploys to S3 and several things in master have changed, this script needs to reflect the latest changes * An unsigned RPM is built by default, so that users of older RPM based distros can download and use that RPM by default * In addition a signed RPM is built, that is used for the repositories * Paths for the new distributions have been fixed * The check for the number of jars has been removed, as this is done as part of the license checking in `mvn verify` * Checksum generation has been removed, as this is done as part of the mvn build * Publishing artifacts of S3 has been removed * Repostitory creation script has been updated --- dev-tools/build_release.py | 104 +++++++++----------------------- dev-tools/build_repositories.sh | 6 +- distribution/rpm/pom.xml | 7 ++- 3 files changed, 39 insertions(+), 78 deletions(-) diff --git a/dev-tools/build_release.py b/dev-tools/build_release.py index 483076a6118..5d41aef90bc 100644 --- a/dev-tools/build_release.py +++ b/dev-tools/build_release.py @@ -251,27 +251,20 @@ def build_release(release_version, run_tests=False, dry_run=True, cpus=1, bwc_ve print('Running Backwards compatibility tests against version [%s]' % (bwc_version)) run_mvn('clean', 'test -Dtests.filter=@backwards -Dtests.bwc.version=%s -Dtests.bwc=true -Dtests.jvms=1' % bwc_version) run_mvn('clean test-compile -Dforbidden.test.signatures="org.apache.lucene.util.LuceneTestCase\$AwaitsFix @ Please fix all bugs before release"') - gpg_args = '-Dgpg.key="%s" -Dgpg.passphrase="%s" -Ddeb.sign=true -Drpm.sign=true' % (env.get('GPG_KEY_ID'), env.get('GPG_PASSPHRASE')) + # dont sign the RPM, so older distros will be able to use the uploaded RPM package + gpg_args = '-Dgpg.key="%s" -Dgpg.passphrase="%s" -Ddeb.sign=true -Drpm.sign=false' % (env.get('GPG_KEY_ID'), env.get('GPG_PASSPHRASE')) if env.get('GPG_KEYRING'): gpg_args += ' -Dgpg.keyring="%s"' % env.get('GPG_KEYRING') run_mvn('clean %s -DskipTests %s' % (target, gpg_args)) success = False try: - # create unsigned RPM first for downloads.elasticsearch.org - run_mvn('-DskipTests rpm:rpm') - # move unsigned RPM to target/releases - # this is an oddness of RPM that is attaches -1 so we have to rename it - rpm = os.path.join('target/rpm/elasticsearch/RPMS/noarch/', 'elasticsearch-%s-1.noarch.rpm' % release_version) + # create additional signed RPM for the repositories + run_mvn('-f distribution/rpm/pom.xml package -DskipTests -Dsign.rpm=true -Drpm.outputDirectory=target/releases/signed/ %s' % (gpg_args)) + rpm = os.path.join('target/releases/signed', 'elasticsearch-%s.rpm' % release_version) if os.path.isfile(rpm): - log('RPM [%s] contains: ' % rpm) + log('Signed RPM [%s] contains: ' % rpm) run('rpm -pqli %s' % rpm) - renamed_rpm = os.path.join('target/releases/', 'elasticsearch-%s.noarch.rpm' % release_version) - shutil.move(rpm, renamed_rpm) - else: - raise RuntimeError('Could not find required RPM at %s' % rpm) - # now create signed RPM for repositories - run_mvn('-DskipTests rpm:rpm %s' % (gpg_args)) - success = True + success = True finally: if not success: print(""" @@ -358,63 +351,44 @@ def find_release_version(src_branch): return match.group(1) raise RuntimeError('Could not find release version in branch %s' % src_branch) -def artifact_names(release, path = ''): - artifacts = [os.path.join(path, 'elasticsearch-%s.%s' % (release, t)) for t in ['deb', 'tar.gz', 'zip']] - artifacts.append(os.path.join(path, 'elasticsearch-%s.noarch.rpm' % (release))) +def artifact_names(release): + artifacts = [] + artifacts.append(os.path.join('distribution/zip/target/releases', 'elasticsearch-%s.zip' % (release))) + artifacts.append(os.path.join('distribution/tar/target/releases', 'elasticsearch-%s.tar.gz' % (release))) + artifacts.append(os.path.join('distribution/deb/target/releases', 'elasticsearch-%s.deb' % (release))) + artifacts.append(os.path.join('distribution/rpm/target/releases', 'elasticsearch-%s.rpm' % (release))) return artifacts def get_artifacts(release): - common_artifacts = artifact_names(release, 'target/releases/') + common_artifacts = artifact_names(release) for f in common_artifacts: if not os.path.isfile(f): raise RuntimeError('Could not find required artifact at %s' % f) return common_artifacts -# Checks the jar files in each package -# Barfs if any of the package jar files differ -def check_artifacts_for_same_jars(artifacts): - jars = [] - for file in artifacts: - if file.endswith('.zip'): - jars.append(subprocess.check_output("unzip -l %s | grep '\.jar$' | awk -F '/' '{ print $NF }' | sort" % file, shell=True)) - if file.endswith('.tar.gz'): - jars.append(subprocess.check_output("tar tzvf %s | grep '\.jar$' | awk -F '/' '{ print $NF }' | sort" % file, shell=True)) - if file.endswith('.rpm'): - jars.append(subprocess.check_output("rpm -pqli %s | grep '\.jar$' | awk -F '/' '{ print $NF }' | sort" % file, shell=True)) - if file.endswith('.deb'): - jars.append(subprocess.check_output("dpkg -c %s | grep '\.jar$' | awk -F '/' '{ print $NF }' | sort" % file, shell=True)) - if len(set(jars)) != 1: - raise RuntimeError('JAR contents of packages are not the same, please check the package contents. Use [unzip -l], [tar tzvf], [dpkg -c], [rpm -pqli] to inspect') - -# Generates sha1 checsums for all files -# and returns the checksum files as well -# as the given files in a list -def generate_checksums(files): - res = [] - for release_file in files: - directory = os.path.dirname(release_file) - file = os.path.basename(release_file) - checksum_file = '%s.sha1.txt' % file - - if os.system('cd %s; shasum %s > %s' % (directory, file, checksum_file)): - raise RuntimeError('Failed to generate checksum for file %s' % release_file) - res = res + [os.path.join(directory, checksum_file), release_file] - return res - -def download_and_verify(release, files, plugins=None, base_url='https://download.elastic.co/elasticsearch/elasticsearch'): +# Sample URL: +# http://download.elasticsearch.org/elasticsearch/release/org/elasticsearch/distribution/elasticsearch-rpm/2.0.0-beta1-SNAPSHOT/elasticsearch-rpm-2.0.0-beta1-SNAPSHOT.rpm +def download_and_verify(release, files, plugins=None, base_url='https://download.elastic.co/elasticsearch/release/org/elasticsearch/distribution'): print('Downloading and verifying release %s from %s' % (release, base_url)) tmp_dir = tempfile.mkdtemp() try: downloaded_files = [] for file in files: name = os.path.basename(file) - url = '%s/%s' % (base_url, name) + if name.endswith('tar.gz'): + url = '%s/tar/elasticsearch/%s/%s' % (base_url, release, name) + elif name.endswith('zip'): + url = '%s/zip/elasticsearch/%s/%s' % (base_url, release, name) + elif name.endswith('rpm'): + url = '%s/rpm/elasticsearch/%s/%s' % (base_url, release, name) + elif name.endswith('deb'): + url = '%s/deb/elasticsearch/%s/%s' % (base_url, release, name) abs_file_path = os.path.join(tmp_dir, name) print(' Downloading %s' % (url)) downloaded_files.append(abs_file_path) urllib.request.urlretrieve(url, abs_file_path) - url = ''.join([url, '.sha1.txt']) - checksum_file = os.path.join(tmp_dir, ''.join([abs_file_path, '.sha1.txt'])) + url = ''.join([url, '.sha1']) + checksum_file = os.path.join(tmp_dir, ''.join([abs_file_path, '.sha1'])) urllib.request.urlretrieve(url, checksum_file) print(' Verifying checksum %s' % (checksum_file)) run('cd %s && sha1sum -c %s' % (tmp_dir, os.path.basename(checksum_file))) @@ -444,10 +418,7 @@ def smoke_test_release(release, files, expected_hash, plugins): run('%s; %s install %s' % (java_exe(), es_plugin_path, plugin)) plugin_names[name] = True - if release.startswith("0.90."): - background = '' # 0.90.x starts in background automatically - else: - background = '-d' + background = '-d' print(' Starting elasticsearch deamon from [%s]' % os.path.join(tmp_dir, 'elasticsearch-%s' % release)) run('%s; %s -Des.node.name=smoke_tester -Des.cluster.name=prepare_release -Des.discovery.zen.ping.multicast.enabled=false -Des.script.inline=on -Des.script.indexed=on %s' % (java_exe(), es_run_path, background)) @@ -505,21 +476,11 @@ def merge_tag_push(remote, src_branch, release_version, dry_run): else: print(' dryrun [True] -- skipping push to remote %s' % remote) -def publish_artifacts(artifacts, base='elasticsearch/elasticsearch', dry_run=True): - location = os.path.dirname(os.path.realpath(__file__)) - for artifact in artifacts: - if dry_run: - print('Skip Uploading %s to Amazon S3' % artifact) - else: - print('Uploading %s to Amazon S3' % artifact) - # requires boto to be installed but it is not available on python3k yet so we use a dedicated tool - run('python %s/upload-s3.py --file %s ' % (location, os.path.abspath(artifact))) - def publish_repositories(version, dry_run=True): if dry_run: print('Skipping package repository update') else: - print('Triggering repository update - calling dev-tools/build_repositories.sh %s' % version) + print('Triggering repository update for version %s - calling dev-tools/build_repositories.sh %s' % (version, src_branch)) # src_branch is a version like 1.5/1.6/2.0/etc.. so we can use this run('dev-tools/build_repositories.sh %s' % src_branch) @@ -756,22 +717,17 @@ if __name__ == '__main__': print('Building Release candidate') input('Press Enter to continue...') if not dry_run: - print(' Running maven builds now and publish to Sonatype - run-tests [%s]' % run_tests) + print(' Running maven builds now and publish to Sonatype and S3 - run-tests [%s]' % run_tests) else: print(' Running maven builds now run-tests [%s]' % run_tests) build_release(release_version, run_tests=run_tests, dry_run=dry_run, cpus=cpus, bwc_version=find_bwc_version(release_version, bwc_path)) artifacts = get_artifacts(release_version) - print('Checking if all artifacts contain the same jars') - check_artifacts_for_same_jars(artifacts) - artifacts_and_checksum = generate_checksums(artifacts) smoke_test_release(release_version, artifacts, get_head_hash(), PLUGINS) print(''.join(['-' for _ in range(80)])) print('Finish Release -- dry_run: %s' % dry_run) input('Press Enter to continue...') print(' merge release branch, tag and push to %s %s -- dry_run: %s' % (remote, src_branch, dry_run)) merge_tag_push(remote, src_branch, release_version, dry_run) - print(' publish artifacts to S3 -- dry_run: %s' % dry_run) - publish_artifacts(artifacts_and_checksum, dry_run=dry_run) print(' Updating package repositories -- dry_run: %s' % dry_run) publish_repositories(src_branch, dry_run=dry_run) cherry_pick_command = '.' diff --git a/dev-tools/build_repositories.sh b/dev-tools/build_repositories.sh index 9bad8ff2c14..d00f6c2e2ad 100755 --- a/dev-tools/build_repositories.sh +++ b/dev-tools/build_repositories.sh @@ -158,8 +158,8 @@ mkdir -p $centosdir echo "RPM: Syncing repository for version $version into $centosdir" $s3cmd sync s3://$S3_BUCKET_SYNC_FROM/elasticsearch/$version/centos/ $centosdir -rpm=target/rpm/elasticsearch/RPMS/noarch/elasticsearch*.rpm -echo "RPM: Copying $rpm into $centosdor" +rpm=distribution/rpm/target/releases/signed/elasticsearch*.rpm +echo "RPM: Copying signed $rpm into $centosdir" cp $rpm $centosdir echo "RPM: Running createrepo in $centosdir" @@ -176,7 +176,7 @@ $s3cmd sync -P $centosdir/ s3://$S3_BUCKET_SYNC_TO/elasticsearch/$version/centos ## DEB ################### -deb=target/releases/elasticsearch*.deb +deb=distribution/deb/target/releases/elasticsearch*.deb echo "DEB: Creating repository directory structure" diff --git a/distribution/rpm/pom.xml b/distribution/rpm/pom.xml index 698c310ecc3..86da5bba0f2 100644 --- a/distribution/rpm/pom.xml +++ b/distribution/rpm/pom.xml @@ -23,6 +23,11 @@ + + true + ${project.build.directory}/releases/ + + @@ -301,7 +306,7 @@ ${project.version} ${project.packaging} true - ${project.build.directory}/releases/ + ${rpm.outputDirectory} From 0b0846f84b2c2b79a5a3553c4d5df411c31e62ee Mon Sep 17 00:00:00 2001 From: Clinton Gormley Date: Wed, 5 Aug 2015 10:52:56 +0200 Subject: [PATCH 23/23] Updated multi-match-query.asciidoc Corrected note about which field is boosted in a cross-fields multi_match query. Relates to #12294 --- docs/reference/query-dsl/multi-match-query.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/query-dsl/multi-match-query.asciidoc b/docs/reference/query-dsl/multi-match-query.asciidoc index 940fd05ab2e..ecfaad76d6d 100644 --- a/docs/reference/query-dsl/multi-match-query.asciidoc +++ b/docs/reference/query-dsl/multi-match-query.asciidoc @@ -304,7 +304,7 @@ That solves one of the two problems. The problem of differing term frequencies is solved by _blending_ the term frequencies for all fields in order to even out the differences. In other words, `first_name:smith` will be treated as though it has the same weight as `last_name:smith`. (Actually, -`first_name:smith` is given a tiny advantage over `last_name:smith`, just to +`last_name:smith` is given a tiny advantage over `first_name:smith`, just to make the order of results more stable.) If you run the above query through the <>, it returns this