Get output of sh using gradle - java

I am facing an issue that I'm not able to figure out. What I want to achieve is to have a gradle task that spawn a docker-compose process that is a mssql server, and then use liquibase to run-up all migrations and seed the database.
But the problem is that the docker takes some time to get the server up, and the liquibase is running before it gets up.
What i did was to start docker-compose in a daemon using -d flag, and then use a loop to ping the server until the port 1433 responds and then let the gradle continue with the other dependent tasks (that actually creates the database and seed it).
here is what I did:
task checkDbStatusAndGetsItUp(){
group "localEnvironment"
description "Check current local db is up or sets it up"
dependsOn 'cloneEntityProject'
println 'Checking db Status and setting it up'
println '---------------------------'
def stdoutDocker = new ByteArrayOutputStream()
exec{
executable 'sh'
args "-c", """
docker ps | grep microsoft | wc -c
"""
standardOutput = stdoutDocker
}
doLast {
if (stdoutDocker.toString().trim() == '0') {
exec {
executable 'sh'
workingDir 'setup/dp-entidades'
args "-c", """
docker-compose up -d
"""
}
}
def shouldStop = false;
while (shouldStop == false){
def stdoutPing = new ByteArrayOutputStream()
exec{
workingDir 'setup/dp-entidades'
executable 'sh'
args """
nc -zv localhost 1433
"""
ignoreExitValue = true
standardOutput = stdoutPing
}
println stdoutPing.toString();
sleep(1000)
}
}
}
What I get from the above code is a loop showing that the docker never gets it up. But if I open another terminal and ping it manually it works, and the database is actually up. (I even tried to use telnet, with same results)
What I need to do, to achive the ping from the gradle and if success on conecting to database let the task continue?

-c flag of sh is missing in the last exec block. Another problem is that you never set shouldStop to true, so the last loop will never terminate. You can e.g. check the exit status of exec:
def result = exec { ... }
shouldStop = result.exitValue == 0
Note that you should also limit the number of tries to propagate server failure instead of waiting forever.

Related

kotlin Runtime.GetRuntime().exec process not terminating when executing 'docker-compose down'

I'm trying to stop and remove containers programmatically using a Kotlin process that execute the following command: docker-compose down --remove-orphans ,but the process never terminates, even after I call process.waitFor(). Alternatively, when I execute the same command in a powershell/cmd window the command terminates successfully.
Does anyone knows if there are any issues with 'Runtime.GetRuntime().exec()' or the docker-compose down command, and if there's another way to execute cmd/powershell commands (and receive their exit value) in Kotlin?
Thanks in advance.
My code:
val process: Process?
val sb = StringBuilder()
val command = "powershell.exe -command docker-compose down --remove-orphans"
process = Runtime.GetRuntime().exec(command)
process.waitFor()
val processStream: InputStream?
if(process.exitValue() == 0){
processStream = process.inputStream
} else {
processStream = process.errorStream
}
Scanner(processStream).use{
while(it.hasNextLine())
sb.append(it.nextLine() + System.lineSeparator())
}
println(sb.toString())

Executing command in ProcessBuilder does not seem to work for "java" commands in Windows (Kotlin)

Currently trying to execute a .jar file programmatically. But to test out java, I tried running the the following:
val p = ProcessBuilder("cmd.exe", "/c", "java", "-version").start()
val results: List<String> = p.inputStream.bufferedReader().readLines()
assertThat("Results should contain java version: ", results, hasItem(containsString("java version")))
However, nothing seems to output.
I am successfully able to run:
val pb = ProcessBuilder("cmd.exe", "/c", "echo", "hello world")
I have tried adding a working directory where the java executable is located, but nothing happens.
I am running out of ideas on how to make this work. If I run cmd and type out java -version I get the version information.
What else could I do to get this to work?
ProcessBuilder writes the result of command java -version to error output Process.errorStream, not Process.inputStream.
Try this code:
val results: List<String> = p.errorStream.bufferedReader().readLines()
Also you may try Koproc lib
It's a small Kotlin lib to run process and execute commands based on Java ProcessBuilder
You may run java process with timeout = 120 sec :
val koproc = "java -jar some.jar".startProcess { timeoutSec = 120 }
koproc.use {
println("Out: ${it.readAvailableOut}")
println("Err: ${it.readAvailableErrOut}")
}
println("Full result after closing: ${koproc.result}")
Run cmd command:
// 'cmd.exe' process will be closed after timeout
val commandResult = "cmd.exe dirs".startCommand { timeoutSec = 1 }
// But you will get the output
println("Out: ${commandResult.out}")
See examples in unit tests: https://github.com/kochetkov-ma/koproc/blob/main/src/test/kotlin/ru/iopump/koproc/ExtensionKtIT.kt

How can I programmatically terminate a running process in the same script that started it?

How do I start processes from a script in a way that also allows me to terminate them?
Basically, I can easily terminate the main script, but terminating the external processes that this main script starts has been the issue. I googled like crazy for Perl 6 solutions. I was just about to post my question and then thought I'd open the question up to solutions in other languages.
Starting external processes is easy with PerlĀ 6:
my $proc = shell("possibly_long_running_command");
shell returns a process object after the process finishes. So, I don't know how to programmatically find out the PID of the running process because the variable $proc isn't even created until the external process finishes. (side note: after it finishes, $proc.pid returns an undefined Any, so it doesn't tell me what PID it used to have.)
Here is some code demonstrating some of my attempts to create a "self destructing" script:
#!/bin/env perl6
say "PID of the main script: $*PID";
# limit run time of this script
Promise.in(10).then( {
say "Took too long! Killing job with PID of $*PID";
shell "kill $*PID"
} );
my $example = shell('echo "PID of bash command: $$"; sleep 20; echo "PID of bash command after sleeping is still $$"');
say "This line is never printed";
This results in the following output which kills the main script, but not the externally created process (see output after the word Terminated):
[prompt]$ ./self_destruct.pl6
PID of the main script: 30432
PID of bash command: 30436
Took too long! Killing job with PID of 30432
Terminated
[prompt]$ my PID after sleeping is still 30436
By the way, the PID of sleep was also different (i.e. 30437) according to top.
I'm also not sure how to make this work with Proc::Async. Unlike the result of shell, the asynchronous process object it creates doesn't have a pid method.
I was originally looking for a Perl 6 solution, but I'm open to solutions in Python, Perl 5, Java, or any language that interacts with the "shell" reasonably well.
For Perl 6, there seems to be the Proc::Async module
Proc::Async allows you to run external commands asynchronously, capturing standard output and error handles, and optionally write to its standard input.
# command with arguments
my $proc = Proc::Async.new('echo', 'foo', 'bar');
# subscribe to new output from out and err handles:
$proc.stdout.tap(-> $v { print "Output: $v" });
$proc.stderr.tap(-> $v { print "Error: $v" });
say "Starting...";
my $promise = $proc.start;
# wait for the external program to terminate
await $promise;
say "Done.";
Method kill:
kill(Proc::Async:D: $signal = "HUP")
Sends a signal to the running program. The signal can be a signal name ("KILL" or "SIGKILL"), an integer (9) or an element of the Signal enum (Signal::SIGKILL).
An example on how to use it:
#!/usr/bin/env perl6
use v6;
say 'Start';
my $proc = Proc::Async.new('sleep', 10);
my $promise= $proc.start;
say 'Process started';
sleep 2;
$proc.kill;
await $promise;
say 'Process killed';
As you can see, $proc has a method to kill the process.
Neither Perl, Perl 6, nor Java, but bash:
timeout 5 bash -c "echo hello; sleep 10; echo goodbye" &
In Java you can create a process like this:
ProcessBuilder processBuilder = new ProcessBuilder("C:\\Path\program.exe", "param1", "param2", "ecc...");
Process process = processBuilder.start(); // start the process
process.waitFor(timeLimit, timeUnit); // This causes the current thread to wait until the process has terminated or the specified time elapses
// when you want to kill the process
if(process.isAlive()) {
process.destroy();
}
Or you can use process.destroyForcibly();, see the Process documentation for more info.
To execute a bash command point to the bash executable and set the command as a parameter.

Ant sshexec task called from gradle doesn't show output

I want to use Apache ant sshexec task in my gradle custom task. The problem is that this task doesn't work (output is not shown in console and sshexec action is not executed). This is how I use it:
configurations {
sshexecAntTask
}
repositories {
mavenCentral()
}
dependencies {
sshexecAntTask 'org.apache.ant:ant-jsch:1.7.0'
}
// ----------------------------------------------------
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files
class MyCustomTask extends DefaultTask {
#TaskAction
def build() {
String command = ""
command = 'cmd.exe /C mdir C:\\aadd'
runSshCommand(command)
}
private void runSshCommand(String command) {
String host = "host"
String username = "username"
String password = "password"
ant.taskdef(name: 'sshexec', classname: 'org.apache.tools.ant.taskdefs.optional.ssh.SSHExec', classpath: project.configurations.sshexecAntTask.asPath)
// this command is not executed; why?
ant.sshexec(host: host, username: username, password: password, command: command, trust: 'true', failonerror: 'true')
}
}
[EDIT]
I've tested sshexec and those are my results:
The command cmd.exe /C echo test > C:\testresult.txt started from ant works correctly and output is returned to file.
The command cmd.exe /C echo test > C:\testresult.txt started from gradle works correctly and output is returned to file. Great!
The command cmd.exe /C echo test started from ant works correctly and output is returned to stdout. !
The command cmd.exe /C echo test started from gradle works correctly but output is not returned to stdout. !
The command cmd.exe /C mkdir C:\\\\Inetpub\\\\ftproot\\\\temp\\\\jakisnowykatalog started from ant works correctly and directory is created (I need to use \\\\ as path separator because \\, \, / doesn't work)
The command cmd.exe /C mkdir C:\\\\Inetpub\\\\ftproot\\\\temp\\\\jakisnowykatalog started from gradle doesn't work and directory is not created.
I should add that I want to connect with windows ssh server (not unix/mac) but I've also tested those commands with mac shh without success. Please help!
[Another edit]
I've created groovy test code which uses jsch library to execute command and it works. I still don't know why ant task doesn't work.
import com.jcraft.jsch.*
import java.util.Properties;
private void jschTest() {
Session session = null
Channel channel = null
try {
JSch jsch = new JSch()
session = jsch.getSession("host", "login", 22)
session.setPassword("password")
Properties config = new Properties()
config.put("StrictHostKeyChecking", "no")
session.setConfig(config)
session.connect()
String command = "cmd.exe /C mkdir C:\\gradledir"
channel = session.openChannel("exec");
((ChannelExec)channel).setCommand(command);
channel.connect()
}
catch (Exception e) {
println e.getMessage()
}
finally {
if (session!=null) {
session.disconnect()
}
if (channel!=null) {
channel.disconnect()
}
}
}
Assuming you declare a task of type MyCustomTask and execute it correctly, I see no reason why the Ant task wouldn't get executed. The problem is more likely elsewhere (e.g. wrong configuration of the Ant task).

execute command from Grails app

I want to perform an svn delete from my Grails app. I tested out both of the following in the Grails console:
"svn delete /usr/share/mydir".execute()
Runtime.getRuntime().exec("svn delete /usr/share/mydir")
In both cases, a instance of java.lang.Process is returned, but the command does not get executed (/usr/share/mydir is not deleted).
This behaviour only happens when the app is running on Linux (Ubuntu). If I run it on Windows, the command does get executed.
Update
Following Tim's advice in the comments, I changed the command so that it captures the process output:
def process = "svn delete /usr/share/mydir".execute()
def out = new StringBuilder()
process.waitForProcessOutput(out, new StringBuilder())
println "$out"
I now see that the reason it's failing is because:
error svn: Can't open file '/usr/share/mydir/.svn/lock': Permission
denied
The below code works fine for me on CentOS.
def scriptCom="/folderlocation/shellscript.sh"
println "[[Running $scriptCom]]"
def proc = scriptCom.execute()
def oneMinute = 60000
proc.waitForOrKill(oneMinute)
if(proc.exitValue()!=0){
println "[[return code: ${proc.exitValue()}]]"
println "[[stderr: ${proc.err.text}]]"
return null
}else{
println "[[stdout:$revisionid]]"
return proc.in.text.readLines()
}

Categories