diff --git a/elasticsearch5/pom.xml b/elasticsearch5/pom.xml index 3eb388084de5d63412f5a82085ba110bbd179bd4..5ceabfdbd291e80bf9f385111d98c6b79b9d7873 100644 --- a/elasticsearch5/pom.xml +++ b/elasticsearch5/pom.xml @@ -29,7 +29,143 @@ LICENSE file. <properties> <!-- Skip tests by default. will be activated by jdk8 profile --> <skipTests>true</skipTests> + <elasticsearch.groupid>org.elasticsearch.distribution.zip</elasticsearch.groupid> + + <!-- For integration tests using ANT --> + <integ.http.port>9400</integ.http.port> + <integ.transport.port>9500</integ.transport.port> </properties> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-dependency-plugin</artifactId> + <version>2.10</version> + <executions> + <execution> + <id>integ-setup-dependencies</id> + <phase>pre-integration-test</phase> + <goals> + <goal>copy</goal> + </goals> + <configuration> + <skip>${skipTests}</skip> + <artifactItems> + <artifactItem> + <groupId>${elasticsearch.groupid}</groupId> + <artifactId>elasticsearch</artifactId> + <version>${elasticsearch5-version}</version> + <type>zip</type> + </artifactItem> + </artifactItems> + <useBaseVersion>true</useBaseVersion> + <outputDirectory>${project.build.directory}/integration-tests/binaries</outputDirectory> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-antrun-plugin</artifactId> + <version>1.8</version> + <executions> + <!-- start up external cluster --> + <execution> + <id>integ-setup</id> + <phase>pre-integration-test</phase> + <goals> + <goal>run</goal> + </goals> + <configuration> + <skip>${skipTests}</skip> + <target> + <ant antfile="src/test/ant/integration-tests.xml" target="start-external-cluster"/> + </target> + </configuration> + </execution> + <!-- shut down external cluster --> + <execution> + <id>integ-teardown</id> + <phase>post-integration-test</phase> + <goals> + <goal>run</goal> + </goals> + <configuration> + <skip>${skipTests}</skip> + <target> + <ant antfile="src/test/ant/integration-tests.xml" target="stop-external-cluster"/> + </target> + </configuration> + </execution> + </executions> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>2.19</version> + <executions> + <execution> + <id>default-test</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + + <plugin> + <groupId>com.carrotsearch.randomizedtesting</groupId> + <artifactId>junit4-maven-plugin</artifactId> + <version>2.3.3</version> + + <configuration> + <assertions enableSystemAssertions="false"> + <enable/> + </assertions> + + <listeners> + <report-text /> + </listeners> + </configuration> + + <executions> + <execution> + <id>unit-tests</id> + <phase>test</phase> + <goals> + <goal>junit4</goal> + </goals> + <inherited>true</inherited> + <configuration> + <skipTests>${skipTests}</skipTests> + <includes> + <include>**/*Test.class</include> + </includes> + <excludes> + <exclude>**/*$*</exclude> + </excludes> + </configuration> + </execution> + <execution> + <id>integration-tests</id> + <phase>integration-test</phase> + <goals> + <goal>junit4</goal> + </goals> + <inherited>true</inherited> + <configuration> + <skipTests>${skipTests}</skipTests> + <includes> + <include>**/*IT.class</include> + </includes> + <excludes> + <exclude>**/*$*</exclude> + </excludes> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> <artifactId>elasticsearch5-binding</artifactId> <name>Elasticsearch 5.x Binding</name> @@ -75,4 +211,19 @@ LICENSE file. <scope>test</scope> </dependency> </dependencies> + + <profiles> + <!-- Requires JDK8 to run, so none of our tests + will work unless we're using jdk8. + --> + <profile> + <id>jdk8-tests</id> + <activation> + <jdk>1.8</jdk> + </activation> + <properties> + <skipTests>false</skipTests> + </properties> + </profile> + </profiles> </project> diff --git a/elasticsearch5/src/main/java/com/yahoo/ycsb/db/elasticsearch5/ElasticsearchRestClient.java b/elasticsearch5/src/main/java/com/yahoo/ycsb/db/elasticsearch5/ElasticsearchRestClient.java index e4f3c9e535740efec00ac52077f3e04049265b4a..55ddc5b4d8e6996f5c6df2b06708a18d54018be7 100644 --- a/elasticsearch5/src/main/java/com/yahoo/ycsb/db/elasticsearch5/ElasticsearchRestClient.java +++ b/elasticsearch5/src/main/java/com/yahoo/ycsb/db/elasticsearch5/ElasticsearchRestClient.java @@ -120,13 +120,11 @@ public class ElasticsearchRestClient extends DB { Collections.<String, String>emptyMap(), new NStringEntity(new ObjectMapper().writeValueAsString(data), ContentType.APPLICATION_JSON)); - if(response.getStatusLine().getStatusCode() == 200) { - return Status.OK; - } + return Status.OK; } catch (Exception e) { e.printStackTrace(); + return Status.ERROR; } - return Status.ERROR; } @Override @@ -136,13 +134,11 @@ public class ElasticsearchRestClient extends DB { HttpDelete.METHOD_NAME, "/" + indexKey + "/" + table + "/" + key); - if(response.getStatusLine().getStatusCode() == 200) { - return Status.OK; - } + return Status.OK; } catch (Exception e) { e.printStackTrace(); + return Status.ERROR; } - return Status.ERROR; } @Override @@ -150,13 +146,11 @@ public class ElasticsearchRestClient extends DB { try { Response response = restClient.performRequest(HttpGet.METHOD_NAME, "/"); - if(response.getStatusLine().getStatusCode() == 200) { - return Status.OK; - } + return Status.OK; } catch (Exception e) { e.printStackTrace(); + return Status.ERROR; } - return Status.ERROR; // try { // final GetResponse response = client.prepareGet(indexKey, table, key).execute().actionGet(); diff --git a/elasticsearch5/src/test/ant/integration-tests.xml b/elasticsearch5/src/test/ant/integration-tests.xml new file mode 100644 index 0000000000000000000000000000000000000000..59675ccebc08a8d296237000b7a8f6f2f8d046d8 --- /dev/null +++ b/elasticsearch5/src/test/ant/integration-tests.xml @@ -0,0 +1,282 @@ +<?xml version="1.0"?> +<project name="integration-tests" xmlns:if="ant:if" xmlns:unless="ant:unless"> + <!-- our pid file for easy cleanup --> + <property name="integ.pidfile" location="${project.build.directory}/integration-tests/run/es.pid"/> + + <!-- if this exists, ES is running (maybe) --> + <available property="integ.pidfile.exists" file="${integ.pidfile}"/> + + <!-- name of our cluster, maybe needs changing --> + <property name="integ.cluster.name" value="elasticsearch_integration"/> + + <!-- runs an OS script --> + <macrodef name="run-script"> + <attribute name="script"/> + <attribute name="spawn" default="false"/> + <element name="nested" optional="true"/> + <sequential> + <local name="failonerror"/> + <condition property="failonerror"> + <isfalse value="@{spawn}"/> + </condition> + + <!-- create a temp CWD, to enforce that commands don't rely on CWD --> + <local name="temp.cwd"/> + <tempfile property="temp.cwd" destDir="${project.build.directory}/integration-tests/run/tmp" deleteonexit="true"/> + <mkdir dir="${temp.cwd}"/> + + <!-- print commands we run --> + <local name="script.base"/> + <basename file="@{script}" property="script.base"/> + <!-- crappy way to output, but we need it. make it nice later --> + <echoxml><exec script="${script.base}"><nested/></exec></echoxml> + <exec executable="cmd" osfamily="winnt" dir="${temp.cwd}" failonerror="${failonerror}" spawn="@{spawn}" taskname="${script.base}"> + <arg value="/c"/> + <arg value="""/> + <arg value="@{script}.bat"/> + <nested/> + <arg value="""/> + </exec> + + <exec executable="bash" osfamily="unix" dir="${temp.cwd}" failonerror="${failonerror}" spawn="@{spawn}" taskname="${script.base}"> + <arg value="@{script}"/> + <nested/> + </exec> + </sequential> + </macrodef> + + <!-- extracts PID from file --> + <macrodef name="extract-pid"> + <attribute name="file"/> + <attribute name="property"/> + <sequential> + <loadfile srcFile="@{file}" property="@{property}"> + <filterchain> + <striplinebreaks/> + </filterchain> + </loadfile> + </sequential> + </macrodef> + + <!-- applies transformations to src and stores in dst --> + <macrodef name="filter-property"> + <attribute name="src"/> + <attribute name="dest"/> + <element name="chain"/> + <sequential> + <loadresource property="@{dest}"> + <propertyresource name="@{src}"/> + <filterchain> + <tokenfilter> + <chain/> + </tokenfilter> + </filterchain> + </loadresource> + </sequential> + </macrodef> + + <!-- waits for elasticsearch to start --> + <macrodef name="waitfor-elasticsearch"> + <attribute name="port"/> + <attribute name="timeoutproperty"/> + <sequential> + <echo>Waiting for elasticsearch to become available on port @{port}...</echo> + <waitfor maxwait="30" maxwaitunit="second" + checkevery="500" checkeveryunit="millisecond" + timeoutproperty="@{timeoutproperty}"> + <http url="http://localhost:@{port}"/> + </waitfor> + </sequential> + </macrodef> + + <scriptdef name="isGreater" language="javascript"> + <attribute name="v1"/> + <attribute name="v2"/> + <![CDATA[ + + var i, l, d, s = false; + + a = attributes.get("v1").split('.'); + b = attributes.get("v2").split('.'); + l = Math.min(a.length, b.length); + + for (i=0; i<l; i++) { + d = parseInt(a[i], 10) - parseInt(b[i], 10); + if (d !== 0) { + project.setProperty("compare-result", d > 0); + s = true; + break; + } + } + + if(!s){ + d = a.length - b.length; + project.setProperty("compare-result", d >= 0); + } + + ]]> + </scriptdef> + + <!-- start elasticsearch and wait until its ready --> + <macrodef name="startup-elasticsearch"> + <attribute name="home" default="${project.build.directory}/integration-tests/run/elasticsearch-${elasticsearch5-version}"/> + <attribute name="spawn" default="true"/> + <attribute name="es.cluster.name" default="${integ.cluster.name}"/> + <attribute name="es.http.port" default="${integ.http.port}"/> + <attribute name="es.transport.tcp.port" default="${integ.transport.port}"/> + <attribute name="es.pidfile" default="${integ.pidfile}"/> + <element name="additional-args" optional="true"/> + <sequential> + <!-- make sure no elasticsearch instance is currently running and listening on the port we need --> + <fail message="This test expects port @{es.http.port} to be free but an elasticsearch instance is already running and listening on that port. + Maybe the last test run did not manage to shut down the node correctly? + You must kill it before tests can run."> + <condition> + <socket server="localhost" port="@{es.http.port}"></socket> + </condition> + </fail> + <!-- run bin/elasticsearch with args --> + <echo>Starting up external cluster...</echo> + <isGreater v1="${elasticsearch5-version}" v2="5.0.0" /> + + <echo if:true="${compare-result}">running Elasticsearch 5.0.0 or superior</echo> + <echo unless:true="${compare-result}">running Elasticsearch < 5.0.0</echo> + + <run-script script="@{home}/bin/elasticsearch" + spawn="@{spawn}"> + <nested> + <arg value="-Des.pidfile=@{es.pidfile}" unless:true="${compare-result}"/> + <arg value="-Des.cluster.name=@{es.cluster.name}" unless:true="${compare-result}"/> + <arg value="-Des.http.port=@{es.http.port}" unless:true="${compare-result}"/> + <arg value="-Des.transport.tcp.port=@{es.transport.tcp.port}" unless:true="${compare-result}"/> + <arg value="-Des.network.host=127.0.0.1" unless:true="${compare-result}"/> + <arg value="-Epidfile=@{es.pidfile}" if:true="${compare-result}"/> + <arg value="-Ecluster.name=@{es.cluster.name}" if:true="${compare-result}"/> + <arg value="-Ehttp.port=@{es.http.port}" if:true="${compare-result}"/> + <arg value="-Etransport.tcp.port=@{es.transport.tcp.port}" if:true="${compare-result}"/> + <arg value="-Enetwork.host=127.0.0.1" if:true="${compare-result}"/> + <additional-args/> + </nested> + </run-script> + + <!-- wait for startup --> + <local name="failed.to.start"/> + <waitfor-elasticsearch port="@{es.http.port}" + timeoutproperty="failed.to.start"/> + + <!-- best effort, print console log. useful if it fails especially --> + <local name="log.contents"/> + <loadfile srcFile="@{home}/logs/@{es.cluster.name}.log" + property="log.contents" + failonerror="false"/> + <echo message="${log.contents}" taskname="elasticsearch"/> + + <fail message="ES instance did not start" if="failed.to.start"/> + + <local name="integ.pid"/> + <extract-pid file="@{es.pidfile}" property="integ.pid"/> + <echo>External node started PID ${integ.pid}</echo> + </sequential> + </macrodef> + + <macrodef name="stop-node"> + <attribute name="es.pidfile" default="${integ.pidfile}"/> + <sequential> + <local name="integ.pid"/> + + <extract-pid file="@{es.pidfile}" property="integ.pid"/> + <echo>Shutting down external node PID ${integ.pid}</echo> + <!-- verify with jps that this actually is the correct pid. + See if we can find the line "pid org.elasticsearch.bootstrap.Elasticsearch" in the output of jps -l.--> + <local name="jps.pidline"/> + <local name="jps.executable"/> + <local name="environment"/> + <property environment="environment"/> + <property name="jps.executable" location="${environment.JAVA_HOME}/bin/jps"/> + <exec executable="${jps.executable}" failonerror="true"> + <arg value="-l"/> + <redirector outputproperty="jps.pidline"> + <outputfilterchain> + <linecontains> + <contains value="${integ.pid} org.elasticsearch.bootstrap.Elasticsearch"/> + </linecontains> + </outputfilterchain> + </redirector> + </exec> + <fail + message="pid file at @{es.pidfile} is ${integ.pid} but jps -l did not report any process with org.elasticsearch.bootstrap.Elasticsearch and this pid. + Did you run mvn clean? Maybe an old pid file is still lying around."> + <condition> + <equals arg1="${jps.pidline}" arg2=""/> + </condition> + </fail> + + <exec executable="taskkill" failonerror="true" osfamily="winnt"> + <arg value="/F"/> + <arg value="/PID"/> + <arg value="${integ.pid}"/> + </exec> + <exec executable="kill" failonerror="true" osfamily="unix"> + <arg value="-9"/> + <arg value="${integ.pid}"/> + </exec> + <delete file="@{es.pidfile}"/> + </sequential> + </macrodef> + + <target name="stop-external-cluster" if="integ.pidfile.exists"> + <stop-node/> + </target> + + <target name="setup-workspace" depends="stop-external-cluster"> + <sequential> + <delete dir="${project.build.directory}/integration-tests/run"/> + <unzip src="${project.build.directory}/integration-tests/binaries/elasticsearch-${elasticsearch5-version}.zip" + dest="${project.build.directory}/integration-tests/run"/> + </sequential> + </target> + + <target name="start-external-cluster" depends="setup-workspace"> + <startup-elasticsearch/> + </target> + + <!-- unzip integ test artifact, install plugin, then start ES --> + <target name="start-external-cluster-with-plugin" depends="setup-workspace"> + <install-plugin name="${project.artifactId}" file="${project.build.directory}/releases/${project.artifactId}-${project.version}.zip"/> + <startup-elasticsearch/> + </target> + + <!-- installs a plugin into elasticsearch --> + <macrodef name="install-plugin"> + <attribute name="home" default="${project.build.directory}/integration-tests/run/elasticsearch-${elasticsearch5-version}"/> + <attribute name="name"/> + <attribute name="file"/> + <sequential> + <local name="url"/> + <makeurl property="url" file="@{file}"/> + + <isGreater v1="${elasticsearch5-version}" v2="5.0.0" /> + <property name="commandline" value="@{home}/bin/plugin" unless:true="${compare-result}"/> + <property name="commandline" value="@{home}/bin/elasticsearch-plugin" if:true="${compare-result}"/> + + <!-- install plugin --> + <echo>Installing plugin @{name}...</echo> + <run-script script="${commandline}"> + <nested> + <arg value="install"/> + <arg value="${url}"/> + </nested> + </run-script> + + <fail message="did not find plugin installed as @{name}"> + <condition> + <not> + <resourceexists> + <file file="@{home}/plugins/@{name}"/> + </resourceexists> + </not> + </condition> + </fail> + </sequential> + </macrodef> +</project> \ No newline at end of file diff --git a/elasticsearch5/src/test/java/com/yahoo/ycsb/db/elasticsearch5/ElasticsearchRestClientTestIT.java b/elasticsearch5/src/test/java/com/yahoo/ycsb/db/elasticsearch5/ElasticsearchRestClientTestIT.java new file mode 100644 index 0000000000000000000000000000000000000000..fac85fd7ba26e1eb74a64d15a059fe9f31dfbed9 --- /dev/null +++ b/elasticsearch5/src/test/java/com/yahoo/ycsb/db/elasticsearch5/ElasticsearchRestClientTestIT.java @@ -0,0 +1,124 @@ +/** + * Copyright (c) 2017 YCSB contributors. All rights reserved. + * <p> + * Licensed 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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. See accompanying + * LICENSE file. + */ + +package com.yahoo.ycsb.db.elasticsearch5; + +import com.yahoo.ycsb.ByteIterator; +import com.yahoo.ycsb.DBException; +import com.yahoo.ycsb.Status; +import com.yahoo.ycsb.StringByteIterator; +import org.junit.*; + +import java.util.HashMap; +import java.util.Properties; +import java.util.Set; +import java.util.Vector; + +import static org.junit.Assert.assertEquals; + +public class ElasticsearchRestClientTestIT { + private final static String TEST_HOST = "localhost:9400"; + private final static ElasticsearchRestClient instance = new ElasticsearchRestClient(); + private final static HashMap<String, ByteIterator> MOCK_DATA; + private final static String MOCK_TABLE = "MOCK_TABLE"; + private final static String MOCK_KEY0 = "0"; + private final static String MOCK_KEY1 = "1"; + private final static String MOCK_KEY2 = "2"; + + static { + MOCK_DATA = new HashMap<>(10); + for (int i = 1; i <= 10; i++) { + MOCK_DATA.put("field" + i, new StringByteIterator("value" + i)); + } + } + + @BeforeClass + public static void setUpClass() throws DBException { + final Properties props = new Properties(); + props.setProperty("es.hosts.list", TEST_HOST); + instance.setProperties(props); + instance.init(); + } + + @AfterClass + public static void tearDownClass() throws DBException { + instance.cleanup(); + } + + @Before + public void setUp() { + instance.insert(MOCK_TABLE, MOCK_KEY1, MOCK_DATA); + instance.insert(MOCK_TABLE, MOCK_KEY2, MOCK_DATA); + } + + @After + public void tearDown() { + instance.delete(MOCK_TABLE, MOCK_KEY1); + instance.delete(MOCK_TABLE, MOCK_KEY2); + } + + @Test + public void testInsert() { + Status result = instance.insert(MOCK_TABLE, MOCK_KEY0, MOCK_DATA); + assertEquals(Status.OK, result); + } + + @Test + public void testDelete() { + Status result = instance.delete(MOCK_TABLE, MOCK_KEY1); + assertEquals(Status.OK, result); + } + + @Test + public void testRead() { + Set<String> fields = MOCK_DATA.keySet(); + HashMap<String, ByteIterator> resultParam = new HashMap<>(10); + Status result = instance.read(MOCK_TABLE, MOCK_KEY1, fields, resultParam); + assertEquals(Status.OK, result); + } + + @Test + public void testUpdate() { + int i; + HashMap<String, ByteIterator> newValues = new HashMap<>(10); + + for (i = 1; i <= 10; i++) { + newValues.put("field" + i, new StringByteIterator("newvalue" + i)); + } + + Status result = instance.update(MOCK_TABLE, MOCK_KEY1, newValues); + assertEquals(Status.OK, result); + + //validate that the values changed + HashMap<String, ByteIterator> resultParam = new HashMap<>(10); + instance.read(MOCK_TABLE, MOCK_KEY1, MOCK_DATA.keySet(), resultParam); + + for (i = 1; i <= 10; i++) { + assertEquals("newvalue" + i, resultParam.get("field" + i).toString()); + } + + } + + @Test + public void testScan() { + int recordcount = 10; + Set<String> fields = MOCK_DATA.keySet(); + Vector<HashMap<String, ByteIterator>> resultParam = new Vector<>(10); + Status result = instance.scan(MOCK_TABLE, MOCK_KEY1, recordcount, fields, resultParam); + assertEquals(Status.NOT_IMPLEMENTED, result); + } +}