Bug 471668: Support Docker Machine Connections.

Change-Id: I61bcf2a4134506361b62ec0a1c9bc5712d4787d8
Reviewed-on: https://git.eclipse.org/r/56262
Tested-by: Hudson CI
Reviewed-by: Roland Grunberg <rgrunber@redhat.com>
Tested-by: Roland Grunberg <rgrunber@redhat.com>
diff --git a/containers/org.eclipse.linuxtools.docker.core/src/org/eclipse/linuxtools/internal/docker/core/DockerMachine.java b/containers/org.eclipse.linuxtools.docker.core/src/org/eclipse/linuxtools/internal/docker/core/DockerMachine.java
new file mode 100644
index 0000000..694b9c7
--- /dev/null
+++ b/containers/org.eclipse.linuxtools.docker.core/src/org/eclipse/linuxtools/internal/docker/core/DockerMachine.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Red Hat.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Red Hat - Initial Contribution
+ *******************************************************************************/
+package org.eclipse.linuxtools.internal.docker.core;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+public class DockerMachine {
+
+	private static final String DM = "docker-machine"; //$NON-NLS-1$
+	private static final String LS = "ls"; //$NON-NLS-1$
+	private static final String URL = "url"; //$NON-NLS-1$
+	private static final String ENV = "env"; //$NON-NLS-1$
+
+	public static String[] getNames() {
+		return call(new String[] { LS, "-q" }); //$NON-NLS-1$
+	}
+
+	public static String getHost(String name) {
+		String[] res = call(new String[] { URL, name });
+		return res.length == 1 ? res[0] : null;
+	}
+
+	public static String getCertPath(String name) {
+		String[] res = getEnv(name);
+		for (String l : res) {
+			if (l.contains("DOCKER_CERT_PATH")) { //$NON-NLS-1$
+				// DOCKER_CERT_PATH="/path/to/cert-folder"
+				return l.split("=")[1].replace("\"", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+			}
+		}
+		return null;
+	}
+
+	private static String[] getEnv(String name) {
+		return call(new String[] { ENV, name });
+	}
+
+	private static String[] call(String[] args) {
+		List<String> result = new ArrayList<>();
+		try {
+			List<String> cmd = new ArrayList<>();
+			cmd.add(DM);
+			cmd.addAll(Arrays.asList(args));
+			Process p = Runtime.getRuntime().exec(cmd.toArray(new String[0]));
+			BufferedReader buff = new BufferedReader(new InputStreamReader(p.getInputStream()));
+			if (p.waitFor(5, TimeUnit.SECONDS) && p.exitValue() == 0) {
+				String line;
+				while ((line = buff.readLine()) != null) {
+					result.add(line);
+				}
+			} else {
+				return new String[0];
+			}
+		} catch (IOException e) {
+		} catch (InterruptedException e) {
+		}
+		return result.toArray(new String[0]);
+	}
+
+}
diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/NewDockerConnectionPage.java b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/NewDockerConnectionPage.java
index 1510389..425166c 100644
--- a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/NewDockerConnectionPage.java
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/NewDockerConnectionPage.java
@@ -17,6 +17,8 @@
 import java.lang.reflect.InvocationTargetException;
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
@@ -39,17 +41,24 @@
 import org.eclipse.jface.layout.GridDataFactory;
 import org.eclipse.jface.layout.GridLayoutFactory;
 import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.Viewer;
 import org.eclipse.jface.wizard.WizardPage;
 import org.eclipse.linuxtools.docker.core.DockerConnectionManager;
 import org.eclipse.linuxtools.docker.core.DockerException;
+import org.eclipse.linuxtools.docker.core.EnumDockerConnectionSettings;
 import org.eclipse.linuxtools.docker.ui.Activator;
 import org.eclipse.linuxtools.internal.docker.core.DockerConnection;
 import org.eclipse.linuxtools.internal.docker.core.DockerConnection.Builder;
+import org.eclipse.linuxtools.internal.docker.core.DockerMachine;
 import org.eclipse.linuxtools.internal.docker.ui.SWTImagesFactory;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.SelectionAdapter;
 import org.eclipse.swt.events.SelectionEvent;
 import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.widgets.Button;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
@@ -59,6 +68,7 @@
 import org.eclipse.swt.widgets.Group;
 import org.eclipse.swt.widgets.Label;
 import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.dialogs.ListDialog;
 
 /**
  * {@link WizardPage} to input the settings to connect to a Docker
@@ -216,12 +226,21 @@
 		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
 				.applyTo(tcpCertPathBrowseButton);
 
+		// the 'Search' button
+		final Button searchButton = new Button(container, SWT.NONE);
+		searchButton.setText(WizardMessages
+				.getString("NewDockerConnectionPage.searchButton")); //$NON-NLS-1$
+		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
+				.span(COLUMNS - 1, 1).align(SWT.BEGINNING, SWT.CENTER)
+				.applyTo(searchButton);
+		searchButton.addSelectionListener(onSearchButtonSelection());
+
 		// the 'test connection' button
 		final Button testConnectionButton = new Button(container, SWT.NONE);
 		testConnectionButton.setText(WizardMessages
 				.getString("NewDockerConnectionPage.testConnection")); //$NON-NLS-1$
 		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
-				.span(COLUMNS, 1).align(SWT.END, SWT.CENTER)
+				.span(1, 1).align(SWT.END, SWT.CENTER)
 				.applyTo(testConnectionButton);
 		testConnectionButton
 				.addSelectionListener(onTestConnectionButtonSelection());
@@ -574,6 +593,67 @@
 		};
 	}
 
+	private SelectionListener onSearchButtonSelection() {
+		return new SelectionAdapter() {
+			@Override
+			public void widgetSelected(SelectionEvent e) {
+				String[] dmNames = DockerMachine.getNames();
+				List<String> activeNames = new ArrayList<>();
+				for (String name : dmNames) {
+					if (DockerMachine.getHost(name) != null) {
+						activeNames.add(name);
+					}
+				}
+				if (activeNames.size() > 0) {
+					ListDialog connPrompt = new ListDialog(getShell());
+					connPrompt.setContentProvider(new ConnectionSelectionContentProvider());
+					connPrompt.setLabelProvider(new ConnectionSelectionLabelProvider());
+					connPrompt.setTitle(WizardMessages.getString(
+							"NewDockerConnectionPage.searchDialog.title")); //$NON-NLS-1$
+					connPrompt.setMessage(WizardMessages.getString(
+							"NewDockerConnectionPage.searchDialog.message")); //$NON-NLS-1$
+					connPrompt.setInput(activeNames.toArray(new String[0]));
+					if (connPrompt.open() == 0 && connPrompt.getResult().length > 0) {
+						String name = ((String) connPrompt.getResult()[0]);
+						String host = DockerMachine.getHost(name);
+						String certPath = DockerMachine.getCertPath(name);
+						model.setBindingMode(EnumDockerConnectionSettings.TCP_CONNECTION);
+						model.setConnectionName(name);
+						model.setUnixSocketPath(null);
+						model.setTcpHost(host);
+						if (certPath != null) {
+							model.setTcpTLSVerify(true);
+							model.setTcpCertPath(certPath);
+						} else {
+							model.setTcpTLSVerify(false);
+							model.setTcpCertPath(null);
+						}
+					}
+				} else {
+					if (dmNames.length > 0) {
+						StringBuffer connections = new StringBuffer();
+						for (String conn : dmNames) {
+							connections.append(", "); //$NON-NLS-1$
+							connections.append(conn);
+						}
+						MessageDialog.openInformation(getShell(),
+								WizardMessages.getString(
+										"NewDockerConnectionPage.searchDialog.discovery.title"), //$NON-NLS-1$
+								WizardMessages.getFormattedString(
+										"NewDockerConnectionPage.searchDialog.discovery.innactive", //$NON-NLS-1$
+										connections.substring(2)));
+					} else {
+						MessageDialog.openInformation(getShell(),
+								WizardMessages.getString(
+										"NewDockerConnectionPage.searchDialog.discovery.title"), //$NON-NLS-1$
+								WizardMessages.getString(
+										"NewDockerConnectionPage.searchDialog.discovery.empty")); //$NON-NLS-1$
+					}
+				}
+			}
+		};
+	}
+
 	/**
 	 * Opens a new {@link DockerConnection} using the settings of this
 	 * {@link NewDockerConnectionPage}.
@@ -805,4 +885,50 @@
 
 	}
 
+	private class ConnectionSelectionContentProvider
+			implements IStructuredContentProvider {
+		@Override
+		public void inputChanged(Viewer viewer, Object oldInput,
+				Object newInput) {
+		}
+
+		@Override
+		public void dispose() {
+		}
+
+		@Override
+		public Object[] getElements(Object inputElement) {
+			return (String[]) (inputElement);
+		}
+	}
+
+	private class ConnectionSelectionLabelProvider implements ILabelProvider {
+		@Override
+		public void removeListener(ILabelProviderListener listener) {
+		}
+
+		@Override
+		public boolean isLabelProperty(Object element, String property) {
+			return false;
+		}
+
+		@Override
+		public void dispose() {
+		}
+
+		@Override
+		public void addListener(ILabelProviderListener listener) {
+		}
+
+		@Override
+		public String getText(Object element) {
+			return element.toString();
+		}
+
+		@Override
+		public Image getImage(Object element) {
+			return SWTImagesFactory.DESC_REPOSITORY_MIDDLE.createImage();
+		}
+	}
+
 }
diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/WizardMessages.properties b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/WizardMessages.properties
index 41d0f7d..fefc3aa 100644
--- a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/WizardMessages.properties
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/WizardMessages.properties
@@ -164,6 +164,7 @@
 NewDockerConnectionPage.tcpPathLabel=Path:
 NewDockerConnectionPage.tcpPathTooltip=Path to the certificates folder
 NewDockerConnectionPage.testConnection=Test Connection
+NewDockerConnectionPage.searchButton=Search
 NewDockerConnectionPage.retrieveTask=Retrieving Docker connection settings...
 NewDockerConnectionPage.pingTask=Pinging Docker daemon...
 NewDockerConnectionPage.success=Success
@@ -187,6 +188,11 @@
 NewDockerConnectionPage.validation.missingTcpCertPath.msg=Path to TCP connection certificates is missing
 NewDockerConnectionPage.validation.invalidTcpCertPath.msg=Path to TCP connection certificates is invalid
 NewDockerConnectionPage.validation.unreadableTcpCertPath.msg=Path to TCP connection certificates is not readable
+NewDockerConnectionPage.searchDialog.title=Docker Connection Selection
+NewDockerConnectionPage.searchDialog.message=Select the connection to create.
+NewDockerConnectionPage.searchDialog.discovery.title=Connection Discovery
+NewDockerConnectionPage.searchDialog.discovery.empty=No connections were found.
+NewDockerConnectionPage.searchDialog.discovery.innactive="Discovered connection(s): {0} but none are active."
 
 ImageSearch.title=Search and pull a Docker image
 ImageSearchPage.title=Search the Docker Registry for images