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:
parent
776bb288b5
commit
9373382155
|
@ -18,22 +18,17 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.gradle.test
|
package org.elasticsearch.gradle.test
|
||||||
|
|
||||||
import org.gradle.internal.jvm.Jvm
|
import org.apache.tools.ant.DefaultLogger
|
||||||
|
|
||||||
import java.nio.file.Paths
|
|
||||||
|
|
||||||
import org.apache.tools.ant.taskdefs.condition.Os
|
import org.apache.tools.ant.taskdefs.condition.Os
|
||||||
import org.elasticsearch.gradle.VersionProperties
|
import org.elasticsearch.gradle.VersionProperties
|
||||||
import org.gradle.api.DefaultTask
|
import org.gradle.api.*
|
||||||
import org.gradle.api.GradleException
|
|
||||||
import org.gradle.api.InvalidUserDataException
|
|
||||||
import org.gradle.api.Project
|
|
||||||
import org.gradle.api.Task
|
|
||||||
import org.gradle.api.file.FileCollection
|
import org.gradle.api.file.FileCollection
|
||||||
import org.gradle.api.tasks.Copy
|
import org.gradle.api.tasks.Copy
|
||||||
import org.gradle.api.tasks.Delete
|
import org.gradle.api.tasks.Delete
|
||||||
import org.gradle.api.tasks.Exec
|
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.
|
* 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,
|
'JAVA_HOME' : project.javaHome,
|
||||||
'ES_GC_OPTS': config.jvmArgs
|
'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()) {
|
for (Map.Entry<String, String> property : System.properties.entrySet()) {
|
||||||
if (property.getKey().startsWith('es.')) {
|
if (property.getKey().startsWith('es.')) {
|
||||||
esProps.add("-D${property.getKey()}=${property.getValue()}")
|
esProps.add("-D${property.getKey()}=${property.getValue()}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Closure esPostStartActions = { ant, logger ->
|
String executable
|
||||||
ant.waitfor(maxwait: '30', maxwaitunit: 'second', checkevery: '500', checkeveryunit: 'millisecond', timeoutproperty: "failed${name.capitalize()}") {
|
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 {
|
and {
|
||||||
resourceexists {
|
resourceexists {
|
||||||
file file: pidFile.toString()
|
file(file: pidFile.toString())
|
||||||
}
|
}
|
||||||
http(url: "http://localhost:${config.httpPort}")
|
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())) {
|
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")
|
File logFile = new File(home, "logs/${clusterName}.log")
|
||||||
if (logFile.exists()) {
|
if (logFile.exists()) {
|
||||||
logFile.eachLine { line -> logger.error(line) }
|
logFile.eachLine { line -> logger.error(line) }
|
||||||
|
@ -240,35 +269,10 @@ class ClusterFormationTasks {
|
||||||
throw new GradleException('Failed to start elasticsearch')
|
throw new GradleException('Failed to start elasticsearch')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
File esScript = new File(home, 'bin/elasticsearch')
|
|
||||||
|
Task start = project.tasks.create(name: name, type: DefaultTask, dependsOn: setup)
|
||||||
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
start.doLast(elasticsearchRunner)
|
||||||
// elasticsearch.bat is spawned as it has no daemon mode
|
return start
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Adds a task to check if the process with the given pidfile is actually elasticsearch */
|
/** 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) {
|
static File pidFile(File dir) {
|
||||||
return new File(dir, 'es.pid')
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue