diff --git a/nanocloud/src/test/java/org/gridkit/nanocloud/ApacheSSHD_RemoteNodeFeatureTest.java b/nanocloud/src/test/java/org/gridkit/nanocloud/ApacheSSHD_RemoteNodeFeatureTest.java index 88dcb04..5ad4cc3 100644 --- a/nanocloud/src/test/java/org/gridkit/nanocloud/ApacheSSHD_RemoteNodeFeatureTest.java +++ b/nanocloud/src/test/java/org/gridkit/nanocloud/ApacheSSHD_RemoteNodeFeatureTest.java @@ -231,4 +231,15 @@ public void verify_jvm_agent_with_options() throws Exception { public void verify_jvm_agent_multiple_agents() throws Exception { super.verify_jvm_agent_multiple_agents(); } + @Test + @Override + public void verify_exit_code_is_printed_to_logs() throws Exception { + super.verify_exit_code_is_printed_to_logs(); + } + + @Test + @Override + public void verify_exit_code_is_reported() { + super.verify_exit_code_is_reported(); + } } diff --git a/nanocloud/src/test/java/org/gridkit/nanocloud/CentOS_RemoteNodeFeatureTest.java b/nanocloud/src/test/java/org/gridkit/nanocloud/CentOS_RemoteNodeFeatureTest.java index a2bb8fb..f97a023 100644 --- a/nanocloud/src/test/java/org/gridkit/nanocloud/CentOS_RemoteNodeFeatureTest.java +++ b/nanocloud/src/test/java/org/gridkit/nanocloud/CentOS_RemoteNodeFeatureTest.java @@ -201,4 +201,10 @@ public void verify_jvm_agent_multiple_agents() throws Exception { public void verify_exit_code_is_reported() { super.verify_exit_code_is_reported(); } + + @Test + @Override + public void verify_exit_code_is_printed_to_logs() throws Exception { + super.verify_exit_code_is_printed_to_logs(); + } } diff --git a/nanocloud/src/test/java/org/gridkit/nanocloud/FreeBSD_RemoteNodeFeatureTest.java b/nanocloud/src/test/java/org/gridkit/nanocloud/FreeBSD_RemoteNodeFeatureTest.java index 9644b3f..4aef647 100644 --- a/nanocloud/src/test/java/org/gridkit/nanocloud/FreeBSD_RemoteNodeFeatureTest.java +++ b/nanocloud/src/test/java/org/gridkit/nanocloud/FreeBSD_RemoteNodeFeatureTest.java @@ -196,6 +196,12 @@ public void verify_jvm_agent_multiple_agents() throws Exception { super.verify_jvm_agent_multiple_agents(); } + @Test + @Override + public void verify_exit_code_is_printed_to_logs() throws Exception { + super.verify_exit_code_is_printed_to_logs(); + } + @Test @Override public void verify_exit_code_is_reported() { diff --git a/nanocloud/src/test/java/org/gridkit/nanocloud/LocalNodeFeatureTest.java b/nanocloud/src/test/java/org/gridkit/nanocloud/LocalNodeFeatureTest.java index 87a04a8..6d39e68 100644 --- a/nanocloud/src/test/java/org/gridkit/nanocloud/LocalNodeFeatureTest.java +++ b/nanocloud/src/test/java/org/gridkit/nanocloud/LocalNodeFeatureTest.java @@ -160,6 +160,12 @@ public void verify_slave_working_dir() throws IOException { super.verify_slave_working_dir(); } + @Test + @Override + public void verify_exit_code_is_printed_to_logs() throws Exception { + super.verify_exit_code_is_printed_to_logs(); + } + @Test public void verify_jvm_agent() throws Exception { super.verify_jvm_agent(); diff --git a/nanocloud/src/test/java/org/gridkit/nanocloud/ViNodeFeatureTest.java b/nanocloud/src/test/java/org/gridkit/nanocloud/ViNodeFeatureTest.java index 566b331..e2ade93 100755 --- a/nanocloud/src/test/java/org/gridkit/nanocloud/ViNodeFeatureTest.java +++ b/nanocloud/src/test/java/org/gridkit/nanocloud/ViNodeFeatureTest.java @@ -697,6 +697,31 @@ public Void call() throws Exception { }); } + public void verify_exit_code_is_printed_to_logs() throws Exception { + ViNode node = cloud.node(testName.getMethodName()); + StringWriter writer = new StringWriter(); + node.x(VX.CONSOLE).bindOut(writer); + node.exec(new Runnable() { + @Override + public void run() { + new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.exit(42); + } + }).start(); + } + }); + + Thread.sleep(2000); + assertThat(writer.toString()).contains("Process exited with code 42"); + } + public void verify_exit_code_is_reported() { ViNode node = cloud.node(testName.getMethodName()); @@ -736,7 +761,7 @@ public void run() { assertThat(writer.toString()).contains("Terminated, exitCode=42"); } } - + private static String readMarkerFromResources() throws IOException { URL url = IsolateNodeFeatureTest.class.getResource("/marker.txt"); Assert.assertNotNull(url); diff --git a/telecontrol-ssh/src/main/java/org/gridkit/vicluster/telecontrol/ssh/TunnellerJvmReplicator.java b/telecontrol-ssh/src/main/java/org/gridkit/vicluster/telecontrol/ssh/TunnellerJvmReplicator.java index 01c5024..916570d 100644 --- a/telecontrol-ssh/src/main/java/org/gridkit/vicluster/telecontrol/ssh/TunnellerJvmReplicator.java +++ b/telecontrol-ssh/src/main/java/org/gridkit/vicluster/telecontrol/ssh/TunnellerJvmReplicator.java @@ -29,78 +29,78 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.jar.Attributes; -import java.util.jar.Manifest; - -import com.jcraft.jsch.ChannelExec; -import com.jcraft.jsch.JSchException; -import com.jcraft.jsch.Session; -import org.gridkit.util.concurrent.AdvancedExecutor; -import org.gridkit.util.concurrent.FutureBox; -import org.gridkit.util.concurrent.FutureEx; -import org.gridkit.vicluster.telecontrol.BackgroundStreamDumper; -import org.gridkit.vicluster.telecontrol.Classpath; -import org.gridkit.vicluster.telecontrol.ClasspathUtils; -import org.gridkit.vicluster.telecontrol.ExecCommand; -import org.gridkit.vicluster.telecontrol.FileBlob; -import org.gridkit.vicluster.telecontrol.JvmConfig; -import org.gridkit.vicluster.telecontrol.ManagedProcess; -import org.gridkit.vicluster.telecontrol.StreamCopyService; -import org.gridkit.vicluster.telecontrol.bootstraper.Bootstraper; -import org.gridkit.vicluster.telecontrol.bootstraper.Tunneller; -import org.gridkit.vicluster.telecontrol.bootstraper.TunnellerConnection; -import org.gridkit.vicluster.telecontrol.bootstraper.TunnellerConnection.ExecHandler; -import org.gridkit.vicluster.telecontrol.bootstraper.TunnellerConnection.SocketHandler; -import org.gridkit.zerormi.DuplexStream; -import org.gridkit.zerormi.NamedStreamPair; -import org.gridkit.zerormi.hub.LegacySpore; -import org.gridkit.zerormi.hub.MasterHub; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; +import org.gridkit.util.concurrent.AdvancedExecutor; +import org.gridkit.util.concurrent.FutureBox; +import org.gridkit.util.concurrent.FutureEx; +import org.gridkit.vicluster.telecontrol.BackgroundStreamDumper; +import org.gridkit.vicluster.telecontrol.Classpath; +import org.gridkit.vicluster.telecontrol.ClasspathUtils; +import org.gridkit.vicluster.telecontrol.ExecCommand; +import org.gridkit.vicluster.telecontrol.FileBlob; +import org.gridkit.vicluster.telecontrol.JvmConfig; +import org.gridkit.vicluster.telecontrol.ManagedProcess; +import org.gridkit.vicluster.telecontrol.StreamCopyService; +import org.gridkit.vicluster.telecontrol.bootstraper.Bootstraper; +import org.gridkit.vicluster.telecontrol.bootstraper.Tunneller; +import org.gridkit.vicluster.telecontrol.bootstraper.TunnellerConnection; +import org.gridkit.vicluster.telecontrol.bootstraper.TunnellerConnection.ExecHandler; +import org.gridkit.vicluster.telecontrol.bootstraper.TunnellerConnection.SocketHandler; +import org.gridkit.zerormi.DuplexStream; +import org.gridkit.zerormi.NamedStreamPair; +import org.gridkit.zerormi.hub.LegacySpore; +import org.gridkit.zerormi.hub.MasterHub; import org.gridkit.zerormi.hub.RemotingHub; import org.gridkit.zerormi.hub.RemotingHub.SessionEventListener; import org.gridkit.zerormi.zlog.LogLevel; -import org.gridkit.zerormi.zlog.ZLogFactory; -import org.gridkit.zerormi.zlog.ZLogger; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class TunnellerJvmReplicator implements RemoteJmvReplicator { - - private static final long DEFAULT_CONN_TIMEOUT = 5000; - - private final StreamCopyService streamCopyService; - - private SshRemotingConfig rconfig = new SshRemotingConfig(); - private boolean initialized; - private boolean destroyed; - - private Session session; - private RemotingHub hub; - private TunnellerConnection control; - - private RemoteFileCache jarCache; - private String tunnellerJarPath; - - private String tunnelHost; - private int tunnelPort; - private long connectTimeoutMS = DEFAULT_CONN_TIMEOUT; - - private ZLogger logger; - - public TunnellerJvmReplicator(StreamCopyService streamCopyService) { - this.streamCopyService = streamCopyService; - } - - public TunnellerJvmReplicator(StreamCopyService streamCopyService, ZLogger logger) { - this(streamCopyService); - this.logger = logger; - } - - @Override - public synchronized void configure(Map nodeConfig) { - rconfig.configure(nodeConfig); - rconfig.validate(); - } - +import org.gridkit.zerormi.zlog.ZLogFactory; +import org.gridkit.zerormi.zlog.ZLogger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TunnellerJvmReplicator implements RemoteJmvReplicator { + + private static final long DEFAULT_CONN_TIMEOUT = 5000; + + private final StreamCopyService streamCopyService; + + private SshRemotingConfig rconfig = new SshRemotingConfig(); + private boolean initialized; + private boolean destroyed; + + private Session session; + private RemotingHub hub; + private TunnellerConnection control; + + private RemoteFileCache jarCache; + private String tunnellerJarPath; + + private String tunnelHost; + private int tunnelPort; + private long connectTimeoutMS = DEFAULT_CONN_TIMEOUT; + + private ZLogger logger; + + public TunnellerJvmReplicator(StreamCopyService streamCopyService) { + this.streamCopyService = streamCopyService; + } + + public TunnellerJvmReplicator(StreamCopyService streamCopyService, ZLogger logger) { + this(streamCopyService); + this.logger = logger; + } + + @Override + public synchronized void configure(Map nodeConfig) { + rconfig.configure(nodeConfig); + rconfig.validate(); + } + @Override public synchronized String getFingerPrint() { return rconfig.getFingerPrint(); @@ -300,25 +300,25 @@ private void startTunneler() throws JSchException, IOException { verifyJavaVersion(); ChannelExec exec = (ChannelExec) session.openChannel("exec"); - - String cmd = rconfig.getJavaExec() + " -Xms32m -Xmx32m -jar " + tunnellerJarPath; - exec.setCommand(cmd); - - // use std out for binary communication - InputStream cin = exec.getInputStream(); - OutputStream cout = exec.getOutputStream(); - // use std err for diagnostic output - OutputStream tunnel = new LoggerPrintStream(logger.get("console", LogLevel.WARN)); - streamCopyService.link(exec.getExtInputStream(), tunnel, false); - - // unfortunately Pty will merge out and err, so it should be disabled - exec.setPty(false); - exec.connect(); - - PrintStream diagLog = new LoggerPrintStream(logger.get("console", LogLevel.WARN)); - - try { - control = new TunnellerConnection(rconfig.getHost(), cin, cout, diagLog, connectTimeoutMS, TimeUnit.MILLISECONDS); + + String cmd = rconfig.getJavaExec() + " -Xms32m -Xmx32m -jar " + tunnellerJarPath; + exec.setCommand(cmd); + + // use std out for binary communication + InputStream cin = exec.getInputStream(); + OutputStream cout = exec.getOutputStream(); + // use std err for diagnostic output + OutputStream tunnel = new LoggerPrintStream(logger.get("console", LogLevel.WARN)); + streamCopyService.link(exec.getExtInputStream(), tunnel, false); + + // unfortunately Pty will merge out and err, so it should be disabled + exec.setPty(false); + exec.connect(); + + PrintStream diagLog = new LoggerPrintStream(logger.get("console", LogLevel.WARN)); + + try { + control = new TunnellerConnection(rconfig.getHost(), cin, cout, diagLog, connectTimeoutMS, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { killAndDrop(exec); throw new IOException("Connection aborted due to thread interrupt"); @@ -500,54 +500,54 @@ public void consoleFlush() { // do nothing } - @Override - public FutureEx getExitCodeFuture() { - // FIXME getExitCodeFuture for remote process - return new FutureBox(); - } - - @Override - public void bindStdIn(InputStream is) { - if (is != null) { - streamCopyService.link(is, getOutputStream()); - } - else { - try { - getOutputStream().close(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - } - - @Override - public void bindStdOut(OutputStream os) { - if (os != null) { - streamCopyService.link(getInputStream(), os); - } - else { - try { - getInputStream().close(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - } - - @Override - public void bindStdErr(OutputStream os) { - if (os != null) { - streamCopyService.link(getErrorStream(), os); - } - else { - try { - getErrorStream().close(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - } + @Override + public FutureEx getExitCodeFuture() { + // FIXME getExitCodeFuture for remote process + return new FutureBox(); + } + + @Override + public void bindStdIn(InputStream is) { + if (is != null) { + streamCopyService.link(is, getOutputStream()); + } + else { + try { + getOutputStream().close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + @Override + public void bindStdOut(OutputStream os) { + if (os != null) { + streamCopyService.link(getInputStream(), os); + } + else { + try { + getInputStream().close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + } + + @Override + public void bindStdErr(OutputStream os) { + if (os != null) { + streamCopyService.link(getErrorStream(), os); + } + else { + try { + getErrorStream().close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } @Override public void closed() { @@ -595,6 +595,11 @@ public void started(OutputStream stdIn, InputStream stdOut, InputStream stdErr) @Override public void finished(int exitCode) { this.exitCode.setData(exitCode); + try { + stdIn.write(("\nProcess exited with code "+exitCode+"\n").getBytes()); + } catch (IOException e) { + e.printStackTrace(); + } } @Override diff --git a/vicluster-core/src/main/java/org/gridkit/nanocloud/telecontrol/ProcessSporeLauncher.java b/vicluster-core/src/main/java/org/gridkit/nanocloud/telecontrol/ProcessSporeLauncher.java index 358197c..55dbc80 100644 --- a/vicluster-core/src/main/java/org/gridkit/nanocloud/telecontrol/ProcessSporeLauncher.java +++ b/vicluster-core/src/main/java/org/gridkit/nanocloud/telecontrol/ProcessSporeLauncher.java @@ -470,6 +470,7 @@ public void finished(int exitCode) { ProcessStreams ps = fget(procStreams); if (ps != null) { try { + ps.stdOut.write(("\nProcess exited with code "+exitCode+"\n").getBytes()); ps.eofOut.flushAndClose(); ps.eofErr.flushAndClose(); if (ps.stdOut.getOutput() == null) {