/*******************************************************************************
* Copyright (c) 2014, 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.ui.wizards;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.beans.BeanProperties;
import org.eclipse.core.databinding.observable.ChangeEvent;
import org.eclipse.core.databinding.observable.IChangeListener;
import org.eclipse.core.databinding.observable.list.IObservableList;
import org.eclipse.core.databinding.observable.list.WritableList;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.observable.value.IValueChangeListener;
import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
import org.eclipse.core.databinding.validation.MultiValidator;
import org.eclipse.core.databinding.validation.ValidationStatus;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.databinding.swt.WidgetProperties;
import org.eclipse.jface.databinding.viewers.ObservableListContentProvider;
import org.eclipse.jface.databinding.viewers.ViewerSupport;
import org.eclipse.jface.databinding.viewers.ViewersObservables;
import org.eclipse.jface.databinding.wizard.WizardPageSupport;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.fieldassist.ComboContentAdapter;
import org.eclipse.jface.fieldassist.ContentProposal;
import org.eclipse.jface.fieldassist.ContentProposalAdapter;
import org.eclipse.jface.fieldassist.ControlDecoration;
import org.eclipse.jface.fieldassist.IContentProposal;
import org.eclipse.jface.fieldassist.IContentProposalProvider;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.wizard.WizardPage;
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.IDockerImageInfo;
import org.eclipse.linuxtools.docker.core.IDockerImageSearchResult;
import org.eclipse.linuxtools.docker.ui.Activator;
import org.eclipse.linuxtools.internal.docker.ui.SWTImagesFactory;
import org.eclipse.linuxtools.internal.docker.ui.commands.CommandUtils;
import org.eclipse.linuxtools.internal.docker.ui.utils.IRunnableWithResult;
import org.eclipse.linuxtools.internal.docker.ui.views.DVMessages;
import org.eclipse.linuxtools.internal.docker.ui.views.ImagePullProgressHandler;
import org.eclipse.linuxtools.internal.docker.ui.wizards.ImageRunSelectionModel.ContainerLinkModel;
import org.eclipse.linuxtools.internal.docker.ui.wizards.ImageRunSelectionModel.ExposedPortModel;
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.Point;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.Text;
/**
* A {@link WizardPage} to let the user select the {@link IDockerImage} to run
* and select the most common arguments (container name, port settings, etc.)
*
* @author xcoulon
*
*/
public class ImageRunSelectionPage extends WizardPage {
private static final int INDENT = 30;
private static final String ERROR_PULLING_IMAGE = "ImagePullError.msg"; //$NON-NLS-1$
private final DataBindingContext dbc = new DataBindingContext();
private final ImageRunSelectionModel model;
private CheckboxTableViewer exposedPortsTableViewer;
private static final int COLUMNS = 3;
/**
* Default constructor.
*
* @param selectedImage
* the {@link IDockerImage} to run
*
*/
public ImageRunSelectionPage(final IDockerImage selectedImage) {
super("ImageSelectionPage", "Docker Container settings", //$NON-NLS-1$ //$NON-NLS-2$
SWTImagesFactory.DESC_BANNER_REPOSITORY);
setMessage("Run a Docker Image"); //$NON-NLS-1$
setPageComplete(true);
this.model = new ImageRunSelectionModel(selectedImage);
}
/**
* Default constructor.
*
* @param selectedConnection
* the {@link IDockerConnection} to run
*
*/
public ImageRunSelectionPage(final IDockerConnection selectedConnection) {
super("ImageSelectionPage", //$NON-NLS-1$
"Image Selection and Exposed Port Publishing", //$NON-NLS-1$
SWTImagesFactory.DESC_BANNER_REPOSITORY);
setMessage("Select the Docker Image to run and the ports to expose"); //$NON-NLS-1$
setPageComplete(false);
this.model = new ImageRunSelectionModel(selectedConnection);
}
/**
* @return the {@link ImageRunSelectionModel} model associated
*/
public ImageRunSelectionModel getModel() {
return model;
}
@Override
public void dispose() {
dbc.dispose();
super.dispose();
}
@Override
public void createControl(final Composite parent) {
final Composite container = new Composite(parent, SWT.NONE);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).span(1, 1)
.grab(true, false).applyTo(container);
GridLayoutFactory.fillDefaults().numColumns(COLUMNS).margins(6, 6)
.applyTo(container);
createImageSettingsSection(container);
createSectionSeparator(container, true);
createPortSettingsSection(container);
// addSectionSeparator(container, false);
createLinkSettingsSection(container);
// addSectionSeparator(container, false);
createRunOptionsSection(container);
// Observe model changes to propagate to the UI via listeners.
final IObservableValue imageSelectionObservable = BeanProperties
.value(ImageRunSelectionModel.class,
ImageRunSelectionModel.SELECTED_IMAGE_NAME)
.observe(model);
imageSelectionObservable
.addValueChangeListener(onImageSelectionChange());
setDefaultValues();
// setup validation support
WizardPageSupport.create(this, dbc);
// set validation
final ImageSelectionValidator imageSelectionValidator = new ImageSelectionValidator(
imageSelectionObservable);
dbc.addValidationStatusProvider(imageSelectionValidator);
//
setControl(container);
}
private void createSectionSeparator(final Composite container,
final boolean separator) {
final int SECTION_INDENT = 10;
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.span(COLUMNS, 1).grab(true, false)
.indent(SWT.DEFAULT, SECTION_INDENT)
.applyTo(new Label(container, separator
? (SWT.SEPARATOR | SWT.HORIZONTAL) : SWT.NONE));
}
/**
* Creates the {@link Composite} container that will display widgets to
* select an {@link IDockerImage}, name it and specify the command to run.
*
* @param container
* the parent {@link Composite}
*/
private void createImageSettingsSection(final Composite container) {
// Image selection name
final Label imageSelectionLabel = new Label(container, SWT.NONE);
imageSelectionLabel.setText("Image:"); //$NON-NLS-1$
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.grab(false, false).applyTo(imageSelectionLabel);
final Combo imageSelectionCombo = new Combo(container, SWT.BORDER);
final ComboViewer imageSelectionComboViewer = new ComboViewer(
imageSelectionCombo);
imageSelectionCombo.setToolTipText("Select the Docker Image to run"); //$NON-NLS-1$
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.grab(true, false).span(1, 1).applyTo(imageSelectionCombo);
new ControlDecoration(imageSelectionCombo, SWT.TOP | SWT.LEFT);
new ContentProposalAdapter(imageSelectionCombo,
new ComboContentAdapter() {
@Override
public void insertControlContents(Control control,
String text, int cursorPosition) {
final Combo combo = (Combo) control;
final Point selection = combo.getSelection();
combo.setText(text);
selection.x = text.length();
selection.y = selection.x;
combo.setSelection(selection);
}
}, getImageNameContentProposalProvider(imageSelectionCombo),
null, null);
// image search
final Button searchImageButton = new Button(container, SWT.NONE);
searchImageButton.setText("Search..."); //$NON-NLS-1$
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.grab(false, false).span(1, 1).applyTo(searchImageButton);
searchImageButton.addSelectionListener(onSearchImage());
// link to pull image
final Label fillerLabel = new Label(container, SWT.NONE);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.grab(false, false).span(1, 1).applyTo(fillerLabel);
final Link pullImageLink = new Link(container, SWT.NONE);
pullImageLink.setText("Pull this image...");
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.grab(true, false).span(COLUMNS - 1, 1).applyTo(pullImageLink);
pullImageLink.addSelectionListener(onPullImage());
dbc.bindValue(WidgetProperties.enabled().observe(pullImageLink),
BeanProperties
.value(ImageRunSelectionModel.class,
ImageRunSelectionModel.SELECTED_IMAGE_NEEDS_PULLING)
.observe(model));
// bind combo with model (for values and selection)
imageSelectionComboViewer
.setContentProvider(new ObservableListContentProvider());
dbc.bindList(WidgetProperties.items().observe(imageSelectionCombo),
BeanProperties
.list(ImageRunSelectionModel.class,
ImageRunSelectionModel.IMAGE_NAMES)
.observe(model));
dbc.bindValue(WidgetProperties.selection().observe(imageSelectionCombo),
BeanProperties
.value(ImageRunSelectionModel.class,
ImageRunSelectionModel.SELECTED_IMAGE_NAME)
.observe(model));
// Container name (optional)
final Label containerNameLabel = new Label(container, SWT.NONE);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.grab(false, false).applyTo(imageSelectionLabel);
containerNameLabel.setText("Name:"); //$NON-NLS-1$
final Text containerNameText = new Text(container, SWT.BORDER);
containerNameText.setToolTipText(
"a UUID long identifier, a UUID short identifier or a String"); //$NON-NLS-1$
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.grab(true, false).span(1, 1).applyTo(containerNameText);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.grab(false, false).span(1, 1)
.applyTo(new Label(container, SWT.NONE));
dbc.bindValue(
WidgetProperties.text(SWT.Modify).observe(containerNameText),
BeanProperties
.value(ImageRunSelectionModel.class,
ImageRunSelectionModel.CONTAINER_NAME)
.observe(model));
// EntryPoint (optional)
final Label entrypointLabel = new Label(container, SWT.NONE);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.grab(false, false).applyTo(imageSelectionLabel);
entrypointLabel.setText("Entrypoint:"); //$NON-NLS-1$
// TODO: include SWT.SEARCH | SWT.ICON_SEARCH to support value reset
final Text entrypointText = new Text(container, SWT.BORDER);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.grab(true, false).span(1, 1).applyTo(entrypointText);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.grab(false, false).span(1, 1)
.applyTo(new Label(container, SWT.NONE));
dbc.bindValue(WidgetProperties.text(SWT.Modify).observe(entrypointText),
BeanProperties
.value(ImageRunSelectionModel.class,
ImageRunSelectionModel.ENTRYPOINT)
.observe(model));
// Command (optional)
final Label commandLabel = new Label(container, SWT.NONE);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.grab(false, false).applyTo(imageSelectionLabel);
commandLabel.setText("Command:"); //$NON-NLS-1$
final Text commandText = new Text(container, SWT.BORDER);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.grab(true, false).span(1, 1).applyTo(commandText);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.grab(false, false).span(1, 1)
.applyTo(new Label(container, SWT.NONE));
dbc.bindValue(WidgetProperties.text(SWT.Modify).observe(commandText),
BeanProperties.value(ImageRunSelectionModel.class,
ImageRunSelectionModel.COMMAND).observe(model));
}
private void createPortSettingsSection(final Composite container) {
final Button publishAllPortsButton = new Button(container, SWT.CHECK);
publishAllPortsButton.setText(
"Publish all exposed ports to random ports on the host interfaces"); //$NON-NLS-1$
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.span(COLUMNS, 1).grab(true, false)
.applyTo(publishAllPortsButton);
dbc.bindValue(
WidgetProperties.selection().observe(publishAllPortsButton),
BeanProperties
.value(ImageRunSelectionModel.class,
ImageRunSelectionModel.PUBLISH_ALL_PORTS)
.observe(model));
// specify ports
final Label portSettingsLabel = new Label(container, SWT.NONE);
portSettingsLabel.setText(
"Only publish the selected container ports below to the host:");
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.grab(true, false).span(COLUMNS, 1).indent(INDENT, 0)
.applyTo(portSettingsLabel);
exposedPortsTableViewer = createPortSettingsTable(container);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP)
.grab(true, false).span(COLUMNS - 1, 1).indent(INDENT, 0)
.hint(200, 70).applyTo(exposedPortsTableViewer.getTable());
// buttons
final Composite buttonsContainers = new Composite(container, SWT.NONE);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP)
.grab(false, false).applyTo(buttonsContainers);
GridLayoutFactory.fillDefaults().numColumns(1).margins(0, 0)
.spacing(SWT.DEFAULT, 0).applyTo(buttonsContainers);
final Button addButton = new Button(buttonsContainers, SWT.NONE);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP)
.grab(true, false).applyTo(addButton);
addButton.setText("Add"); //$NON-NLS-1$
addButton.addSelectionListener(onAddPort());
final Button editButton = new Button(buttonsContainers, SWT.NONE);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP)
.grab(true, false).applyTo(editButton);
editButton.setText("Edit..."); //$NON-NLS-1$
editButton.setEnabled(false);
editButton.addSelectionListener(onEditPort(exposedPortsTableViewer));
final Button removeButton = new Button(buttonsContainers, SWT.NONE);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP)
.grab(true, false).applyTo(removeButton);
removeButton.setText("Remove"); //$NON-NLS-1$
removeButton
.addSelectionListener(onRemovePorts(exposedPortsTableViewer));
BeanProperties
.value(ImageRunSelectionModel.class,
ImageRunSelectionModel.PUBLISH_ALL_PORTS)
.observe(model)
.addValueChangeListener(onPublishAllPortsChange(
exposedPortsTableViewer.getTable(), addButton,
removeButton));
ViewerSupport.bind(exposedPortsTableViewer, model.getExposedPorts(),
BeanProperties.values(ExposedPortModel.class,
new String[] { ExposedPortModel.CONTAINER_PORT,
ExposedPortModel.PORT_TYPE,
ExposedPortModel.HOST_ADDRESS,
ExposedPortModel.HOST_PORT }));
dbc.bindSet(
ViewersObservables.observeCheckedElements(
exposedPortsTableViewer, ExposedPortModel.class),
BeanProperties.set(ImageRunSelectionModel.SELECTED_PORTS)
.observe(model));
togglePortMappingControls(exposedPortsTableViewer.getTable(), addButton,
removeButton);
}
private CheckboxTableViewer createPortSettingsTable(
final Composite container) {
final Table table = new Table(container, SWT.BORDER | SWT.FULL_SELECTION
| SWT.V_SCROLL | SWT.H_SCROLL | SWT.CHECK);
final CheckboxTableViewer tableViewer = new CheckboxTableViewer(table);
table.setHeaderVisible(true);
table.setLinesVisible(true);
createTableViewerColum(tableViewer, "Container Port", //$NON-NLS-1$
100);
createTableViewerColum(tableViewer, "Type", //$NON-NLS-1$
50);
createTableViewerColum(tableViewer, "Host Address", //$NON-NLS-1$
100);
createTableViewerColum(tableViewer, "Host Port", //$NON-NLS-1$
100);
tableViewer.setContentProvider(new ObservableListContentProvider());
return tableViewer;
}
private TableViewerColumn createTableViewerColum(
final TableViewer tableViewer, final String title,
final int width) {
final TableViewerColumn viewerColumn = new TableViewerColumn(
tableViewer, SWT.NONE);
final TableColumn column = viewerColumn.getColumn();
if (title != null) {
column.setText(title);
}
column.setWidth(width);
return viewerColumn;
}
private void createLinkSettingsSection(final Composite container) {
final Label linksLabel = new Label(container, SWT.NONE);
linksLabel.setText("Links to other containers:");
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.grab(false, false).span(COLUMNS, 1).applyTo(linksLabel);
final TableViewer linksTableViewer = createLinksTable(container);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP).grab(true, true)
.span(COLUMNS - 1, 1).hint(200, 50).indent(INDENT, SWT.DEFAULT)
.applyTo(linksTableViewer.getTable());
// buttons
final Composite buttonsContainers = new Composite(container, SWT.NONE);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP)
.grab(false, false).applyTo(buttonsContainers);
GridLayoutFactory.fillDefaults().numColumns(1).margins(0, 0)
.spacing(SWT.DEFAULT, 0).applyTo(buttonsContainers);
final Button addButton = new Button(buttonsContainers, SWT.NONE);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP)
.grab(true, false).applyTo(addButton);
addButton.setText("Add..."); //$NON-NLS-1$
addButton.addSelectionListener(onAddLink());
final Button editButton = new Button(buttonsContainers, SWT.NONE);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP)
.grab(true, false).applyTo(editButton);
editButton.setText("Edit..."); //$NON-NLS-1$
editButton.setEnabled(false);
editButton.addSelectionListener(onEditLink(linksTableViewer));
final Button removeButton = new Button(buttonsContainers, SWT.NONE);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP)
.grab(true, false).applyTo(removeButton);
removeButton.setText("Remove"); //$NON-NLS-1$
removeButton.addSelectionListener(onRemoveLinks(linksTableViewer));
removeButton.setEnabled(false);
ViewerSupport
.bind(linksTableViewer, model.getLinks(),
BeanProperties.values(ContainerLinkModel.class,
new String[] {
ContainerLinkModel.CONTAINER_NAME,
ContainerLinkModel.CONTAINER_ALIAS }));
linksTableViewer.addSelectionChangedListener(
onLinkSelected(editButton, removeButton));
// disable the edit and removeButton if the table is empty
model.getLinks()
.addChangeListener(onRemoveLinks(editButton, removeButton));
}
private ISelectionChangedListener onLinkSelected(
final Control... controls) {
return new ISelectionChangedListener() {
@Override
public void selectionChanged(final SelectionChangedEvent e) {
if (e.getSelection().isEmpty()) {
setControlsEnabled(controls, false);
} else {
setControlsEnabled(controls, true);
}
}
};
}
private TableViewer createLinksTable(final Composite container) {
final Table table = new Table(container,
SWT.BORDER | SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL);
final TableViewer tableViewer = new TableViewer(table);
table.setHeaderVisible(true);
table.setLinesVisible(true);
createTableViewerColum(tableViewer, "Container Name", //$NON-NLS-1$
200);
createTableViewerColum(tableViewer, "Alias", //$NON-NLS-1$
150);
return tableViewer;
}
private SelectionListener onAddLink() {
return new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
final ContainerLinkDialog dialog = new ContainerLinkDialog(
getShell(), model.getSelectedConnection());
dialog.create();
if (dialog.open() == IDialogConstants.OK_ID) {
model.addLink(dialog.getContainerName(),
dialog.getContainerAlias());
}
}
};
}
private SelectionListener onEditLink(final TableViewer linksTableViewer) {
return new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
final IStructuredSelection selection = linksTableViewer
.getStructuredSelection();
final ContainerLinkModel selectedContainerLink = (ContainerLinkModel) selection
.getFirstElement();
final ContainerLinkDialog dialog = new ContainerLinkDialog(
getShell(), model.getSelectedConnection(),
selectedContainerLink);
dialog.create();
if (dialog.open() == IDialogConstants.OK_ID) {
selectedContainerLink
.setContainerName(dialog.getContainerName());
selectedContainerLink
.setContainerAlias(dialog.getContainerAlias());
linksTableViewer.refresh();
}
}
};
}
private IChangeListener onRemoveLinks(final Button... buttons) {
return new IChangeListener() {
@Override
public void handleChange(final ChangeEvent event) {
@SuppressWarnings("unchecked")
final List links = (List) event
.getSource();
if (links.isEmpty()) {
setControlsEnabled(buttons, false);
}
}
};
}
private SelectionListener onRemoveLinks(
final TableViewer linksTableViewer) {
return new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent e) {
final IStructuredSelection selection = linksTableViewer
.getStructuredSelection();
for (@SuppressWarnings("unchecked")
Iterator iterator = selection
.iterator(); iterator.hasNext();) {
model.removeLink(iterator.next());
}
}
};
}
private void createRunOptionsSection(final Composite container) {
// interactive/show in console mode
final Button interactiveButton = new Button(container, SWT.CHECK);
interactiveButton.setText(
"Keep STDIN open to Console even if not attached (-i)"); //$NON-NLS-1$
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.span(COLUMNS, 1).grab(true, false).applyTo(interactiveButton);
dbc.bindValue(WidgetProperties.selection().observe(interactiveButton),
BeanProperties
.value(ImageRunSelectionModel.class,
ImageRunSelectionModel.INTERACTIVE_MODE)
.observe(model));
// allocate pseudo-TTY
final Button allocatePseudoTTY = new Button(container, SWT.CHECK);
allocatePseudoTTY.setText("Allocate pseudo-TTY from Console (-t)"); //$NON-NLS-1$
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.span(COLUMNS, 1).grab(true, false).applyTo(allocatePseudoTTY);
dbc.bindValue(WidgetProperties.selection().observe(allocatePseudoTTY),
BeanProperties
.value(ImageRunSelectionModel.class,
ImageRunSelectionModel.ALLOCATE_PSEUDO_TTY)
.observe(model));
// remove when exits
final Button removeWhenExitsButton = new Button(container, SWT.CHECK);
removeWhenExitsButton.setText(
"Automatically remove the container when it exits (--rm)"); //$NON-NLS-1$
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.span(COLUMNS, 1).grab(true, false)
.applyTo(removeWhenExitsButton);
dbc.bindValue(
WidgetProperties.selection().observe(removeWhenExitsButton),
BeanProperties
.value(ImageRunSelectionModel.class,
ImageRunSelectionModel.REMOVE_WHEN_EXITS)
.observe(model));
}
/**
* Creates an {@link IContentProposalProvider} to propose
* {@link IDockerImage} names based on the current text.
*
* @param items
* @return
*/
private IContentProposalProvider getImageNameContentProposalProvider(
final Combo imageSelectionCombo) {
return new IContentProposalProvider() {
@Override
public IContentProposal[] getProposals(final String contents,
final int position) {
final List proposals = new ArrayList<>();
for (String imageName : imageSelectionCombo.getItems()) {
if (imageName.contains(contents)) {
proposals.add(new ContentProposal(imageName, imageName,
imageName, position));
}
}
return proposals.toArray(new IContentProposal[0]);
}
};
}
private IValueChangeListener onImageSelectionChange() {
return new IValueChangeListener() {
@Override
public void handleValueChange(final ValueChangeEvent event) {
final IDockerImage selectedImage = model.getSelectedImage();
// skip if the selected image does not exist in the local Docker
// host
if (selectedImage == null) {
model.setExposedPorts(new WritableList());
return;
}
findImageInfo(selectedImage);
}
};
}
private SelectionListener onPullImage() {
return new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent e) {
pullSelectedImage();
}
};
}
private IValueChangeListener onPublishAllPortsChange(
final Control... controls) {
return new IValueChangeListener() {
@Override
public void handleValueChange(final ValueChangeEvent event) {
togglePortMappingControls(controls);
}
};
}
private SelectionListener onSearchImage() {
return new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
final ImageSearch imageSearchWizard = new ImageSearch(
ImageRunSelectionPage.this.model
.getSelectedConnection());
final boolean completed = CommandUtils
.openWizard(imageSearchWizard, getShell());
if (completed) {
final IDockerImageSearchResult selectedSearchImage = imageSearchWizard
.getSelectedImage();
if (selectedSearchImage.getName().contains(":")) {
model.setSelectedImageName(
selectedSearchImage.getName());
} else {
// assume tag is 'latest'
model.setSelectedImageName(
selectedSearchImage.getName() + ":latest"); //$NON-NLS-1$
}
}
}
};
}
private SelectionListener onAddPort() {
return new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent e) {
final ContainerPortDialog dialog = new ContainerPortDialog(
getShell());
dialog.create();
if (dialog.open() == IDialogConstants.OK_ID) {
model.addAvailablePort(dialog.getPort());
}
}
};
}
private SelectionListener onEditPort(final TableViewer portsTableViewer) {
return new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent e) {
final IStructuredSelection selection = portsTableViewer
.getStructuredSelection();
final ExposedPortModel selectedContainerPort = (ExposedPortModel) selection
.getFirstElement();
final ContainerPortDialog dialog = new ContainerPortDialog(
getShell(), selectedContainerPort);
dialog.create();
if (dialog.open() == IDialogConstants.OK_ID) {
final ExposedPortModel configuredPort = dialog.getPort();
selectedContainerPort.setContainerPort(
configuredPort.getContainerPort());
selectedContainerPort
.setHostAddress(configuredPort.getHostAddress());
selectedContainerPort
.setHostPort(configuredPort.getHostPort());
portsTableViewer.refresh();
}
}
};
}
private SelectionListener onRemovePorts(
final TableViewer portsTableViewer) {
return new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent e) {
final IStructuredSelection selection = portsTableViewer
.getStructuredSelection();
for (@SuppressWarnings("unchecked")
Iterator iterator = selection
.iterator(); iterator.hasNext();) {
model.removeAvailablePort(iterator.next());
}
}
};
}
private void setDefaultValues() {
final IDockerImage selectedImage = model.getSelectedImage();
if (selectedImage == null) {
return;
}
findImageInfo(selectedImage);
}
private void findImageInfo(final IDockerImage selectedImage) {
try {
final FindImageInfoRunnable findImageInfoRunnable = new FindImageInfoRunnable(
selectedImage);
getContainer().run(true, true, findImageInfoRunnable);
final IDockerImageInfo selectedImageInfo = findImageInfoRunnable
.getResult();
final Set exposedPorts = selectedImageInfo.config()
.exposedPorts();
final WritableList availablePorts = new WritableList();
if (exposedPorts != null) {
for (String exposedPort : exposedPorts) {
final String privatePort = exposedPort.substring(0,
exposedPort.indexOf('/'));
final String type = exposedPort
.substring(exposedPort.indexOf('/'));
availablePorts.add(
new ExposedPortModel(privatePort, type, "", ""));
}
}
model.setExposedPorts(availablePorts);
model.setCommand(selectedImageInfo.config().cmd());
model.setEntrypoint(selectedImageInfo.config().entrypoint());
} catch (InvocationTargetException | InterruptedException e) {
Activator.log(e);
}
}
private void togglePortMappingControls(final Control... controls) {
for (Control control : controls) {
if (model.isPublishAllPorts()) {
control.setEnabled(false);
} else {
control.setEnabled(true);
}
}
}
private void pullSelectedImage() {
try {
getContainer().run(true, true, new IRunnableWithProgress() {
@Override
public void run(final IProgressMonitor monitor)
throws InterruptedException {
final IDockerConnection connection = model
.getSelectedConnection();
final String imageName = model.getSelectedImageName();
monitor.beginTask("Pulling image '" + imageName + "'", 1);
try {
connection.pullImage(imageName,
new ImagePullProgressHandler(connection,
imageName));
} catch (final DockerException e) {
Display.getDefault().syncExec(new Runnable() {
@Override
public void run() {
MessageDialog.openError(
Display.getCurrent().getActiveShell(),
DVMessages.getFormattedString(
ERROR_PULLING_IMAGE, imageName),
e.getMessage());
}
});
} finally {
monitor.done();
// refresh the widgets
model.refreshImageNames();
if (model.getImageNames().contains(imageName)) {
model.setSelectedImageName(imageName);
}
}
}
});
} catch (InvocationTargetException | InterruptedException e) {
Activator.log(e);
}
}
private static void setControlsEnabled(final Control[] controls,
final boolean enabled) {
for (Control control : controls) {
control.setEnabled(enabled);
}
}
private static final class FindImageInfoRunnable
implements IRunnableWithResult {
private final IDockerImage selectedImage;
private IDockerImageInfo selectedImageInfo;
private FindImageInfoRunnable(IDockerImage selectedImage) {
this.selectedImage = selectedImage;
}
@Override
public void run(final IProgressMonitor monitor) {
selectedImageInfo = selectedImage.getConnection()
.getImageInfo(selectedImage.id());
}
@Override
public IDockerImageInfo getResult() {
return selectedImageInfo;
}
}
private class ImageSelectionValidator extends MultiValidator {
private final IObservableValue imageSelectionObservable;
ImageSelectionValidator(
final IObservableValue imageSelectionObservable) {
this.imageSelectionObservable = imageSelectionObservable;
}
@Override
protected IStatus validate() {
final String selectedImageName = (String) imageSelectionObservable
.getValue();
if (selectedImageName.isEmpty()) {
model.setSelectedImageNeedsPulling(false);
return ValidationStatus
.error("Please specify the image to run."); //$NON-NLS-1$
}
if (model.getSelectedImage() != null) {
model.setSelectedImageNeedsPulling(false);
return ValidationStatus.ok();
}
model.setSelectedImageNeedsPulling(true);
return ValidationStatus.warning("Image named '" + selectedImageName
+ "' does not exist locally. Click on the link under the 'Image' combo to start pulling it."); //$NON-NLS-1$
}
@Override
public IObservableList getTargets() {
WritableList targets = new WritableList();
targets.add(imageSelectionObservable);
return targets;
}
}
}