diff --git a/plugins/org.jboss.tools.openshift.ui/META-INF/MANIFEST.MF b/plugins/org.jboss.tools.openshift.ui/META-INF/MANIFEST.MF
index 0ea3dd666a..484330b007 100644
--- a/plugins/org.jboss.tools.openshift.ui/META-INF/MANIFEST.MF
+++ b/plugins/org.jboss.tools.openshift.ui/META-INF/MANIFEST.MF
@@ -50,7 +50,8 @@ Require-Bundle: org.jboss.tools.openshift.common.ui;bundle-version="[3.0.0,4.0.0
org.eclipse.ui.editors,
org.eclipse.core.expressions;bundle-version="3.5.0",
org.eclipse.jst.server.core,
- org.eclipse.swt;bundle-version="3.104.1"
+ org.eclipse.swt;bundle-version="3.104.1",
+ com.spotify.docker.client;bundle-version="3.4.0"
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Bundle-ActivationPolicy: lazy
Export-Package: org.jboss.tools.openshift.internal.ui;x-friends:="org.jboss.tools.openshift.test",
diff --git a/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/dockerutils/DockerImageUtils.java b/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/dockerutils/DockerImageUtils.java
new file mode 100644
index 0000000000..41ae4a83b7
--- /dev/null
+++ b/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/dockerutils/DockerImageUtils.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2016 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.jboss.tools.openshift.internal.ui.dockerutils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.eclipse.linuxtools.docker.core.IDockerConnection;
+import org.eclipse.linuxtools.docker.core.IDockerImage;
+
+/**
+ * Utility class for {@link IDockerImage}s
+ */
+public class DockerImageUtils {
+
+ /**
+ * Checks if an image with the given {@code repo} and {@code tag} exists in
+ * the given {@code dockerConnection}
+ *
+ * Workaround until https://bugs.eclipse.org/bugs/show_bug.cgi?id=495243 is
+ * fixed.
+ *
+ *
+ * @param dockerConnection
+ * the {@link IDockerConnection}
+ * @param repoName
+ * the repository/name of the image to look-up
+ * @param tag
+ * the image tag
+ * @return true
if match found, false
otherwise
+ */
+ public static boolean hasImage(final IDockerConnection dockerConnection, final String repoName, final String tag) {
+ for (IDockerImage image : dockerConnection.getImages()) {
+ final Map> repoTags = extractTagsByRepo(image.repoTags());
+ for (Entry> entry : repoTags.entrySet()) {
+ final String repo = entry.getKey();
+ final List tags = entry.getValue();
+ if (repo != null && repo.equals(repoName) && tags != null && tags.contains(tag)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Extracts the org/repo and all the associated tags from the given
+ * {@code repoTags}, assuming that the given repoTags elements have the
+ * following format: {@code [org/]repo[:tag]}. Tags are sorted by their
+ * natural order.
+ *
+ * @param repoTags
+ * the list of repo/tags to analyze
+ * @return the tags indexed by org/repo
+ */
+ public static Map> extractTagsByRepo(final List repoTags) {
+ final Map> results = new HashMap<>();
+ for (String entry : repoTags) {
+ final int indexOfColonChar = entry.lastIndexOf(':');
+ final String repo = (indexOfColonChar > -1) ? entry.substring(0, indexOfColonChar) : entry;
+ if (!results.containsKey(repo)) {
+ results.put(repo, new ArrayList());
+ }
+ if (indexOfColonChar > -1) {
+ results.get(repo).add(entry.substring(indexOfColonChar + 1));
+ }
+ }
+ // now sort the tags
+ for (Entry> entry : results.entrySet()) {
+ Collections.sort(entry.getValue());
+ }
+ return results;
+ }
+
+ /**
+ * Retrieves the {@code 'name:tag'} from {@code '[[registry/]repo/]name:tag'} format of the given {@code imageName}
+ * @param imageName
+ * @return the {@code 'name:tag'}
+ */
+ public static String extractImageNameAndTag(final String imageName) {
+ final int lastIndexOfSlash = imageName.lastIndexOf('/');
+ if(lastIndexOfSlash == -1) {
+ return imageName;
+ }
+ return imageName.substring(lastIndexOfSlash + 1);
+ }
+}
diff --git a/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/dockerutils/ProgressJob.java b/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/dockerutils/ProgressJob.java
new file mode 100644
index 0000000000..de90670be0
--- /dev/null
+++ b/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/dockerutils/ProgressJob.java
@@ -0,0 +1,74 @@
+package org.jboss.tools.openshift.internal.ui.dockerutils;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+
+// FIXME: temporary code duplication until https://bugs.eclipse.org/bugs/show_bug.cgi?id=495251 is done.
+public class ProgressJob extends Job {
+
+ private int percentageDone = 0;
+ private int percentageChange = 0;
+
+ private Object lockObject = new Object();
+
+ private String jobName;
+
+ public ProgressJob(String name, String jobName) {
+ super(name);
+ this.jobName = jobName;
+ }
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ monitor.beginTask(jobName, 100);
+ boolean done = false;
+
+ while (!done) {
+ if (monitor.isCanceled()) {
+ return Status.CANCEL_STATUS;
+ }
+ // if work percentage has changed...add new amount
+ int change = getPercentageChange();
+ if (change > 0) {
+ monitor.worked(change);
+ setPercentageChange(0);
+ }
+ // if we are 100% or more done, then we are done
+ if (percentageDone >= 100) {
+ done = true;
+ }
+ // otherwise, sleep and then loop again
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ done = true;
+ }
+ }
+ monitor.done();
+ return Status.OK_STATUS;
+ }
+
+ private int getPercentageChange() {
+ synchronized (lockObject) {
+ return percentageChange;
+ }
+ }
+
+ private void setPercentageChange(int percentChange) {
+ synchronized (lockObject) {
+ this.percentageChange = percentChange;
+ }
+ }
+
+ public void setPercentageDone(int percentage) {
+ synchronized (lockObject) {
+ if (percentage > percentageDone) {
+ percentageChange = percentage - percentageDone;
+ percentageDone = percentage;
+ }
+ }
+ }
+
+}
diff --git a/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/dockerutils/PushImageToRegistryJob.java b/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/dockerutils/PushImageToRegistryJob.java
new file mode 100644
index 0000000000..0d5eb6d652
--- /dev/null
+++ b/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/dockerutils/PushImageToRegistryJob.java
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * Copyright (c) 2016 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.jboss.tools.openshift.internal.ui.dockerutils;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.linuxtools.docker.core.DockerException;
+import org.eclipse.linuxtools.docker.core.IDockerConnection;
+import org.eclipse.linuxtools.docker.core.IRegistryAccount;
+import org.eclipse.linuxtools.internal.docker.ui.views.ImagePushProgressHandler;
+import org.jboss.tools.openshift.internal.common.core.job.AbstractDelegatingMonitorJob;
+import org.jboss.tools.openshift.internal.ui.OpenShiftUIActivator;
+
+import com.spotify.docker.client.DockerCertificateException;
+
+/**
+ * {@link Job} to push an image from a Docker daemon into the OpenShift registry
+ */
+public class PushImageToRegistryJob extends AbstractDelegatingMonitorJob {
+
+ private final IDockerConnection dockerConnection;
+
+ private final IRegistryAccount registryAccount;
+
+ private final String imageName;
+
+ private final String openshiftProject;
+
+ /**
+ * Constructor
+ * @param dockerConnection the Docker connection to use
+ * @param registryAccount the registry account to push the image into
+ * @param openshiftProject the name of the OpenShift project, because the image has to be into the same namespace
+ * @param imageName the name of the image
+ */
+ public PushImageToRegistryJob(final IDockerConnection dockerConnection, final IRegistryAccount registryAccount, final String openshiftProject, final String imageName) {
+ super("Pushing Docker image to OpenShift registry...");
+ this.dockerConnection = dockerConnection;
+ this.registryAccount = registryAccount;
+ this.imageName = imageName;
+ this.openshiftProject = openshiftProject;
+ }
+
+ @Override
+ protected IStatus doRun(final IProgressMonitor monitor) {
+ monitor.beginTask("Pushing image to registry", 1);
+ final String tmpImageName = getPushToRegistryImageName();
+ try {
+ // first, we need to tag the image with the OpenShift target
+ // project
+ this.dockerConnection.tagImage(imageName, tmpImageName);
+ // then we can push that image with the new name
+ this.dockerConnection.pushImage(tmpImageName, registryAccount,
+ new ImagePushProgressHandler(this.dockerConnection, tmpImageName));
+ }
+ // FIXME: needs to catch DockerCertificateException until
+ // https://bugs.eclipse.org/bugs/show_bug.cgi?id=495249 is fixed
+ catch (DockerException | InterruptedException | DockerCertificateException e) {
+ return new Status(IStatus.ERROR, OpenShiftUIActivator.PLUGIN_ID,
+ "Failed to push the selected Docker image into OpenShift registry", e);
+ } finally {
+ // we need to untag the image, even if the push operation failed
+ try {
+ this.dockerConnection.removeTag(tmpImageName);
+ } catch (DockerException | InterruptedException e) {
+ return new Status(IStatus.WARNING, OpenShiftUIActivator.PLUGIN_ID,
+ "Pushed the selected Docker image into OpenShift registry but failed to untag it afterwards",
+ e);
+ }
+ monitor.done();
+ }
+ // TODO Auto-generated method stub
+ return Status.OK_STATUS;
+ }
+
+ /**
+ * @return the name used to push to the registry.
+ */
+ public String getPushToRegistryImageName() {
+ try {
+ final String registryHostname = new URL(this.registryAccount.getServerAddress()).getHost();
+ final String tmpImageName = registryHostname + '/' + this.openshiftProject + '/' + DockerImageUtils.extractImageNameAndTag(this.imageName);
+ return tmpImageName;
+ } catch (MalformedURLException e) {
+ OpenShiftUIActivator.getDefault().getLog().log(new Status(IStatus.ERROR, OpenShiftUIActivator.PLUGIN_ID,
+ "Failed to push the selected Docker image into OpenShift registry", e));
+ return null;
+ }
+
+ }
+
+}
diff --git a/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/dockerutils/package-info.java b/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/dockerutils/package-info.java
new file mode 100644
index 0000000000..4076d9adbd
--- /dev/null
+++ b/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/dockerutils/package-info.java
@@ -0,0 +1,15 @@
+/*******************************************************************************
+ * Copyright (c) 2016 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.jboss.tools.openshift.internal.ui.dockerutils;
\ No newline at end of file
diff --git a/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/handler/DeployImageHandler.java b/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/handler/DeployImageHandler.java
index d816ec6e9e..0acd3c1ed4 100644
--- a/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/handler/DeployImageHandler.java
+++ b/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/handler/DeployImageHandler.java
@@ -117,7 +117,7 @@ public void done(IJobChangeEvent event) {
@Override
public void run() {
DeployImageWizard wizard = new DeployImageWizard(image, connection, project, connected[0]);
- WizardUtils.openWizardDialog(600, 1500, wizard, shell);
+ WizardUtils.openWizardDialog(500, 500, wizard, shell);
}
});
}
diff --git a/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/job/DeployImageJob.java b/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/job/DeployImageJob.java
index 6ae86f73ac..0e6a805414 100644
--- a/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/job/DeployImageJob.java
+++ b/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/job/DeployImageJob.java
@@ -33,6 +33,7 @@
import org.jboss.tools.openshift.internal.core.Trace;
import org.jboss.tools.openshift.internal.core.util.OpenShiftProjectUtils;
import org.jboss.tools.openshift.internal.ui.OpenShiftUIActivator;
+import org.jboss.tools.openshift.internal.ui.dockerutils.DockerImageUtils;
import org.jboss.tools.openshift.internal.ui.wizard.common.EnvironmentVariable;
import org.jboss.tools.openshift.internal.ui.wizard.common.IResourceLabelsPageModel.Label;
import org.jboss.tools.openshift.internal.ui.wizard.deployimage.IDeployImageParameters;
@@ -138,7 +139,13 @@ private Collection createResources(Connection connection, Collection
private Map generateResources(final Connection connection, final String name) {
final IResourceFactory factory = connection.getResourceFactory();
final IProject project = parameters.getProject();
- DockerImageURI sourceImage = new DockerImageURI(parameters.getImageName());
+ String imageName;
+ if (parameters.isPushImageToRegistry()) {
+ imageName = project.getNamespace() +"/" + DockerImageUtils.extractImageNameAndTag(parameters.getImageName());
+ } else {
+ imageName = parameters.getImageName();
+ }
+ DockerImageURI sourceImage = new DockerImageURI(imageName);
Map resources = new HashMap<>(4);
diff --git a/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/wizard/deployimage/DeployImagePage.java b/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/wizard/deployimage/DeployImagePage.java
index 24401a7a97..5097ba32b7 100644
--- a/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/wizard/deployimage/DeployImagePage.java
+++ b/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/wizard/deployimage/DeployImagePage.java
@@ -63,6 +63,7 @@
import org.jboss.tools.common.ui.WizardUtils;
import org.jboss.tools.common.ui.databinding.ParametrizableWizardPageSupport;
import org.jboss.tools.common.ui.databinding.ValueBindingBuilder;
+import org.jboss.tools.openshift.common.core.utils.UrlUtils;
import org.jboss.tools.openshift.core.connection.Connection;
import org.jboss.tools.openshift.internal.common.ui.connection.ConnectionColumLabelProvider;
import org.jboss.tools.openshift.internal.common.ui.databinding.IsNotNull2BooleanConverter;
@@ -88,13 +89,16 @@
* @author jeff.cantrill
*/
public class DeployImagePage extends AbstractOpenShiftWizardPage {
+
private static final String MISSING_DOCKER_CONNECTION_MSG = "You must select a Docker connection.";
static String DEPLOY_IMAGE_PAGE_NAME = "Deployment Config Settings Page";
private static final String PAGE_DESCRIPTION = "This page allows you to choose an image and the name to be used for the deployed resources.";
- private IDeployImagePageModel model;
+ private static final int NUM_COLUMS = 4;
+
+ private final IDeployImagePageModel model;
ContentProposalAdapter imageNameProposalAdapter;
@@ -175,7 +179,9 @@ protected void onPageWillGetActivated(final Direction progress, final PageChangi
@Override
protected void doCreateControls(Composite parent, DataBindingContext dbc) {
- GridLayoutFactory.fillDefaults().numColumns(3).margins(10, 10).applyTo(parent);
+ GridLayoutFactory.fillDefaults().numColumns(NUM_COLUMS)
+ .margins(10, 10)
+ .applyTo(parent);
createOpenShiftConnectionControl(parent, dbc);
createProjectControl(parent, dbc);
createSeparator(parent);
@@ -185,7 +191,18 @@ protected void doCreateControls(Composite parent, DataBindingContext dbc) {
createDockerConnectionInfoControl(parent, dbc);
}
createImageNameControls(parent, dbc);
- new ResourceNameControl().doCreateControl(parent, dbc, model);
+ new ResourceNameControl() {
+ @Override
+ protected void layoutText(Text resourceNameText) {
+ GridDataFactory.fillDefaults()
+ .align(SWT.FILL, SWT.CENTER)
+ .grab(true, false)
+ .span(NUM_COLUMS -1, 1)
+ .applyTo(resourceNameText);
+ }
+ }.doCreateControl(parent, dbc, model);
+ createSeparator(parent);
+ createPushToRegistrySettings(parent, dbc);
}
private void createSeparator(Composite parent) {
@@ -193,11 +210,30 @@ private void createSeparator(Composite parent) {
.fillDefaults()
.align(SWT.FILL, SWT.BEGINNING)
.grab(true, false)
- .span(3, 1)
+ .span(NUM_COLUMS, 1)
.applyTo(new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL));
}
- private SelectionAdapter onSearch(Text txtImage) {
+ private SelectionAdapter onBrowseImage() {
+ return new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ if (model.getDockerConnection() == null) {
+ MessageDialog.openError(getShell(), "A Docker connection must be selected", MISSING_DOCKER_CONNECTION_MSG);
+ return;
+ }
+ final ListDockerImagesWizard wizard = new ListDockerImagesWizard(model.getDockerConnection(), model.getImageName());
+ final OkCancelButtonWizardDialog wizardDialog = new OkCancelButtonWizardDialog(getShell(), wizard);
+ wizardDialog.setPageSize(500, 400);
+ if(Window.OK == wizardDialog.open()){
+ //this bypasses validation
+ model.setImageName(wizard.getSelectedImageName());
+ }
+ }
+ };
+ }
+
+ private SelectionAdapter onSearchImage(final Text txtImage) {
return new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
@@ -223,7 +259,7 @@ private void createDockerConnectionControl(Composite parent, DataBindingContext
GridDataFactory.fillDefaults()
.align(SWT.FILL, SWT.CENTER)
.grab(true, false)
- .span(1, 1)
+ .span(NUM_COLUMS - 2, 1)
.applyTo(connectionViewer.getControl());
connectionViewer.setContentProvider(new ObservableListContentProvider());
@@ -259,7 +295,7 @@ public String getText(Object element) {
.applyTo(newDockerConnectionButton);
UIUtils.setDefaultButtonWidth(newDockerConnectionButton);
newDockerConnectionButton.addSelectionListener(onNewDockerConnectionClicked());
-
+
dbc.addValidationStatusProvider(validator);
}
@@ -304,7 +340,7 @@ private void createDockerConnectionInfoControl(Composite parent, DataBindingCont
connectionText.setBackground(lblConnection.getBackground());
GridDataFactory.fillDefaults()
.align(SWT.FILL, SWT.CENTER)
- .span(2, 1)
+ .span(NUM_COLUMS - 1, 1)
.grab(true, false)
.applyTo(connectionText);
final IObservableValue connnectionTextObservable = WidgetProperties.text(SWT.None).observe(connectionText);
@@ -332,7 +368,7 @@ private void createOpenShiftConnectionControl(Composite parent, DataBindingConte
connectionText.setBackground(lblConnection.getBackground());
GridDataFactory.fillDefaults()
.align(SWT.FILL, SWT.CENTER)
- .span(2, 1)
+ .span(NUM_COLUMS - 1, 1)
.grab(true, false)
.applyTo(connectionText);
final IObservableValue connnectionTextObservable = WidgetProperties.text(SWT.None).observe(connectionText);
@@ -362,6 +398,7 @@ private void createProjectControl(Composite parent, DataBindingContext dbc) {
.align(SWT.FILL, SWT.CENTER)
.grab(true, false)
.hint(SWT.DEFAULT, 30)
+ .span(NUM_COLUMS - 2, 1)
.applyTo(cmboProject.getControl());
final OpenShiftExplorerLabelProvider labelProvider = new OpenShiftExplorerLabelProvider();
@@ -391,7 +428,7 @@ private void createProjectControl(Composite parent, DataBindingContext dbc) {
.applyTo(newProjectButton);
UIUtils.setDefaultButtonWidth(newProjectButton);
newProjectButton.addSelectionListener(onNewProjectClicked());
-
+
dbc.addValidationStatusProvider(validator);
cmboProject.getControl().forceFocus();
@@ -456,15 +493,23 @@ public void insertControlContents(Control control,
}
}, getImageNameContentProposalProvider(imageNameText),
null, null);
- //browse
+
+ // List local Docker images
+ Button btnDockerBrowse = new Button(parent, SWT.NONE);
+ btnDockerBrowse.setText("Browse...");
+ btnDockerBrowse.setToolTipText("Look-up an image by browsing the Docker daemon");
+ btnDockerBrowse.addSelectionListener(onBrowseImage());
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).applyTo(btnDockerBrowse);
+ ValueBindingBuilder.bind(WidgetProperties.enabled().observe(btnDockerBrowse)).notUpdatingParticipant()
+ .to(BeanProperties.value(IDeployImagePageModel.PROPERTY_DOCKER_CONNECTION).observe(model))
+ .converting(new IsNotNull2BooleanConverter()).in(dbc);
+
+ // search on Docker registry (Docker Hub)
Button btnDockerSearch = new Button(parent, SWT.NONE);
btnDockerSearch.setText("Search...");
- btnDockerSearch.setToolTipText("Look-up an image by browsing the docker daemon");
- btnDockerSearch.addSelectionListener(onSearch(imageNameText));
- GridDataFactory.fillDefaults()
- .align(SWT.FILL, SWT.CENTER)
- .applyTo(btnDockerSearch);
-
+ btnDockerSearch.setToolTipText("Search an image on the Docker registry");
+ btnDockerSearch.addSelectionListener(onSearchImage(imageNameText));
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).applyTo(btnDockerSearch);
ValueBindingBuilder
.bind(WidgetProperties.enabled().observe(btnDockerSearch))
.notUpdatingParticipant()
@@ -472,7 +517,110 @@ public void insertControlContents(Control control,
.converting(new IsNotNull2BooleanConverter())
.in(dbc);
}
+
+ @SuppressWarnings("unchecked")
+ private void createPushToRegistrySettings(final Composite parent, final DataBindingContext dbc) {
+ // checkbox
+ final Button pushImageToRegistryButton = new Button(parent, SWT.CHECK);
+ pushImageToRegistryButton.setText("Push Image to Registry");
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).span(NUM_COLUMS, 1).applyTo(pushImageToRegistryButton);
+ final IObservableValue pushImageToRegistryButtonObservable = BeanProperties
+ .value(IDeployImagePageModel.PROPERTY_PUSH_IMAGE_TO_REGISTRY).observe(model);
+ ValueBindingBuilder.bind(WidgetProperties.selection().observe(pushImageToRegistryButton))
+ .to(pushImageToRegistryButtonObservable).in(dbc);
+
+ // registry location
+ final Label registryLocationLabel = new Label(parent, SWT.NONE);
+ registryLocationLabel.setText("Image Registry URL:");
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false).indent(30, 0)
+ .applyTo(registryLocationLabel);
+ final Text registryLocationText = new Text(parent, SWT.BORDER);
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false).span(NUM_COLUMS - 1, 1)
+ .applyTo(registryLocationText);
+ final IObservableValue registryLocationObservable = BeanProperties
+ .value(IDeployImagePageModel.PROPERTY_TARGET_REGISTRY_LOCATION).observe(model);
+ ValueBindingBuilder.bind(WidgetProperties.text(SWT.Modify).observe(registryLocationText))
+ .to(registryLocationObservable).in(dbc);
+ ValueBindingBuilder.bind(WidgetProperties.enabled().observe(registryLocationText))
+ .to(pushImageToRegistryButtonObservable).in(dbc);
+
+ // username to authenticate on registry
+ final Label registryUsernameLabel = new Label(parent, SWT.NONE);
+ registryUsernameLabel.setText("Username:");
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false).indent(30, 0)
+ .applyTo(registryUsernameLabel);
+ final Text registryUsernameText = new Text(parent, SWT.BORDER);
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false).span(NUM_COLUMS - 1, 1)
+ .applyTo(registryUsernameText);
+ final IObservableValue registryUsernameObservable = BeanProperties
+ .value(IDeployImagePageModel.PROPERTY_TARGET_REGISTRY_USERNAME).observe(model);
+ ValueBindingBuilder.bind(WidgetProperties.text(SWT.Modify).observe(registryUsernameText))
+ .to(registryUsernameObservable).in(dbc);
+ ValueBindingBuilder.bind(WidgetProperties.enabled().observe(registryUsernameText))
+ .to(pushImageToRegistryButtonObservable).in(dbc);
+
+ // password to authenticate on registry
+ final Label registryPasswordLabel = new Label(parent, SWT.NONE);
+ registryPasswordLabel.setText("Password:");
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false).indent(30, 0)
+ .applyTo(registryPasswordLabel);
+ final Text registryPasswordText = new Text(parent, SWT.BORDER + SWT.PASSWORD);
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false).span(NUM_COLUMS - 1, 1)
+ .applyTo(registryPasswordText);
+ final IObservableValue registryPasswordObservable = BeanProperties
+ .value(IDeployImagePageModel.PROPERTY_TARGET_REGISTRY_PASSWORD).observe(model);
+ ValueBindingBuilder.bind(WidgetProperties.text(SWT.Modify).observe(registryPasswordText))
+ .to(registryPasswordObservable).in(dbc);
+ ValueBindingBuilder.bind(WidgetProperties.enabled().observe(registryPasswordText))
+ .to(pushImageToRegistryButtonObservable).in(dbc);
+
+ // validation
+ final PushImageToRegistryStatusProvider validator = new PushImageToRegistryStatusProvider(
+ pushImageToRegistryButtonObservable, registryLocationObservable, registryUsernameObservable,
+ registryPasswordObservable);
+ dbc.addValidationStatusProvider(validator);
+
+ }
+ class PushImageToRegistryStatusProvider extends MultiValidator {
+
+ private final IObservableValue pushImageToRegistryObservable;
+ private final IObservableValue targetRegistryLocationObservable;
+ private final IObservableValue targetRegistryUsernameObservable;
+ private final IObservableValue targetRegistryPasswordObservable;
+
+ PushImageToRegistryStatusProvider(final IObservableValue pushImageToRegistryObservable,
+ final IObservableValue targetRegistryLocationObservable, final IObservableValue targetRegistryUsernameObservable,
+ final IObservableValue targetRegistryPasswordObservable) {
+ this.pushImageToRegistryObservable = pushImageToRegistryObservable;
+ this.targetRegistryLocationObservable = targetRegistryLocationObservable;
+ this.targetRegistryUsernameObservable = targetRegistryUsernameObservable;
+ this.targetRegistryPasswordObservable = targetRegistryPasswordObservable;
+ }
+
+ @Override
+ protected IStatus validate() {
+ final boolean pushImageToRegistry = pushImageToRegistryObservable.getValue();
+ final String targetRegistryLocation = targetRegistryLocationObservable.getValue();
+ final String targetRegistryUsername = targetRegistryUsernameObservable.getValue();
+ final String targetRegistryPassword = targetRegistryPasswordObservable.getValue();
+ if (pushImageToRegistry) {
+ if (targetRegistryLocation == null || targetRegistryLocation.isEmpty()) {
+ return ValidationStatus.error("Please specify location of the Docker registry to push the image");
+ } else if(!UrlUtils.hasScheme(targetRegistryLocation)) {
+ return ValidationStatus.error("Please provide a valid image registry (HTTP/S) URL.");
+ }
+ if (targetRegistryUsername == null || targetRegistryUsername.isEmpty()) {
+ return ValidationStatus.info("The username to authenticate to the target registry is missing. Authentication may fail.");
+ }
+ if (targetRegistryPassword == null || targetRegistryPassword.isEmpty()) {
+ return ValidationStatus.info("The password to authenticate to the target registry is missing. Authentication may fail.");
+ }
+ }
+ return Status.OK_STATUS;
+ }
+ }
+
/**
* Creates an {@link IContentProposalProvider} to propose
* {@link IDockerImage} names based on the current text.
@@ -572,4 +720,6 @@ protected IStatus updateUI(IProgressMonitor monitor) {
}
};
}
+
+
}
diff --git a/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/wizard/deployimage/DeployImageWizard.java b/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/wizard/deployimage/DeployImageWizard.java
index 2aa5800e84..d24cb5de04 100644
--- a/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/wizard/deployimage/DeployImageWizard.java
+++ b/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/wizard/deployimage/DeployImageWizard.java
@@ -11,17 +11,26 @@
import static org.jboss.tools.common.ui.WizardUtils.runInWizard;
import java.lang.reflect.InvocationTargetException;
+import java.util.List;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.linuxtools.docker.core.DockerException;
import org.eclipse.linuxtools.docker.core.IDockerConnection;
import org.eclipse.linuxtools.docker.core.IDockerImage;
+import org.eclipse.linuxtools.docker.core.IDockerImageSearchResult;
+import org.eclipse.linuxtools.docker.core.IRegistryAccount;
+import org.eclipse.linuxtools.docker.core.IRepositoryTag;
import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
import org.jboss.tools.common.ui.JobUtils;
+import org.jboss.tools.foundation.core.jobs.DelegatingProgressMonitor;
import org.jboss.tools.openshift.common.ui.wizard.AbstractOpenShiftWizard;
+import org.jboss.tools.openshift.core.ICommonAttributes;
import org.jboss.tools.openshift.core.connection.Connection;
import org.jboss.tools.openshift.core.connection.ConnectionsRegistryUtil;
import org.jboss.tools.openshift.internal.common.core.UsageStats;
@@ -30,6 +39,7 @@
import org.jboss.tools.openshift.internal.common.ui.utils.OpenShiftUIUtils;
import org.jboss.tools.openshift.internal.ui.OpenShiftUIActivator;
import org.jboss.tools.openshift.internal.ui.dialog.ResourceSummaryDialog;
+import org.jboss.tools.openshift.internal.ui.dockerutils.PushImageToRegistryJob;
import org.jboss.tools.openshift.internal.ui.job.DeployImageJob;
import org.jboss.tools.openshift.internal.ui.job.RefreshResourcesJob;
import org.jboss.tools.openshift.internal.ui.wizard.common.ResourceLabelsPage;
@@ -65,7 +75,12 @@ public DeployImageWizard(IDockerImage image, Connection connection, IProject pro
model.setConnection(connection);
}
}
-
+ if(connection != null) {
+ model.setTargetRegistryLocation(
+ (String) connection.getExtendedProperties().get(ICommonAttributes.IMAGE_REGISTRY_URL_KEY));
+ model.setTargetRegistryUsername(connection.getUsername());
+ model.setTargetRegistryPassword(connection.getToken());
+ }
model.setStartedWithActiveConnection(isConnected);
setNeedsProgressMonitor(true);
@@ -93,7 +108,40 @@ public IWizardPage getStartingPage() {
@Override
public boolean performFinish() {
- final DeployImageJob deployJob = new DeployImageJob( getModel());
+ // checks if we need to push the image, first
+ final Job job = getJobChain(getModel(), PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell());
+ job.addJobChangeListener(new JobChangeAdapter() {
+ @Override
+ public void done(IJobChangeEvent event) {
+ final IStatus status = event.getResult();
+ UsageStats.getInstance().newV3Application( getModel().getConnection().getHost(), isFailed(status));
+ super.done(event);
+ }
+ });
+ job.schedule();
+
+ return true;
+ }
+
+ /**
+ * Gets the Job to run as a chain of smaller jobs, depending on the use-case
+ * @param model the wizard model
+ * @param shell the current shell
+ * @return
+ */
+ private Job getJobChain(final IDeployImageParameters model, final Shell shell) {
+ final DeployImageJob deployJob = getDeployImageJob(getModel(), getShell());
+ final boolean pushImageToRegistry = model.isPushImageToRegistry();
+ if(pushImageToRegistry) {
+ final PushImageToRegistryJob pushImageToRegistryJob = getPushImageToRegistryJob(model);
+ return new JobChainBuilder(pushImageToRegistryJob).runWhenSuccessfullyDone(deployJob)
+ .runWhenSuccessfullyDone(new RefreshResourcesJob(deployJob, true)).build();
+ }
+ return new JobChainBuilder(deployJob).runWhenSuccessfullyDone(new RefreshResourcesJob(deployJob, true)).build();
+ }
+
+ private static DeployImageJob getDeployImageJob(final IDeployImageParameters model, final Shell shell) {
+ final DeployImageJob deployJob = new DeployImageJob(model);
deployJob.addJobChangeListener(new JobChangeAdapter(){
@Override
@@ -104,7 +152,7 @@ public void done(IJobChangeEvent event) {
@Override
public void run() {
new ResourceSummaryDialog(
- getShell(),
+ shell,
deployJob.getResources(),
TITLE,
deployJob.getSummaryMessage()).open();
@@ -114,25 +162,62 @@ public void run() {
}
}
});
- boolean success = false;
- try {
- Job job = new JobChainBuilder(deployJob)
- .runWhenSuccessfullyDone(new RefreshResourcesJob(deployJob, true))
- .build();
- IStatus status = runInWizard(
- job,
- deployJob.getDelegatingProgressMonitor(),
- getContainer());
- success = isFailed(status);
- } catch (InvocationTargetException | InterruptedException e) {
- OpenShiftUIActivator.getDefault().getLogger().logError(e);
- success = false;
- } finally {
- UsageStats.getInstance().newV3Application( getModel().getConnection().getHost(), success);
- }
- return success;
+ return deployJob;
+ }
+
+ private static PushImageToRegistryJob getPushImageToRegistryJob(final IDeployImageParameters model) {
+ final IDockerConnection dockerConnection = model.getDockerConnection();
+ final String imageName = model.getImageName();
+ final String deployProjectName = model.getProject().getName();
+ final IRegistryAccount registryAccount = new IRegistryAccount() {
+
+ @Override
+ public String getServerAddress() {
+ return model.getTargetRegistryLocation();
+ }
+
+ @Override
+ public String getUsername() {
+ return model.getTargetRegistryUsername();
+ }
+
+ @Override
+ public char[] getPassword() {
+ return model.getTargetRegistryPassword().toCharArray();
+ }
+
+ @Override
+ public String getEmail() {
+ return null;
+ }
+
+ @Override
+ public List getTags(String arg0) throws DockerException {
+ return null;
+ }
+
+ @Override
+ public boolean isVersion2() {
+ return false;
+ }
+
+ @Override
+ public List getImages(String arg0) throws DockerException {
+ return null;
+ }
+ };
+ return new PushImageToRegistryJob(dockerConnection, registryAccount, deployProjectName, imageName);
}
+ /**
+ * Checks if the given {@code status}
+ *
+ * @param status
+ * the {@link IStatus} to check
+ * @return true
if the given status severity is
+ * {@link IStatus.OK} or {@link IStatus.WARNING}, false
+ * otherwise.
+ */
public static boolean isFailed(IStatus status) {
return JobUtils.isOk(status) || JobUtils.isWarning(status);
}
@@ -140,7 +225,7 @@ public static boolean isFailed(IStatus status) {
@Override
public void dispose() {
super.dispose();
- getModel().dispose();
+ //getModel().dispose();
}
}
diff --git a/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/wizard/deployimage/DeployImageWizardModel.java b/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/wizard/deployimage/DeployImageWizardModel.java
index 655ba00a54..dcf7eddfbb 100644
--- a/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/wizard/deployimage/DeployImageWizardModel.java
+++ b/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/wizard/deployimage/DeployImageWizardModel.java
@@ -20,6 +20,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.Map.Entry;
import java.util.stream.Collectors;
import org.apache.commons.lang.StringUtils;
@@ -32,6 +33,7 @@
import org.eclipse.linuxtools.docker.core.DockerConnectionManager;
import org.eclipse.linuxtools.docker.core.IDockerConnection;
import org.eclipse.linuxtools.docker.core.IDockerConnectionManagerListener2;
+import org.eclipse.linuxtools.docker.core.IDockerImage;
import org.eclipse.linuxtools.docker.core.IDockerImageInfo;
import org.jboss.tools.openshift.common.core.connection.ConnectionsRegistrySingleton;
import org.jboss.tools.openshift.core.connection.Connection;
@@ -40,9 +42,11 @@
import org.jboss.tools.openshift.internal.core.models.PortSpecAdapter;
import org.jboss.tools.openshift.internal.core.util.OpenShiftProjectUtils;
import org.jboss.tools.openshift.internal.ui.OpenShiftUIActivator;
+import org.jboss.tools.openshift.internal.ui.dockerutils.DockerImageUtils;
import org.jboss.tools.openshift.internal.ui.wizard.common.EnvironmentVariable;
import org.jboss.tools.openshift.internal.ui.wizard.common.EnvironmentVariablesPageModel;
import org.jboss.tools.openshift.internal.ui.wizard.common.ResourceLabelsPageModel;
+import org.jboss.tools.openshift.internal.ui.wizard.deployimage.ListDockerImagesWizardModel.DockerImageTag;
import com.openshift.restclient.ResourceKind;
import com.openshift.restclient.images.DockerImageURI;
@@ -86,6 +90,11 @@ public class DeployImageWizardModel
private boolean originatedFromDockerExplorer;
private boolean isStartedWithActiveConnection = false;
private IDockerImageMetadata imageMeta;
+ private boolean pushImageToRegistry = false;
+ private String targetRegistryLocation;
+ private String targetRegistryUsername;
+ private String targetRegistryPassword;
+
private final List imageNames = new ArrayList<>();
@@ -277,6 +286,50 @@ public void setImageName(String imageName, boolean forceUpdate) {
setImageName(imageName);
}
+ @Override
+ public boolean isPushImageToRegistry() {
+ return this.pushImageToRegistry;
+ }
+
+ @Override
+ public void setPushImageToRegistry(final boolean pushImageToRegistry) {
+ firePropertyChange(PROPERTY_PUSH_IMAGE_TO_REGISTRY, this.pushImageToRegistry,
+ this.pushImageToRegistry = pushImageToRegistry);
+ }
+
+ @Override
+ public String getTargetRegistryLocation() {
+ return this.targetRegistryLocation;
+ }
+
+ @Override
+ public void setTargetRegistryLocation(final String targetRegistryLocation) {
+ firePropertyChange(PROPERTY_TARGET_REGISTRY_LOCATION, this.targetRegistryLocation,
+ this.targetRegistryLocation = targetRegistryLocation);
+ }
+
+ @Override
+ public String getTargetRegistryUsername() {
+ return this.targetRegistryUsername;
+ }
+
+ @Override
+ public void setTargetRegistryUsername(final String targetRegistryUsername) {
+ firePropertyChange(PROPERTY_TARGET_REGISTRY_USERNAME, this.targetRegistryUsername,
+ this.targetRegistryUsername = targetRegistryUsername);
+ }
+
+ @Override
+ public String getTargetRegistryPassword() {
+ return this.targetRegistryPassword;
+ }
+
+ @Override
+ public void setTargetRegistryPassword(final String targetRegistryPassword) {
+ firePropertyChange(PROPERTY_TARGET_REGISTRY_PASSWORD, this.targetRegistryPassword,
+ this.targetRegistryPassword = targetRegistryPassword);
+ }
+
@Override
public boolean initializeContainerInfo() {
this.imageMeta = lookupImageMetadata();
@@ -500,9 +553,12 @@ public void setDockerConnection(IDockerConnection dockerConnection) {
if(dockerConnection == null) {
return;
}
- this.imageNames.addAll(dockerConnection.getImages().stream()
- .filter(image -> !image.isDangling() && !image.isIntermediateImage())
- .flatMap(image -> image.repoTags().stream()).sorted().collect(Collectors.toList()));
+ final List images = dockerConnection.getImages();
+ if(images != null) {
+ this.imageNames.addAll(dockerConnection.getImages().stream()
+ .filter(image -> !image.isDangling() && !image.isIntermediateImage())
+ .flatMap(image -> image.repoTags().stream()).sorted().collect(Collectors.toList()));
+ }
}
@@ -514,7 +570,8 @@ protected IDockerImageMetadata lookupImageMetadata() {
final DockerImageURI imageURI = new DockerImageURI(this.imageName);
final String repo = imageURI.getUriWithoutTag();
final String tag = StringUtils.defaultIfBlank(imageURI.getTag(), "latest");
- if (dockerConnection != null && dockerConnection.hasImage(repo, tag)) {
+
+ if (dockerConnection != null && DockerImageUtils.hasImage(dockerConnection, repo, tag)) {
final IDockerImageInfo info = dockerConnection.getImageInfo(this.imageName);
return new DockerConfigMetaData(info);
} else if (this.project != null) {
diff --git a/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/wizard/deployimage/IDeployImagePageModel.java b/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/wizard/deployimage/IDeployImagePageModel.java
index cb98a9fcd8..4dd6deb585 100644
--- a/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/wizard/deployimage/IDeployImagePageModel.java
+++ b/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/wizard/deployimage/IDeployImagePageModel.java
@@ -33,6 +33,11 @@ public interface IDeployImagePageModel extends IConnectionAware{
static final String PROPERTY_PROJECT = "project";
static final String PROPERTY_RESOURCE_NAME = "resourceName";
static final String PROPERTY_IMAGE_NAME = "imageName";
+ static final String PROPERTY_PUSH_IMAGE_TO_REGISTRY = "pushImageToRegistry";
+ static final String PROPERTY_TARGET_REGISTRY_LOCATION = "targetRegistryLocation";
+ static final String PROPERTY_TARGET_REGISTRY_USERNAME = "targetRegistryUsername";
+ static final String PROPERTY_TARGET_REGISTRY_PASSWORD = "targetRegistryPassword";
+
/**
*
@@ -110,6 +115,47 @@ public interface IDeployImagePageModel extends IConnectionAware{
*/
List getImageNames();
+
+ /**
+ * @return flag to indicate if the image should be pushed to the Docker registry on OpenShift
+ */
+ boolean isPushImageToRegistry();
+
+ /**
+ * @param pushImageToRegistry flag to indicate if the image should be pushed to the Docker registry on OpenShift
+ */
+ void setPushImageToRegistry(boolean pushImageToRegistry);
+
+ /**
+ * @return the URL to the target registry where the image will be pushed
+ */
+ String getTargetRegistryLocation();
+
+ /**
+ * @param targetRegistryLocation the URL to the target registry where the image will be pushed
+ */
+ void setTargetRegistryLocation(String targetRegistryLocation);
+
+ /**
+ * @return the username to connect to the target registry where the image will be pushed
+ */
+ String getTargetRegistryUsername();
+
+ /**
+ * @param targetRegistryUsername the username to connect to the target registry where the image will be pushed
+ */
+ void setTargetRegistryUsername(String targetRegistryUsername);
+
+ /**
+ * @return the password to connect to the target registry where the image will be pushed
+ */
+ String getTargetRegistryPassword();
+
+ /**
+ * @param targetRegistryPassword the password to connect to the target registry where the image will be pushed
+ */
+ void setTargetRegistryPassword(String targetRegistryPassword);
+
/**
* Initializes the container info from the selected Docker Image.
*
diff --git a/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/wizard/deployimage/ListDockerImagesWizard.java b/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/wizard/deployimage/ListDockerImagesWizard.java
new file mode 100644
index 0000000000..4375dad3af
--- /dev/null
+++ b/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/wizard/deployimage/ListDockerImagesWizard.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2016 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.jboss.tools.openshift.internal.ui.wizard.deployimage;
+
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.linuxtools.docker.core.IDockerConnection;
+
+/**
+ * {@link Wizard} to list and select a Docker Image.
+ */
+public class ListDockerImagesWizard extends Wizard {
+
+ private final ListDockerImagesWizardModel model;
+
+ private final ListDockerImagesWizardPage dockerImagesWizardPage;
+
+ /**
+ * Constructor.
+ * @param dockerConnection the Docker connection
+ * @param filterName the name to use to start filtering images
+ */
+ public ListDockerImagesWizard(final IDockerConnection dockerConnection, final String filterName) {
+ this.model = new ListDockerImagesWizardModel(dockerConnection, filterName);
+ this.dockerImagesWizardPage = new ListDockerImagesWizardPage(this, this.model);
+ setNeedsProgressMonitor(true);
+ }
+
+ @Override
+ public void addPages() {
+ addPage(this.dockerImagesWizardPage);
+ super.addPages();
+ }
+
+ @Override
+ public boolean performFinish() {
+ return true;
+ }
+
+ public String getSelectedImageName() {
+ if(this.model.getSelectedDockerImage() == null) {
+ return null;
+ }
+ return this.model.getSelectedDockerImage().getRepoName() + ':' + this.model.getSelectedDockerImage().getTag();
+ }
+
+
+
+}
diff --git a/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/wizard/deployimage/ListDockerImagesWizardModel.java b/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/wizard/deployimage/ListDockerImagesWizardModel.java
new file mode 100644
index 0000000000..431add2c05
--- /dev/null
+++ b/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/wizard/deployimage/ListDockerImagesWizardModel.java
@@ -0,0 +1,143 @@
+/*******************************************************************************
+ * Copyright (c) 2016 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.jboss.tools.openshift.internal.ui.wizard.deployimage;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.stream.Collectors;
+
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.linuxtools.docker.core.IDockerConnection;
+import org.eclipse.linuxtools.docker.core.IDockerImage;
+import org.jboss.tools.common.databinding.ObservablePojo;
+import org.jboss.tools.openshift.internal.ui.dockerutils.DockerImageUtils;
+
+/**
+ * {@link WizardPage} to list and select a Docker Image.
+ */
+public class ListDockerImagesWizardModel extends ObservablePojo {
+
+ private final IDockerConnection dockerConnection;
+
+ public static final String FILTER_NAME = "filterName";
+
+ public static final String DOCKER_IMAGES = "dockerImages";
+
+ public static final String SELECTED_DOCKER_IMAGE = "selectedDockerImage";
+
+ private String filterName;
+
+ private List dockerImages;
+
+ private DockerImageTag selectedDockerImage;
+
+ public ListDockerImagesWizardModel(final IDockerConnection dockerConnection, final String imageName) {
+ this.dockerConnection = dockerConnection;
+ this.filterName = imageName;
+ }
+
+ public IDockerConnection getDockerConnection() {
+ return this.dockerConnection;
+ }
+
+ public String getFilterName() {
+ return this.filterName;
+ }
+
+ public void setImageName(final String filterName) {
+ firePropertyChange(FILTER_NAME, this.filterName, this.filterName = filterName);
+ }
+
+ public List getDockerImages() {
+ return this.dockerImages;
+ }
+
+ public void setDockerImages(final List dockerImages) {
+ final List topLevelImages = dockerImages.stream()
+ .filter(image -> !image.isDangling() && !image.isIntermediateImage()).collect(Collectors.toList());
+ final List imageTags = new ArrayList<>();
+ for(IDockerImage topLevelImage : topLevelImages) {
+ final Map> repoTags = DockerImageUtils.extractTagsByRepo(topLevelImage.repoTags());
+ for(Entry> entry : repoTags.entrySet()) {
+ final String repo = entry.getKey();
+ final List tags = entry.getValue();
+ for(String tag : tags) {
+ imageTags.add(new DockerImageTag(topLevelImage.id(), repo, tag));
+ }
+ }
+ }
+
+ Collections.sort(imageTags, new Comparator() {
+ @Override
+ public int compare(DockerImageTag image1, DockerImageTag image2) {
+ return image1.getRepoName().compareTo(image2.getRepoName());
+ }
+ });
+ firePropertyChange(DOCKER_IMAGES, this.dockerImages,
+ this.dockerImages = imageTags);
+ }
+
+ public DockerImageTag getSelectedDockerImage() {
+ return this.selectedDockerImage;
+ }
+
+ public void setSelectedDockerImage(final DockerImageTag selectedDockerImage) {
+ firePropertyChange(SELECTED_DOCKER_IMAGE, this.selectedDockerImage,
+ this.selectedDockerImage = selectedDockerImage);
+ }
+
+ static class DockerImageTag {
+
+ /** the corresponding image id. */
+ private final String id;
+
+ /** repository name of the image. */
+ private final String repoName;
+
+ /** the tag for this specific image. */
+ private final String tag;
+
+ public DockerImageTag(final String id, final String repoName, final String tag) {
+ this.id = id.startsWith("sha256:") ? id.substring("sha256:".length(), "sha256:".length() + 12) : id.substring(0, 12);
+ this.repoName = repoName;
+ this.tag = tag;
+ }
+
+ /**
+ * @return the id
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * @return the repoName
+ */
+ public String getRepoName() {
+ return repoName;
+ }
+
+ /**
+ * @return the tag
+ */
+ public String getTag() {
+ return tag;
+ }
+
+ }
+
+}
diff --git a/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/wizard/deployimage/ListDockerImagesWizardPage.java b/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/wizard/deployimage/ListDockerImagesWizardPage.java
new file mode 100644
index 0000000000..2a350754b9
--- /dev/null
+++ b/plugins/org.jboss.tools.openshift.ui/src/org/jboss/tools/openshift/internal/ui/wizard/deployimage/ListDockerImagesWizardPage.java
@@ -0,0 +1,193 @@
+/*******************************************************************************
+ * Copyright (c) 2016 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.jboss.tools.openshift.internal.ui.wizard.deployimage;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.databinding.viewers.ObservableListContentProvider;
+import org.eclipse.jface.databinding.viewers.ViewerProperties;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.viewers.CellLabelProvider;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.jface.wizard.IWizard;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.Text;
+import org.jboss.tools.openshift.internal.common.ui.wizard.AbstractOpenShiftWizardPage;
+import org.jboss.tools.openshift.internal.ui.OpenShiftUIActivator;
+import org.jboss.tools.openshift.internal.ui.wizard.deployimage.ListDockerImagesWizardModel.DockerImageTag;
+
+/**
+ * {@link WizardPage} to list and select a Docker Image.
+ */
+public class ListDockerImagesWizardPage extends AbstractOpenShiftWizardPage {
+
+ /** the model. */
+ private final ListDockerImagesWizardModel model;
+
+ private static String LIST_DOCKER_IMAGES_PAGE_NAME = "List Docker Images Page";
+
+ private static final String PAGE_DESCRIPTION = "This page allows you to choose a local image and the name to be used for the deployed resources.";
+
+ /**
+ * Constructor.
+ *
+ * @param wizard
+ * the parent wizard
+ * @param model
+ * the model
+ */
+ public ListDockerImagesWizardPage(final IWizard wizard, final ListDockerImagesWizardModel model) {
+ super("Deploy an Image", PAGE_DESCRIPTION, LIST_DOCKER_IMAGES_PAGE_NAME, wizard);
+ this.model = model;
+ }
+
+ @Override
+ public boolean isPageComplete() {
+ // can finish if a Docker image was selected
+ return this.model.getSelectedDockerImage() != null;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected void doCreateControls(final Composite parent, final DataBindingContext dbc) {
+ GridLayoutFactory.fillDefaults().margins(10, 10).numColumns(2).applyTo(parent);
+
+ // filter image by name
+ final Label filterByNameLabel = new Label(parent, SWT.SEARCH);
+ filterByNameLabel.setText("Filter:");
+ filterByNameLabel.setToolTipText("Filter images by their name");
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(false, false).applyTo(filterByNameLabel);
+ final Text filterByNameText = new Text(parent, SWT.BORDER);
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false).applyTo(filterByNameText);
+
+ // table with all images
+ final Table dockerImagesTable = new Table(parent,
+ SWT.BORDER | SWT.FULL_SELECTION | SWT.V_SCROLL | SWT.H_SCROLL);
+ final TableViewer dockerImagesTableViewer = new TableViewer(dockerImagesTable);
+ dockerImagesTable.setHeaderVisible(true);
+ dockerImagesTable.setLinesVisible(true);
+ addTableViewerColum(dockerImagesTableViewer, "Name", SWT.NONE, SWT.LEFT, 200, new ColumnLabelProvider() {
+
+ @Override
+ public String getText(final Object element) {
+ return ((DockerImageTag) element).getRepoName();
+ }
+ });
+ addTableViewerColum(dockerImagesTableViewer, "Tag", SWT.NONE, SWT.LEFT, 100, new ColumnLabelProvider() {
+
+ @Override
+ public String getText(final Object element) {
+ return ((DockerImageTag) element).getTag();
+ }
+ });
+ addTableViewerColum(dockerImagesTableViewer, "Image ID", SWT.NONE, SWT.LEFT, 150, new ColumnLabelProvider() {
+
+ @Override
+ public String getText(final Object element) {
+ return ((DockerImageTag) element).getId();
+ }
+ });
+ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).span(2, 1).hint(200, 100)
+ .applyTo(dockerImagesTable);
+
+ // observe the viewer content
+ dockerImagesTableViewer.setContentProvider(new ObservableListContentProvider());
+ // observe the viewer content
+ dockerImagesTableViewer.setInput(BeanProperties
+ .list(ListDockerImagesWizardModel.class, ListDockerImagesWizardModel.DOCKER_IMAGES).observe(model));
+
+ // filter by name
+ final ViewerFilter imageNameFilter = new ViewerFilter() {
+
+ @Override
+ public boolean select(Viewer viewer, Object parentElement, Object element) {
+ return ((DockerImageTag)element).getRepoName().contains(filterByNameText.getText());
+ }
+ };
+ dockerImagesTableViewer.addFilter(imageNameFilter);
+ filterByNameText.addModifyListener(onFilterImages(dockerImagesTableViewer));
+
+ // bind selection
+ dbc.bindValue(ViewerProperties.singleSelection().observe(dockerImagesTableViewer),
+ BeanProperties.value(ListDockerImagesWizardModel.SELECTED_DOCKER_IMAGE).observe(model));
+
+ // load the Docker images
+ try {
+ getContainer().run(true, false, new IRunnableWithProgress() {
+
+ @Override
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ model.setDockerImages(model.getDockerConnection().getImages(true));
+
+ }
+ });
+ } catch (InvocationTargetException | InterruptedException e) {
+ OpenShiftUIActivator.getDefault().getLogger().logError(e);
+ }
+ }
+
+ private static ModifyListener onFilterImages(final TableViewer dockerImagesTableViewer) {
+ return new ModifyListener() {
+
+ @Override
+ public void modifyText(ModifyEvent e) {
+ dockerImagesTableViewer.refresh();
+ }
+ };
+ }
+
+ private static TableViewerColumn addTableViewerColum(final TableViewer tableViewer, final String title,
+ final int style, final int alignment, final int width, final CellLabelProvider columnLabelProvider) {
+ final TableViewerColumn viewerColumn = new TableViewerColumn(tableViewer, style);
+ final TableColumn column = viewerColumn.getColumn();
+ if (title != null) {
+ column.setText(title);
+ }
+ column.setAlignment(alignment);
+ column.setWidth(width);
+ viewerColumn.setLabelProvider(columnLabelProvider);
+ return viewerColumn;
+ }
+
+ static class ImageIDColumnLabelProvider extends ColumnLabelProvider {
+
+ @Override
+ public String getText(final Object element) {
+ return ((DockerImageTag) element).getId();
+ }
+ }
+
+ static class ImageNameColumnLabelProvider extends ColumnLabelProvider {
+
+ @Override
+ public String getText(final Object element) {
+ return ((DockerImageTag) element).getRepoName();
+ }
+ }
+
+}