Build: Use ant exec for starting elasticsearch in integ tests

Currently elasticsearch in integ tests is started using an ant task on
windows, or gradle exec on everything else. However, gradle exec has
some flaws, one being Ctrl-C does not run finalizedBy tasks, which means
interrupting integ tests will leak a jvm. This change makes all systems
use ant exec. One caveat is, if there is any output by the jvm, we lose
it in ant bit heaven. But this is no different than what we had with
gradle. In the future, we should look at using a separate thread to
pump streams from the elasticsearch process.

closes #14701
This commit is contained in:
Ryan Ernst 2015-11-12 22:33:21 -08:00
parent 776bb288b5
commit 9373382155
1 changed files with 58 additions and 42 deletions

View File

@ -18,22 +18,17 @@
*/
package org.elasticsearch.gradle.test
import org.gradle.internal.jvm.Jvm
import java.nio.file.Paths
import org.apache.tools.ant.DefaultLogger
import org.apache.tools.ant.taskdefs.condition.Os
import org.elasticsearch.gradle.VersionProperties
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.InvalidUserDataException
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.*
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.Delete
import org.gradle.api.tasks.Exec
import java.nio.file.Paths
/**
* A helper for creating tasks to build a cluster that is used by a task, and tear down the cluster when the task is finished.
*/
@ -216,23 +211,57 @@ class ClusterFormationTasks {
'JAVA_HOME' : project.javaHome,
'ES_GC_OPTS': config.jvmArgs
]
List esProps = config.systemProperties.collect { key, value -> "-D${key}=${value}" }
List<String> esProps = config.systemProperties.collect { key, value -> "-D${key}=${value}" }
for (Map.Entry<String, String> property : System.properties.entrySet()) {
if (property.getKey().startsWith('es.')) {
esProps.add("-D${property.getKey()}=${property.getValue()}")
}
}
Closure esPostStartActions = { ant, logger ->
ant.waitfor(maxwait: '30', maxwaitunit: 'second', checkevery: '500', checkeveryunit: 'millisecond', timeoutproperty: "failed${name.capitalize()}") {
String executable
List<String> esArgs = []
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
executable = 'cmd'
esArgs.add('/C')
esArgs.add('call')
} else {
executable = 'sh'
}
// running with cmd on windows will look for this with the .bat extension
esArgs.add(new File(home, 'bin/elasticsearch').toString())
// this closure is converted into ant nodes by groovy's AntBuilder
Closure antRunner = {
exec(executable: executable, spawn: config.daemonize, dir: cwd, taskname: 'elasticsearch') {
esEnv.each { key, value -> env(key: key, value: value) }
(esArgs + esProps).each { arg(value: it) }
}
waitfor(maxwait: '30', maxwaitunit: 'second', checkevery: '500', checkeveryunit: 'millisecond', timeoutproperty: "failed${name}") {
and {
resourceexists {
file file: pidFile.toString()
file(file: pidFile.toString())
}
http(url: "http://localhost:${config.httpPort}")
}
}
}
// this closure is the actual code to run elasticsearch
Closure elasticsearchRunner = {
ByteArrayOutputStream buffer = new ByteArrayOutputStream()
if (logger.isInfoEnabled() || config.daemonize == false) {
// run with piping streams directly out (even stderr to stdout since gradle would capture it)
runAntCommand(project, antRunner, System.out, System.err)
} else {
// buffer the output, we may not need to print it
PrintStream captureStream = new PrintStream(buffer, true, "UTF-8")
runAntCommand(project, antRunner, captureStream, captureStream)
}
if (ant.properties.containsKey("failed${name}".toString())) {
// the waitfor failed, so dump any output we got (may be empty if info logging, but that is ok)
logger.error(buffer.toString('UTF-8'))
// also dump the cluster's log file, it may be useful
File logFile = new File(home, "logs/${clusterName}.log")
if (logFile.exists()) {
logFile.eachLine { line -> logger.error(line) }
@ -240,35 +269,10 @@ class ClusterFormationTasks {
throw new GradleException('Failed to start elasticsearch')
}
}
File esScript = new File(home, 'bin/elasticsearch')
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
// elasticsearch.bat is spawned as it has no daemon mode
return project.tasks.create(name: name, type: DefaultTask, dependsOn: setup) << {
// Fall back to Ant exec task as Gradle Exec task does not support spawning yet
ant.exec(executable: 'cmd', spawn: config.daemonize, dir: cwd) {
esEnv.each { key, value -> env(key: key, value: value) }
(['/C', 'call', esScript] + esProps).each { arg(value: it) }
}
esPostStartActions(ant, logger)
}
} else {
List esExecutable = [esScript]
if(config.daemonize) {
esExecutable.add("-d")
}
return project.tasks.create(name: name, type: Exec, dependsOn: setup) {
workingDir cwd
executable 'sh'
args esExecutable
args esProps
environment esEnv
doLast {
esPostStartActions(ant, logger)
}
}
}
Task start = project.tasks.create(name: name, type: DefaultTask, dependsOn: setup)
start.doLast(elasticsearchRunner)
return start
}
/** Adds a task to check if the process with the given pidfile is actually elasticsearch */
@ -347,4 +351,16 @@ class ClusterFormationTasks {
static File pidFile(File dir) {
return new File(dir, 'es.pid')
}
/** Runs an ant command, sending output to the given out and error streams */
static void runAntCommand(Project project, Closure command, PrintStream outputStream, PrintStream errorStream) {
DefaultLogger listener = new DefaultLogger(
errorPrintStream: errorStream,
outputPrintStream: outputStream,
messageOutputLevel: org.apache.tools.ant.Project.MSG_INFO)
project.ant.project.addBuildListener(listener)
project.configure(project.ant, command)
project.ant.project.removeBuildListener(listener)
}
}