Fix missing output when running Container commands
- change ContainerCommandProcess destroy() to delay before closing
the piped std streams to allow output to complete
- also flush the piped streams at the end of the attach thread and
stop the logging thread after waiting for container completion
- add getImageByTag() method to DockerConnection
- when copying volumes from image in ContainerLauncher,
keep track of which directories are copied and don't bother if
they already recorded in a static map for the connection and
image which we serialize to a file upon exit
- add new getCopiedVolumes() method to ContainerLauncher to get
the volumes that have been copied for an image
- also keep track of the image id and store in a file so that
if the image id changes for the repo tag, assume that previous
copying is null and void and copy all headers again
- don't cause an error if an exception occurs copying as we might
be attempting to copy a local include path that doesn't exist
in the image so just ignore it and move to next path to copy
- fix uses of Objects.toStringHelper to instead use
MoreObjects.toStringHelper (ImageSearchResultV1,
ImageSearchResultV2, and RepositoryTag classes)
Change-Id: Ib01ff56d92c50fb7cebf32ce0d277f32ee825fba
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 bfa46c6..4d6febe 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
@@ -626,10 +626,11 @@
int delayTime = 100;
do {
+ outputStream.write("Sleeping".getBytes());
Thread.sleep(delayTime);
// Second time in loop and following, pause a second to
// allow other threads to do meaningful work
- delayTime = 1000;
+ delayTime = 500;
while (stream.hasNext()) {
ByteBuffer b = stream.next().content();
byte[] bytes = new byte[b.remaining()];
@@ -1094,6 +1095,17 @@
return false;
}
+ public IDockerImage getImageByTag(final String tag) {
+ for (IDockerImage image : getImages()) {
+ for (String repoTag : image.repoTags()) {
+ if (repoTag.equals(tag)) {
+ return image;
+ }
+ }
+ }
+ return null;
+ }
+
@Override
public IDockerProgressHandler getDefaultBuildImageProgressHandler(
String image, int lines) {
diff --git a/containers/org.eclipse.linuxtools.docker.core/src/org/eclipse/linuxtools/internal/docker/core/ImageSearchResultV1.java b/containers/org.eclipse.linuxtools.docker.core/src/org/eclipse/linuxtools/internal/docker/core/ImageSearchResultV1.java
index 08a913b..78610d8 100644
--- a/containers/org.eclipse.linuxtools.docker.core/src/org/eclipse/linuxtools/internal/docker/core/ImageSearchResultV1.java
+++ b/containers/org.eclipse.linuxtools.docker.core/src/org/eclipse/linuxtools/internal/docker/core/ImageSearchResultV1.java
@@ -18,7 +18,7 @@
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonProperty;
-import com.google.common.base.Objects;
+import com.google.common.base.MoreObjects;
import com.spotify.docker.client.messages.ImageSearchResult;
/**
@@ -99,7 +99,7 @@
@Override
public String toString() {
- return Objects.toStringHelper(this)
+ return MoreObjects.toStringHelper(this)
.add("num_pages", getTotalPages()) //$NON-NLS-1$
.add("num_results", getTotalResults()) //$NON-NLS-1$
.add("page_size", getPageSize()) //$NON-NLS-1$
diff --git a/containers/org.eclipse.linuxtools.docker.core/src/org/eclipse/linuxtools/internal/docker/core/ImageSearchResultV2.java b/containers/org.eclipse.linuxtools.docker.core/src/org/eclipse/linuxtools/internal/docker/core/ImageSearchResultV2.java
index 11678f1..dc2a11d 100644
--- a/containers/org.eclipse.linuxtools.docker.core/src/org/eclipse/linuxtools/internal/docker/core/ImageSearchResultV2.java
+++ b/containers/org.eclipse.linuxtools.docker.core/src/org/eclipse/linuxtools/internal/docker/core/ImageSearchResultV2.java
@@ -18,7 +18,7 @@
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonProperty;
-import com.google.common.base.Objects;
+import com.google.common.base.MoreObjects;
import com.spotify.docker.client.messages.ImageSearchResult;
/**
@@ -43,7 +43,7 @@
@Override
public String toString() {
- return Objects.toStringHelper(this)
+ return MoreObjects.toStringHelper(this)
.add("results", getRepositories()).toString(); //$NON-NLS-1$
}
diff --git a/containers/org.eclipse.linuxtools.docker.core/src/org/eclipse/linuxtools/internal/docker/core/RepositoryTag.java b/containers/org.eclipse.linuxtools.docker.core/src/org/eclipse/linuxtools/internal/docker/core/RepositoryTag.java
index 9cc05c8..cc27ef6 100644
--- a/containers/org.eclipse.linuxtools.docker.core/src/org/eclipse/linuxtools/internal/docker/core/RepositoryTag.java
+++ b/containers/org.eclipse.linuxtools.docker.core/src/org/eclipse/linuxtools/internal/docker/core/RepositoryTag.java
@@ -17,7 +17,7 @@
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonProperty;
-import com.google.common.base.Objects;
+import com.google.common.base.MoreObjects;
/**
* Repository tag retrieved from Docker Registry version 0.6.3
@@ -84,7 +84,7 @@
@Override
public String toString() {
- return Objects.toStringHelper(this).add("name", getName()) //$NON-NLS-1$
+ return MoreObjects.toStringHelper(this).add("name", getName()) //$NON-NLS-1$
.add("layer", getLayer()).toString(); //$NON-NLS-1$
}
diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/docker/ui/launch/ContainerLauncher.java b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/docker/ui/launch/ContainerLauncher.java
index e86bc4e..ae97049 100644
--- a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/docker/ui/launch/ContainerLauncher.java
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/docker/ui/launch/ContainerLauncher.java
@@ -10,10 +10,18 @@
*******************************************************************************/
package org.eclipse.linuxtools.docker.ui.launch;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.util.ArrayList;
@@ -34,6 +42,7 @@
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.dialogs.MessageDialog;
@@ -67,10 +76,15 @@
private static final String ERROR_NO_CONNECTIONS = "ContainerNoConnections.msg"; //$NON-NLS-1$
private static final String ERROR_NO_CONNECTION_WITH_URI = "ContainerNoConnectionWithURI.msg"; //$NON-NLS-1$
+ private static final String DIRFILE_NAME = "copiedVolumes"; //$NON-NLS-1$
+
private static RunConsole console;
private static Map<IProject, ID> fidMap = new HashMap<>();
+ private static Object lockObject = new Object();
+ private static Map<String, Map<String, Set<String>>> copiedVolumesMap = null;
+
private class CopyVolumesJob extends Job {
private static final String COPY_VOLUMES_JOB_TITLE = "ContainerLaunch.copyVolumesJob.title"; //$NON-NLS-1$
@@ -140,12 +154,14 @@
private static final String COPY_VOLUMES_FROM_JOB_TITLE = "ContainerLaunch.copyVolumesFromJob.title"; //$NON-NLS-1$
private static final String COPY_VOLUMES_FROM_DESC = "ContainerLaunch.copyVolumesFromJob.desc"; //$NON-NLS-1$
private static final String COPY_VOLUMES_FROM_TASK = "ContainerLaunch.copyVolumesFromJob.task"; //$NON-NLS-1$
- private static final String ERROR_COPYING_VOLUME = "ContainerLaunch.copyVolumesFromJob.error"; //$NON-NLS-1$
+ // private static final String ERROR_COPYING_VOLUME =
+ // "ContainerLaunch.copyVolumesFromJob.error"; //$NON-NLS-1$
private final List<String> volumes;
private final IDockerConnection connection;
private final String image;
private final IPath target;
+ private Set<String> dirList;
public CopyVolumesFromImageJob(
IDockerConnection connection,
@@ -155,17 +171,61 @@
this.connection = connection;
this.image = image;
this.target = target;
+ Map<String, Set<String>> dirMap = null;
+ synchronized (lockObject) {
+ String uri = connection.getUri();
+ dirMap = copiedVolumesMap.get(uri);
+ if (dirMap == null) {
+ dirMap = new HashMap<>();
+ copiedVolumesMap.put(uri, dirMap);
+ }
+ dirList = dirMap.get(image);
+ if (dirList == null) {
+ dirList = new HashSet<>();
+ dirMap.put(image, dirList);
+ }
+ }
}
@Override
protected IStatus run(final IProgressMonitor monitor) {
monitor.beginTask(
- Messages.getString(COPY_VOLUMES_FROM_DESC),
+ Messages.getFormattedString(COPY_VOLUMES_FROM_DESC, image),
volumes.size());
String containerId = null;
try {
+ IDockerImage dockerImage = ((DockerConnection) connection)
+ .getImageByTag(image);
+ IPath imageFilePath = target.append(".IMAGE_ID");
+ File imageFile = imageFilePath.toFile();
+ boolean needImageIdFile = !imageFile.exists();
+ if (!needImageIdFile) {
+ try (FileReader reader = new FileReader(imageFile);
+ BufferedReader bufferReader = new BufferedReader(
+ reader);) {
+ String imageId = bufferReader.readLine();
+ if (!dockerImage.id().equals(imageId)) {
+ // if image id has changed...all bets are off
+ // and we must reload all directories
+ dirList.clear();
+ needImageIdFile = true;
+ }
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ if (needImageIdFile) {
+ try (FileWriter writer = new FileWriter(imageFile);
+ BufferedWriter bufferedWriter = new BufferedWriter(
+ writer);) {
+ bufferedWriter.write(dockerImage.id());
+ bufferedWriter.newLine();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
DockerContainerConfig.Builder builder = new DockerContainerConfig.Builder()
- .cmd("/bin/sh").image(image);
+ .cmd("/bin/sh").image(image); //$NON-NLS-1$
IDockerContainerConfig config = builder.build();
DockerHostConfig.Builder hostBuilder = new DockerHostConfig.Builder();
IDockerHostConfig hostConfig = hostBuilder.build();
@@ -176,7 +236,12 @@
monitor.done();
return Status.CANCEL_STATUS;
}
- if (volume.contains("${ProjName}")) {
+ if (volume.contains("${ProjName}")) { //$NON-NLS-1$
+ monitor.worked(1);
+ continue;
+ }
+ if (dirList.contains(volume)) {
+ monitor.worked(1);
continue;
}
try {
@@ -188,6 +253,10 @@
InputStream in = ((DockerConnection) connection)
.copyContainer(containerId, volume);
+ synchronized (lockObject) {
+ dirList.add(volume);
+ }
+
/*
* The input stream from copyContainer might be
* incomplete or non-blocking so we should wrap it in a
@@ -231,18 +300,18 @@
}
k.close();
} catch (final DockerException e) {
- Display.getDefault()
- .syncExec(() -> MessageDialog.openError(
- PlatformUI.getWorkbench()
- .getActiveWorkbenchWindow()
- .getShell(),
- Messages.getFormattedString(
- ERROR_COPYING_VOLUME,
- new String[] { volume, target
- .toPortableString() }),
- e.getCause() != null
- ? e.getCause().getMessage()
- : e.getMessage()));
+ // Display.getDefault()
+ // .syncExec(() -> MessageDialog.openError(
+ // PlatformUI.getWorkbench()
+ // .getActiveWorkbenchWindow()
+ // .getShell(),
+ // Messages.getFormattedString(
+ // ERROR_COPYING_VOLUME,
+ // new String[] { volume, target
+ // .toPortableString() }),
+ // e.getCause() != null
+ // ? e.getCause().getMessage()
+ // : e.getMessage()));
}
}
} catch (InterruptedException e) {
@@ -282,6 +351,58 @@
}
}
+ @Override
+ protected void finalize() throws Throwable {
+ synchronized (lockObject) {
+ if (copiedVolumesMap != null) {
+ IPath pluginPath = Platform.getStateLocation(
+ Platform.getBundle(Activator.PLUGIN_ID));
+ IPath path = pluginPath.append(DIRFILE_NAME);
+
+ File dirFile = path.toFile();
+ FileOutputStream f = new FileOutputStream(dirFile);
+ try (ObjectOutputStream oos = new ObjectOutputStream(f)) {
+ oos.writeObject(copiedVolumesMap);
+ }
+ }
+ }
+ super.finalize();
+ }
+
+ public ContainerLauncher() {
+ initialize();
+ }
+
+ @SuppressWarnings("unchecked")
+ private void initialize() {
+ synchronized (lockObject) {
+ if (copiedVolumesMap == null) {
+ IPath pluginPath = Platform.getStateLocation(
+ Platform.getBundle(Activator.PLUGIN_ID));
+ IPath path = pluginPath.append(DIRFILE_NAME);
+
+ File dirFile = path.toFile();
+ if (dirFile.exists()) {
+ try (FileInputStream f = new FileInputStream(dirFile)) {
+ try (ObjectInputStream ois = new ObjectInputStream(f)) {
+ copiedVolumesMap = (Map<String, Map<String, Set<String>>>) ois
+ .readObject();
+ } catch (ClassNotFoundException
+ | FileNotFoundException e) {
+ // should never happen
+ e.printStackTrace();
+ }
+ } catch (IOException e) {
+ // will handle this below
+ }
+ }
+ }
+ if (copiedVolumesMap == null) {
+ copiedVolumesMap = new HashMap<>();
+ }
+ }
+ }
+
/**
* Perform a launch of a command in a container.
*
@@ -1063,4 +1184,23 @@
}
+ /**
+ * @since 3.0
+ */
+ public Set<String> getCopiedVolumes(String connectionName,
+ String imageName) {
+ Set<String> copiedSet = new HashSet<>();
+ if (copiedVolumesMap != null) {
+ Map<String, Set<String>> connectionMap = copiedVolumesMap
+ .get(connectionName);
+ if (connectionMap != null) {
+ Set<String> imageSet = connectionMap.get(imageName);
+ if (imageSet != null) {
+ copiedSet = imageSet;
+ }
+ }
+ }
+ return copiedSet;
+ }
+
}
diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/launch/ContainerCommandProcess.java b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/launch/ContainerCommandProcess.java
index 2f2a6b5..7c5e06a 100644
--- a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/launch/ContainerCommandProcess.java
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/launch/ContainerCommandProcess.java
@@ -37,6 +37,8 @@
PipedOutputStream pipedStderr = new PipedOutputStream(
stderr)) {
connection.attachLog(containerId, pipedStdout, pipedStderr);
+ pipedStdout.flush();
+ pipedStderr.flush();
} catch (DockerException | InterruptedException | IOException e) {
// do nothing but close output streams
}
@@ -45,30 +47,18 @@
// start the thread
this.thread = new Thread(logContainer);
this.thread.start();
- // Runnable watchContainer = () -> {
- // try {
- // IDockerContainerExit exit = connection
- // .waitForContainer(containerId);
- // } catch (DockerException | InterruptedException e) {
- // // do nothing
- // }
- //
- // try {
- // System.out.println("watchcontainer finished");
- // this.stdout.close();
- // this.stderr.close();
- // this.thread.interrupt();
- // } catch (IOException e) {
- // Activator.log(e);
- // }
- // };
- // // kick off a thread to stop logging
- // new Thread(watchContainer).start();
}
@Override
public void destroy() {
try {
+ try {
+ // TODO: see if there is a better way of draining the
+ // container output before closing the streams
+ Thread.sleep(1000);
+ } catch (InterruptedException e1) {
+ // ignore
+ }
this.stdout.close();
this.stderr.close();
} catch (IOException e) {
@@ -115,6 +105,7 @@
try {
IDockerContainerExit exit = connection
.waitForContainer(containerId);
+ connection.stopLoggingThread(containerId);
return exit.statusCode();
} catch (DockerException e) {
return -1;