diff options
author | Roland Grunberg | 2015-11-04 15:15:33 +0000 |
---|---|---|
committer | Roland Grunberg | 2015-11-19 15:49:07 +0000 |
commit | fd718c5f08046458acab7a889f1881cf200aba13 (patch) | |
tree | 4feeb94fd75117c946c3f8a0b345f8639e37959e /containers | |
parent | 9f8f13d4ca80a7f3828dcaf46cab536dbe9a23eb (diff) | |
download | org.eclipse.linuxtools-fd718c5f08046458acab7a889f1881cf200aba13.tar.gz org.eclipse.linuxtools-fd718c5f08046458acab7a889f1881cf200aba13.tar.xz org.eclipse.linuxtools-fd718c5f08046458acab7a889f1881cf200aba13.zip |
Bug 471672: Use a TM Terminal for containers with openStdin and tty.
Change-Id: Ifee8ab5ed99ad2419e3e1346899b5939f5b7f2e6
Reviewed-on: https://git.eclipse.org/r/59955
Tested-by: Hudson CI
Reviewed-by: Roland Grunberg <rgrunber@redhat.com>
Diffstat (limited to 'containers')
6 files changed, 104 insertions, 248 deletions
diff --git a/containers/org.eclipse.linuxtools.docker.core/META-INF/MANIFEST.MF b/containers/org.eclipse.linuxtools.docker.core/META-INF/MANIFEST.MF index 4a97886814..301847808a 100644 --- a/containers/org.eclipse.linuxtools.docker.core/META-INF/MANIFEST.MF +++ b/containers/org.eclipse.linuxtools.docker.core/META-INF/MANIFEST.MF @@ -17,7 +17,8 @@ Require-Bundle: org.eclipse.core.runtime;bundle-version="3.10.0", javax.ws.rs;bundle-version="2.0.1", org.glassfish.jersey.core.jersey-client;bundle-version="2.14.0", org.glassfish.jersey.media.jersey-media-json-jackson;bundle-version="2.14.0", - org.glassfish.jersey.core.jersey-common;bundle-version="2.14.0" + org.glassfish.jersey.core.jersey-common;bundle-version="2.14.0", + org.eclipse.tm.terminal.view.core;bundle-version="4.0.0" Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Bundle-ActivationPolicy: lazy Export-Package: org.eclipse.linuxtools.docker.core, diff --git a/containers/org.eclipse.linuxtools.docker.core/src/org/eclipse/linuxtools/internal/docker/core/DockerConnection.java b/containers/org.eclipse.linuxtools.docker.core/src/org/eclipse/linuxtools/internal/docker/core/DockerConnection.java index 13c244b77c..386a92dec3 100644 --- a/containers/org.eclipse.linuxtools.docker.core/src/org/eclipse/linuxtools/internal/docker/core/DockerConnection.java +++ b/containers/org.eclipse.linuxtools.docker.core/src/org/eclipse/linuxtools/internal/docker/core/DockerConnection.java @@ -63,6 +63,9 @@ import org.eclipse.linuxtools.docker.core.IDockerProgressHandler; import org.eclipse.linuxtools.docker.core.ILogger; import org.eclipse.linuxtools.docker.core.Messages; import org.eclipse.osgi.util.NLS; +import org.eclipse.tm.terminal.view.core.TerminalServiceFactory; +import org.eclipse.tm.terminal.view.core.interfaces.ITerminalService; +import org.eclipse.tm.terminal.view.core.interfaces.constants.ITerminalsConnectorConstants; import com.spotify.docker.client.ContainerNotFoundException; import com.spotify.docker.client.DefaultDockerClient; @@ -1275,7 +1278,7 @@ public class DockerConnection implements IDockerConnection, Closeable { } } - public WritableByteChannel attachCommand(final String id, + public void attachCommand(final String id, final InputStream in, final OutputStream out) throws DockerException { @@ -1285,7 +1288,33 @@ public class DockerConnection implements IDockerConnection, Closeable { AttachParameter.STDIN, AttachParameter.STDOUT, AttachParameter.STDERR, AttachParameter.STREAM, AttachParameter.LOGS); - final boolean isTtyEnabled = getContainerInfo(id).config().tty(); + final IDockerContainerInfo info = getContainerInfo(id); + final boolean isTtyEnabled = info.config().tty(); + final boolean isOpenStdin = info.config().openStdin(); + + if (isTtyEnabled) { + OutputStream tout = noBlockingOutputStream(HttpHijackWorkaround.getOutputStream(pty_stream, getUri())); + InputStream tin = HttpHijackWorkaround.getInputStream(pty_stream); + // org.eclipse.tm.terminal.connector.ssh.controls.SshWizardConfigurationPanel + Map<String, Object> properties = new HashMap<>(); + properties.put(ITerminalsConnectorConstants.PROP_DELEGATE_ID, "org.eclipse.tm.terminal.connector.streams.launcher.streams"); + properties.put(ITerminalsConnectorConstants.PROP_TERMINAL_CONNECTOR_ID, "org.eclipse.tm.terminal.connector.streams.StreamsConnector"); + properties.put(ITerminalsConnectorConstants.PROP_TITLE, info.name()); + properties.put(ITerminalsConnectorConstants.PROP_LOCAL_ECHO, false); + properties.put(ITerminalsConnectorConstants.PROP_FORCE_NEW, true); + properties.put(ITerminalsConnectorConstants.PROP_STREAMS_STDIN, tout); + properties.put(ITerminalsConnectorConstants.PROP_STREAMS_STDOUT, tin); + /* + * The JVM will call finalize() on 'pty_stream' (LogStream) + * since we hold no references to it (although we do hold + * references to one of its heavily nested fields. The + * LogStream overrides finalize() to close the stream being + * used so we must preserve a reference to it. + */ + properties.put("PREVENT_JVM_GC_FINALIZE", pty_stream); + ITerminalService service = TerminalServiceFactory.getService(); + service.openConsole(properties, null); + } // Data from the given input stream // Written to container's STDIN @@ -1312,147 +1341,14 @@ public class DockerConnection implements IDockerConnection, Closeable { } }); - t_in.start(); - // Incoming data from container's STDOUT - // Written to the given output stream - Thread t_out = new Thread(new Runnable() { - @Override - public void run() { - try { - InputStream pty_in = HttpHijackWorkaround - .getInputStream(pty_stream); - while (getContainerInfo(id).state().running()) { - byte[] buff = new byte[1024]; - int n = pty_in.read(buff); - if (n > 0) { - /* - * The container's STDOUT contains initial input - * we sent to its STDIN and the result. eg. > - * echo once < echo once \n $ once - * - * Try to remove this unwanted data from the - * stream. - */ - if (isTtyEnabled) { - int idex = 0; - synchronized (prevCmd) { - /* - * Check if buff contains a prefix of - * prevCmd ignoring differences in - * carriage return (10,13). Save the - * prefix's ending index. - */ - for (int i = 0; i < prevCmd.length; i++) { - if (prevCmd[i] != buff[i] - && (prevCmd[i] != 10 && buff[i] != 13) - && (prevCmd[i] != 13 && buff[i] != 10) - && prevCmd[i] != 0) { - idex = 0; - break; - } else if (prevCmd[i] != 0) { - idex++; - } - } - } - // A prefix exists, remove it - // Do not include the ending NL/CR - if (idex != 0) { - shiftLeft(buff, idex + 1); - } - n = removeTerminalCodes(buff); - } else { - /* - * If not in TTY mode, first 8 bytes are - * header data describing payload which we - * don't need. - */ - shiftLeft(buff, 8); - n = n - 8; - } - out.write(buff, 0, n); - } - } - } catch (Exception e) { - /* - * Temporary workaround for BZ #469717 - * Remove this when we begin using a release with : - * https://github.com/spotify/docker-client/pull/223 - */ - if (e instanceof SocketTimeoutException) { - try { - attachCommand(id, in, out); - } catch (DockerException e1) { - } - } - } - } - }); - - /* - * Our handling of STDOUT for terminals is mandatory, but the - * logging framework can handle catching output very early so use it - * for now. - */ - if (isTtyEnabled) { - t_out.start(); + if (!isTtyEnabled && isOpenStdin) { + t_in.start(); } - - return HttpHijackWorkaround.getOutputStream(pty_stream, getUri()); } catch (Exception e) { throw new DockerException(e.getMessage(), e.getCause()); } } - /* - * Incoming data from container's STDOUT contains terminal codes which the - * Eclipse Console does not support. Either we install/use some terminal - * plugin that does, or we need to remove these. - */ - private static int removeTerminalCodes(final byte[] buff) { - String tmp = new String(buff); - byte[] tmp_buff = tmp.replaceAll("\u001B]0;.*\u0007", "") - .replaceAll("\u001B\\[([0-9]{1,2}(;[0-9]{1,2})?)?[mK]", "") - .replaceAll("\u001B\\[\\?[0-9]{1,4}h\\[", "").getBytes(); - for (int i = 0; i < buff.length; i++) { - if (i >= tmp_buff.length) { - buff[i] = 0; - } else { - buff[i] = tmp_buff[i]; - } - } - return getByteLength(buff); - } - - /* - * Shift contents of buff[idex] .. buff[buff.length-1] to buff[0] .. - * buff[(buff.length-1) - idex] - */ - private static void shiftLeft(byte[] buff, int idex) { - for (int i = 0; i < buff.length; i++) { - if (idex + i < buff.length) { - buff[i] = buff[idex + i]; - } else { - buff[i] = 0; - } - } - } - - /* - * Get the number of non-zero bytes from the beginning of the byte array. - */ - private static int getByteLength(byte[] buff) { - int n; - for (n = 0; n < buff.length; n++) { - if (buff[n] == 0) { - break; - } - } - if ((n == buff.length - 1) && buff[buff.length - 1] != 0) { - return buff.length; - } - return n; - } - @Override public String getTcpCertPath() { return tcpCertPath; @@ -1488,4 +1384,33 @@ public class DockerConnection implements IDockerConnection, Closeable { return name; } + public static OutputStream noBlockingOutputStream(final WritableByteChannel out) { + return new OutputStream() { + + @Override + public synchronized void write(int i) throws IOException { + byte b[] = new byte[1]; + b[0] = (byte) i; + write(b); + } + + @Override + public synchronized void write(byte[] b, int off, int len) + throws IOException { + if (len == 0) { + return; + } + ByteBuffer buff = ByteBuffer.wrap(b, off, len); + while (buff.remaining() > 0) { + out.write(buff); + } + } + + @Override + public void close() throws IOException { + out.close(); + } + }; + } + } diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/RunConsole.java b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/RunConsole.java index 37a76cac2c..2542eab450 100644 --- a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/RunConsole.java +++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/RunConsole.java @@ -14,30 +14,19 @@ package org.eclipse.linuxtools.internal.docker.ui; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.nio.ByteBuffer; -import java.nio.channels.WritableByteChannel; -import org.eclipse.jface.action.IToolBarManager; import org.eclipse.linuxtools.docker.core.IDockerConnection; import org.eclipse.linuxtools.docker.core.IDockerContainer; import org.eclipse.linuxtools.internal.docker.core.DockerConnection; import org.eclipse.linuxtools.internal.docker.ui.views.DVMessages; -import org.eclipse.swt.custom.StyledText; -import org.eclipse.swt.events.KeyEvent; -import org.eclipse.swt.events.KeyListener; import org.eclipse.ui.console.ConsolePlugin; import org.eclipse.ui.console.IConsole; -import org.eclipse.ui.console.IConsoleView; import org.eclipse.ui.console.IOConsole; -import org.eclipse.ui.console.TextConsole; -import org.eclipse.ui.internal.console.IOConsolePage; -import org.eclipse.ui.part.IPageBookViewPage; /** * RpmConsole is used to output rpm/rpmbuild output. * */ -@SuppressWarnings("restriction") public class RunConsole extends IOConsole { /** Id of this console. */ @@ -50,7 +39,6 @@ public class RunConsole extends IOConsole { private OutputStream outputStream; private boolean attached = false; - private final WritableByteChannel[] ptyOutRef = new WritableByteChannel[1]; /** * Returns a reference to the console that is for the given container id. If @@ -135,11 +123,6 @@ public class RunConsole extends IOConsole { return ret; } - @Override - public IPageBookViewPage createPage(IConsoleView view) { - return new RunConsolePage(this, view); - } - /** * Set the title of the RunConsole * @@ -170,11 +153,7 @@ public class RunConsole extends IOConsole { .running()) { Thread.sleep(1000); } - WritableByteChannel pty_out = conn - .attachCommand(containerId, in, out); - if (conn.getContainerInfo(containerId).config().tty()) { - ptyOutRef[0] = pty_out; - } + conn.attachCommand(containerId, in, out); } } catch (Exception e) { } @@ -184,6 +163,27 @@ public class RunConsole extends IOConsole { attached = true; } + public static void attachToTerminal (final IDockerConnection connection, final String containerId) { + Thread t = new Thread(new Runnable() { + @Override + public void run() { + try { + DockerConnection conn = (DockerConnection) connection; + if (conn.getContainerInfo(containerId).config() + .openStdin()) { + while (!conn.getContainerInfo(containerId).state() + .running()) { + Thread.sleep(1000); + } + conn.attachCommand(containerId, null, null); + } + } catch (Exception e) { + } + } + }); + t.start(); + } + public void attachToConsole(final IDockerConnection connection, String containerId) { this.containerId = containerId; @@ -263,87 +263,4 @@ public class RunConsole extends IOConsole { this.containerId = containerId; this.id = id; } - - /* - * Custom Page used to add our own set of actions. - */ - private class RunConsolePage extends IOConsolePage { - - public RunConsolePage(TextConsole console, IConsoleView view) { - super(console, view); - } - - @Override - protected void configureToolBar(IToolBarManager mgr) { - super.configureToolBar(mgr); - - if (getControl() != null && getControl() instanceof StyledText) { - StyledText styledText = (StyledText) getControl(); - styledText.addKeyListener(new TTYKeyListener()); - } - } - - } - - /* - * Listener to support sending certain key sequences - */ - private class TTYKeyListener implements KeyListener { - private boolean isCtrlOn; - - private final int CTRL_CODE = 262144; - private final int C_CODE = 'c'; - private final int TAB_CODE = 9; - - public TTYKeyListener() { - this.isCtrlOn = false; - } - - @Override - public void keyReleased(KeyEvent e) { - if (ptyOutRef[0] != null && ptyOutRef[0].isOpen()) { - WritableByteChannel pty_out = ptyOutRef[0]; - try { - switch (e.keyCode) { - case CTRL_CODE: - isCtrlOn = false; - break; - case TAB_CODE: - pty_out.write( - ByteBuffer.wrap(new byte[] { 9, 9 }, 0, 2)); - break; - } - } catch (IOException e1) { - } - } - } - - @Override - public void keyPressed(KeyEvent e) { - if (ptyOutRef[0] != null && ptyOutRef[0].isOpen()) { - WritableByteChannel pty_out = ptyOutRef[0]; - try { - switch (e.keyCode) { - /* - * TODO : These values are configurable, so we should - * start using 'stty -a' to know what they really are. - */ - case C_CODE: - // ETX (End Of Text) (3) is usually the interrupt - // signal. - if (isCtrlOn) { - pty_out.write( - ByteBuffer.wrap(new byte[] { 3 }, 0, 1)); - } - break; - case CTRL_CODE: - isCtrlOn = true; - break; - } - } catch (IOException e1) { - } - } - } - } - } diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/commands/CommandUtils.java b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/commands/CommandUtils.java index 7bc47a087b..7077d8e729 100644 --- a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/commands/CommandUtils.java +++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/commands/CommandUtils.java @@ -193,8 +193,13 @@ public class CommandUtils { * @return the {@link RunConsole} or {@code null} */ public static RunConsole getRunConsole(final IDockerConnection connection, final IDockerContainer container) { + if (connection.getContainerInfo(container.id()).config().tty()) { + RunConsole.attachToTerminal(connection, container.id()); + return null; + } final boolean autoLogOnStart = Activator.getDefault().getPreferenceStore() .getBoolean(PreferenceConstants.AUTOLOG_ON_START); + // if we are auto-logging, grab the // console for the container id and get // its stream. diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/commands/DisplayContainerLogCommandHandler.java b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/commands/DisplayContainerLogCommandHandler.java index 0cfb4179dc..64df9e4565 100644 --- a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/commands/DisplayContainerLogCommandHandler.java +++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/commands/DisplayContainerLogCommandHandler.java @@ -46,12 +46,17 @@ public class DisplayContainerLogCommandHandler extends AbstractHandler { final IDockerContainer container = selectedContainers.get(0); final String id = container.id(); final String name = container.name(); + + if (connection.getContainerInfo(id).config().tty()) { + RunConsole.attachToTerminal(connection, id); + return null; + } try { final RunConsole rc = RunConsole.findConsole(id); - if (!rc.isAttached()) { - rc.attachToConsole(connection); - } if (rc != null) { + if (!rc.isAttached()) { + rc.attachToConsole(connection); + } Display.getDefault().syncExec(new Runnable() { @Override diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/commands/RunImageCommandHandler.java b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/commands/RunImageCommandHandler.java index 4c8530869c..f4c151caa5 100644 --- a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/commands/RunImageCommandHandler.java +++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/commands/RunImageCommandHandler.java @@ -134,9 +134,12 @@ public class RunImageCommandHandler extends AbstractHandler { if (console != null) { // if we are auto-logging, show the console console.showConsole(); + ((DockerConnection) connection).startContainer( + containerId, console.getOutputStream()); + } else { + ((DockerConnection) connection) + .startContainer(containerId, null); } - ((DockerConnection) connection).startContainer(containerId, - console.getOutputStream()); startContainerMonitor.done(); } catch (final DockerException | InterruptedException e) { Display.getDefault().syncExec(new Runnable() { |