1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-21 21:52:10 +02:00

Bug 540423 - C/C++ Container Launcher missing Port Binding

- add new ContainerPortDialog class to allow user to specify
  ports in the launch configuration Container tab
- add new ContainerTabModel and ExposedPortModel classes to
  support new functionality
- add new Ports group to ContainerTab and have a table
  where a user can add, edit, remove, and select ports
  for publishing to the host
- add new attribute ATTR_EXPOSED_PORTS to ILaunchConstants for
  saving and restoring user selected ports in C launch
  configuration
- add needed internal messages to support new port settings
  functionality
- bump up org.eclipse.cdt.docker.launcher version

Change-Id: I93b7503bdc141e3077418800352507ef38e65ab1
This commit is contained in:
Jeff Johnston 2018-11-01 18:19:44 -04:00
parent f9c8f0da02
commit 331be6c45c
8 changed files with 917 additions and 6 deletions

View file

@ -15,7 +15,7 @@ import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -223,6 +223,39 @@ public class ContainerLaunchConfigurationDelegate extends GdbLaunchDelegate
}
additionalDirs = dirs;
}
List<String> ports = new ArrayList<>();
List<String> portInfos = configuration.getAttribute(
ILaunchConstants.ATTR_EXPOSED_PORTS,
Collections.emptyList());
for (String portInfo : portInfos) {
ExposedPortModel m = ExposedPortModel
.createPortModel(portInfo);
if (m.getSelected()) {
StringBuilder b1 = new StringBuilder();
if (m.getHostAddress() != null
&& !m.getHostAddress().isEmpty()) {
b1.append(m.getHostAddress());
b1.append(":"); //$NON-NLS-1$
}
if (m.getHostPort() != null
&& !m.getHostPort().isEmpty()) {
b1.append(m.getHostPort());
}
// regardless if we have a host port or not,
// we may need to add a separator so we can determine
// the case where we don't have a host port vs where we
// don't have a host address
if (b1.length() > 0) {
b1.append(":"); //$NON-NLS-1$
}
String containerPort = m.getContainerPort() + "/" //$NON-NLS-1$
+ m.getPortType();
b1.append(containerPort);
ports.add(b1.toString());
}
}
String image = configuration.getAttribute(
ILaunchConstants.ATTR_IMAGE, (String) null);
String connectionUri = configuration.getAttribute(
@ -240,14 +273,49 @@ public class ContainerLaunchConfigurationDelegate extends GdbLaunchDelegate
connectionUri,
image, command,
commandDir, workingDir, additionalDirs, origEnv,
envMap, null, keepContainer, supportStdin,
envMap, ports.isEmpty() ? null : ports, keepContainer,
supportStdin,
privilegedMode, labels);
} else if (mode.equals(ILaunchManager.DEBUG_MODE)) {
String gdbserverPortNumber = configuration.getAttribute(
ILaunchConstants.ATTR_GDBSERVER_PORT,
ILaunchConstants.ATTR_GDBSERVER_PORT_DEFAULT);
List<String> ports = Arrays
.asList(gdbserverPortNumber + "/tcp"); //$NON-NLS-1$
List<String> ports = new ArrayList<>();
List<String> portInfos = configuration.getAttribute(
ILaunchConstants.ATTR_EXPOSED_PORTS, Collections.emptyList());
String gdbserverPort = gdbserverPortNumber + "/tcp"; //$NON-NLS-1$
boolean gdbserverPortSpecified = false;
for (String portInfo : portInfos) {
ExposedPortModel m = ExposedPortModel.createPortModel(portInfo);
if (m.getSelected()) {
StringBuilder b = new StringBuilder();
if (m.getHostAddress() != null && !m.getHostAddress().isEmpty()) {
b.append(m.getHostAddress());
b.append(":"); //$NON-NLS-1$
}
if (m.getHostPort() != null && !m.getHostPort().isEmpty()) {
b.append(m.getHostPort());
}
// regardless if we have a host port or not,
// we may need to add a separator so we can determine
// the case where we don't have a host port vs where we
// don't have a host address
if (b.length() > 0) {
b.append(":"); //$NON-NLS-1$
}
String containerPort = m.getContainerPort() + "/" + m.getPortType(); //$NON-NLS-1$
b.append(containerPort);
if (gdbserverPort.equals(containerPort)) {
gdbserverPortSpecified = true;
}
ports.add(b.toString());
}
}
// if user hasn't already specified gdbserver port, we need to add it by default
if (!gdbserverPortSpecified) {
ports.add(gdbserverPortNumber + "/tcp"); //$NON-NLS-1$
}
String gdbserverCommand = configuration.getAttribute(
ILaunchConstants.ATTR_GDBSERVER_COMMAND,
ILaunchConstants.ATTR_GDBSERVER_COMMAND_DEFAULT);

View file

@ -0,0 +1,230 @@
/*******************************************************************************
* Copyright (c) 2015, 2018 Red Hat Inc. and others.
* 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.cdt.internal.docker.launcher;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.beans.BeanProperties;
import org.eclipse.core.databinding.observable.value.IValueChangeListener;
import org.eclipse.jface.databinding.swt.ISWTObservableValue;
import org.eclipse.jface.databinding.swt.WidgetProperties;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
/**
* @author xcoulon
*
*/
public class ContainerPortDialog extends Dialog {
private static final String PORT_TYPE = "tcp"; //$NON-NLS-1$
private final ContainerPortDialogModel model;
private final DataBindingContext dbc = new DataBindingContext();
public ContainerPortDialog(final Shell parentShell) {
super(parentShell);
this.model = new ContainerPortDialogModel();
}
public ContainerPortDialog(final Shell parentShell,
final ExposedPortModel selectedContainerPort) {
super(parentShell);
this.model = new ContainerPortDialogModel(
selectedContainerPort.getContainerPort(),
selectedContainerPort.getHostAddress(),
selectedContainerPort.getHostPort());
}
@Override
protected void configureShell(final Shell shell) {
super.configureShell(shell);
setShellStyle(getShellStyle() | SWT.RESIZE);
shell.setText(Messages.ContainerPortDialog_shellTitle);
}
/**
* Disable the 'OK' button by default
*/
@Override
protected Button createButton(Composite parent, int id, String label,
boolean defaultButton) {
final Button button = super.createButton(parent, id, label,
defaultButton);
if (id == IDialogConstants.OK_ID) {
button.setEnabled(false);
}
return button;
}
@Override
protected Point getInitialSize() {
return new Point(400, super.getInitialSize().y);
}
@SuppressWarnings("unchecked")
@Override
protected Control createDialogArea(Composite parent) {
final int COLUMNS = 2;
final Composite container = new Composite(parent, SWT.NONE);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL)
.span(COLUMNS, 1).grab(true, true).applyTo(container);
GridLayoutFactory.fillDefaults().numColumns(COLUMNS).margins(10, 10)
.applyTo(container);
final Label explanationLabel = new Label(container, SWT.NONE);
explanationLabel.setText(Messages.ContainerPortDialog_explanationLabel);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.span(COLUMNS, 1).grab(false, false).applyTo(explanationLabel);
final Label containerLabel = new Label(container, SWT.NONE);
containerLabel.setText(Messages.ContainerPortDialog_containerLabel);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.grab(false, false).applyTo(containerLabel);
final Text containerPortText = new Text(container, SWT.BORDER);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.grab(true, false).applyTo(containerPortText);
final Label hostAddressLabel = new Label(container, SWT.NONE);
hostAddressLabel.setText(Messages.ContainerPortDialog_hostAddressLabel);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.grab(false, false).applyTo(hostAddressLabel);
final Text hostAddressText = new Text(container, SWT.BORDER);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.grab(true, false).applyTo(hostAddressText);
final Label hostPortLabel = new Label(container, SWT.NONE);
hostPortLabel.setText(
Messages.ContainerPortDialog_hostPortLabel);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.grab(false, false).applyTo(hostPortLabel);
final Text hostPortText = new Text(container, SWT.BORDER);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.grab(true, false).applyTo(hostPortText);
// error message
final Label errorMessageLabel = new Label(container, SWT.NONE);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.span(COLUMNS, 1).grab(true, false).applyTo(errorMessageLabel);
// listening to changes
final ISWTObservableValue containerPortObservable = WidgetProperties
.text(SWT.Modify).observe(containerPortText);
dbc.bindValue(containerPortObservable,
BeanProperties
.value(ContainerPortDialogModel.class,
ContainerPortDialogModel.CONTAINER_PORT)
.observe(model));
final ISWTObservableValue hostAddressObservable = WidgetProperties
.text(SWT.Modify).observe(hostAddressText);
dbc.bindValue(hostAddressObservable,
BeanProperties
.value(ContainerPortDialogModel.class,
ContainerPortDialogModel.HOST_ADDRESS)
.observe(model));
final ISWTObservableValue hostPortObservable = WidgetProperties
.text(SWT.Modify).observe(hostPortText);
dbc.bindValue(hostPortObservable,
BeanProperties
.value(ContainerPortDialogModel.class,
ContainerPortDialogModel.HOST_PORT)
.observe(model));
containerPortObservable.addValueChangeListener(
onContainerPortSettingsChanged());
hostPortObservable.addValueChangeListener(
onContainerPortSettingsChanged());
hostAddressObservable.addValueChangeListener(
onContainerPortSettingsChanged());
return container;
}
private IValueChangeListener<?> onContainerPortSettingsChanged() {
return event -> validateInput();
}
private void validateInput() {
final String containerPort = model.getContainerPort();
if (containerPort == null || containerPort.isEmpty()) {
setOkButtonEnabled(false);
} else {
setOkButtonEnabled(true);
}
}
private void setOkButtonEnabled(final boolean enabled) {
getButton(IDialogConstants.OK_ID).setEnabled(enabled);
}
public ExposedPortModel getPort() {
return new ExposedPortModel(model.getContainerPort(), PORT_TYPE,
model.getHostAddress(), model.getHostPort());
}
class ContainerPortDialogModel extends BaseDatabindingModel {
public static final String CONTAINER_PORT = "containerPort"; //$NON-NLS-1$
public static final String HOST_ADDRESS = "hostAddress"; //$NON-NLS-1$
public static final String HOST_PORT = "hostPort"; //$NON-NLS-1$
private String containerPort;
private String hostAddress;
private String hostPort;
public ContainerPortDialogModel() {
}
public ContainerPortDialogModel(final String containerPort,
final String hostAddress, final String hostPort) {
this.containerPort = containerPort;
this.hostAddress = hostAddress;
this.hostPort = hostPort;
}
public String getContainerPort() {
return containerPort;
}
public void setContainerPort(final String containerPort) {
firePropertyChange(CONTAINER_PORT, this.containerPort,
this.containerPort = containerPort);
}
public String getHostAddress() {
return hostAddress;
}
public void setHostAddress(final String hostName) {
firePropertyChange(HOST_ADDRESS, this.hostAddress,
this.hostAddress = hostName);
}
public String getHostPort() {
return hostPort;
}
public void setHostPort(final String hostPort) {
firePropertyChange(HOST_PORT, this.hostPort,
this.hostPort = hostPort);
}
}
}

View file

@ -12,13 +12,31 @@ package org.eclipse.cdt.internal.docker.launcher;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import org.eclipse.cdt.docker.launcher.DockerLaunchUIPlugin;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.beans.BeanProperties;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.ui.AbstractLaunchConfigurationTab;
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.dialogs.IDialogConstants;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.linuxtools.docker.core.DockerConnectionManager;
import org.eclipse.linuxtools.docker.core.IDockerConnection;
import org.eclipse.linuxtools.docker.core.IDockerConnectionManagerListener;
@ -47,6 +65,8 @@ import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.List;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.osgi.service.prefs.Preferences;
public class ContainerTab extends AbstractLaunchConfigurationTab implements
@ -65,12 +85,21 @@ public class ContainerTab extends AbstractLaunchConfigurationTab implements
private Button newButton;
private Button removeButton;
private CheckboxTableViewer tableViewer;
private Button keepButton;
private Button stdinButton;
private Button privilegedButton;
private Combo imageCombo;
private Combo connectionSelector;
private ContainerTabModel model;
private static final int INDENT = 1;
private final DataBindingContext dbc = new DataBindingContext();
private ModifyListener connectionModifyListener = new ModifyListener() {
@Override
@ -92,6 +121,7 @@ public class ContainerTab extends AbstractLaunchConfigurationTab implements
public ContainerTab() {
super();
containerTab = this;
model = new ContainerTabModel();
}
@Override
@ -146,6 +176,7 @@ public class ContainerTab extends AbstractLaunchConfigurationTab implements
createDirectoryList(mainComposite);
createButtons(mainComposite);
createPortSettingsSection(mainComposite);
createOptions(mainComposite);
}
@ -214,6 +245,205 @@ public class ContainerTab extends AbstractLaunchConfigurationTab implements
removeButton.setEnabled(false);
}
@SuppressWarnings("unchecked")
private void createPortSettingsSection(final Composite parent) {
Font font = parent.getFont();
Composite comp = createComposite(parent, 1, 2, GridData.FILL_BOTH);
Group group = new Group(comp, SWT.NONE);
group.setFont(font);
group.setText(Messages.ContainerTab_Ports_Group_Name);
GridData gd2 = new GridData(GridData.FILL_BOTH);
group.setLayoutData(gd2);
group.setLayout(new GridLayout());
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).span(3, 1)
.grab(true, false).applyTo(group);
group.setLayout(new GridLayout());
// specify ports
final Label portSettingsLabel = new Label(group, SWT.NONE);
portSettingsLabel.setText(Messages.ContainerTab_Specify_Ports_Label);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
.grab(true, false).span(3, 1)
.applyTo(portSettingsLabel);
final CheckboxTableViewer exposedPortsTableViewer = createPortSettingsTable(
group);
tableViewer = exposedPortsTableViewer;
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP)
.grab(true, false).span(3 - 1, 1).indent(INDENT, 0)
.hint(200, 70).applyTo(exposedPortsTableViewer.getTable());
// buttons
final Composite buttonsContainers = new Composite(parent, 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(Messages.ContainerTab_Add_Button);
addButton.addSelectionListener(onAddPort(exposedPortsTableViewer));
final Button editButton = new Button(buttonsContainers, SWT.NONE);
GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP)
.grab(true, false).applyTo(editButton);
editButton.setText(Messages.ContainerTab_Edit_Button);
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(Messages.ContainerTab_Remove_Button);
removeButton
.addSelectionListener(onRemovePorts(exposedPortsTableViewer));
ViewerSupport.bind(exposedPortsTableViewer, model.getExposedPorts(),
BeanProperties.values(ExposedPortModel.class,
ExposedPortModel.CONTAINER_PORT,
ExposedPortModel.PORT_TYPE,
ExposedPortModel.HOST_ADDRESS,
ExposedPortModel.HOST_PORT));
dbc.bindSet(
ViewersObservables.observeCheckedElements(
exposedPortsTableViewer, ExposedPortModel.class),
BeanProperties.set(ContainerTabModel.SELECTED_PORTS)
.observe(model));
checkAllElements(exposedPortsTableViewer);
// disable the edit and removeButton if the table is empty
exposedPortsTableViewer.addSelectionChangedListener(
onSelectionChanged(editButton, removeButton));
exposedPortsTableViewer
.addCheckStateListener(new ICheckStateListener() {
@Override
public void checkStateChanged(
CheckStateChangedEvent event) {
ExposedPortModel e = (ExposedPortModel) event
.getElement();
e.setSelected(event.getChecked());
updateLaunchConfigurationDialog();
}
});
}
private void checkAllElements(
final CheckboxTableViewer exposedPortsTableViewer) {
exposedPortsTableViewer.setAllChecked(true);
model.setSelectedPorts(new HashSet<>(model.getExposedPorts()));
}
private SelectionListener onAddPort(
final CheckboxTableViewer exposedPortsTableViewer) {
return SelectionListener.widgetSelectedAdapter(e -> {
final ContainerPortDialog dialog = new ContainerPortDialog(
getShell());
dialog.create();
if (dialog.open() == IDialogConstants.OK_ID) {
final ExposedPortModel port = dialog.getPort();
port.setSelected(true);
model.addAvailablePort(port);
model.getSelectedPorts().add(port);
exposedPortsTableViewer.setChecked(port, true);
updateLaunchConfigurationDialog();
}
});
}
private SelectionListener onEditPort(
final CheckboxTableViewer exposedPortsTableViewer) {
return SelectionListener.widgetSelectedAdapter(e -> {
final IStructuredSelection selection = exposedPortsTableViewer
.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());
exposedPortsTableViewer.refresh();
updateLaunchConfigurationDialog();
}
});
}
private SelectionListener onRemovePorts(
final TableViewer portsTableViewer) {
return SelectionListener.widgetSelectedAdapter(e -> {
final IStructuredSelection selection = portsTableViewer
.getStructuredSelection();
for (@SuppressWarnings("unchecked")
Iterator<ExposedPortModel> iterator = selection.iterator(); iterator
.hasNext();) {
final ExposedPortModel port = iterator.next();
model.removeAvailablePort(port);
model.getSelectedPorts().remove(port);
updateLaunchConfigurationDialog();
}
});
}
private ISelectionChangedListener onSelectionChanged(
final Button... targetButtons) {
return e -> {
if (e.getSelection().isEmpty()) {
setControlsEnabled(targetButtons, false);
} else {
setControlsEnabled(targetButtons, true);
}
};
}
private static void setControlsEnabled(final Control[] controls,
final boolean enabled) {
for (Control control : controls) {
control.setEnabled(enabled);
}
}
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,
Messages.ContainerTab_Port_Column,
100);
createTableViewerColum(tableViewer,
Messages.ContainerTab_Type_Column,
50);
createTableViewerColum(tableViewer,
Messages.ContainerTab_HostAddress_Column,
100);
createTableViewerColum(tableViewer,
Messages.ContainerTab_HostPort_Column,
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 createOptions(Composite parent) {
Font font = parent.getFont();
Composite comp = createComposite(parent, 1, 3, GridData.FILL_BOTH);
@ -406,6 +636,8 @@ public class ContainerTab extends AbstractLaunchConfigurationTab implements
public void setDefaults(ILaunchConfigurationWorkingCopy configuration) {
configuration.setAttribute(ILaunchConstants.ATTR_ADDITIONAL_DIRS,
(String) null);
configuration.setAttribute(ILaunchConstants.ATTR_EXPOSED_PORTS,
(String) null);
configuration.setAttribute(ILaunchConstants.ATTR_CONNECTION_URI, ""); //$NON-NLS-1$
Preferences prefs = InstanceScope.INSTANCE
.getNode(DockerLaunchUIPlugin.PLUGIN_ID);
@ -427,6 +659,19 @@ public class ContainerTab extends AbstractLaunchConfigurationTab implements
if (additionalDirs != null)
directoriesList.setItems(additionalDirs.toArray(new String[0]));
java.util.List<String> exposedPortInfos = configuration
.getAttribute(ILaunchConstants.ATTR_EXPOSED_PORTS,
Collections.<String> emptyList());
model.removeExposedPorts();
for (String port : exposedPortInfos) {
ExposedPortModel m = ExposedPortModel.createPortModel(port);
model.addAvailablePort(m);
if (m.getSelected()) {
model.getSelectedPorts().add(m);
tableViewer.setChecked(m, true);
}
}
connectionUri = configuration.getAttribute(
ILaunchConstants.ATTR_CONNECTION_URI, (String) "");
int defaultIndex = 0;
@ -480,6 +725,8 @@ public class ContainerTab extends AbstractLaunchConfigurationTab implements
stdinButton.getSelection());
configuration.setAttribute(ILaunchConstants.ATTR_PRIVILEGED_MODE,
privilegedButton.getSelection());
configuration.setAttribute(ILaunchConstants.ATTR_EXPOSED_PORTS,
ExposedPortModel.toArrayString(model.getExposedPorts()));
}
@Override

View file

@ -0,0 +1,87 @@
/*******************************************************************************
* Copyright (c) 2018 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.cdt.internal.docker.launcher;
import java.util.List;
import java.util.Set;
import org.eclipse.core.databinding.observable.list.WritableList;
/**
* @since 1.2.1
* @author jjohnstn
*
*/
public class ContainerTabModel extends BaseDatabindingModel {
public static final String PUBLISH_ALL_PORTS = "publishAllPorts"; //$NON-NLS-1$
public static final String EXPOSED_PORTS = "exposedPorts"; //$NON-NLS-1$
public static final String SELECTED_PORTS = "selectedPorts"; //$NON-NLS-1$
private boolean publishAllPorts = true;
private final WritableList<ExposedPortModel> exposedPorts = new WritableList<>();
private Set<ExposedPortModel> selectedPorts;
public boolean isPublishAllPorts() {
return publishAllPorts;
}
public void setPublishAllPorts(boolean publishAllPorts) {
firePropertyChange(PUBLISH_ALL_PORTS, this.publishAllPorts,
this.publishAllPorts = publishAllPorts);
}
public WritableList<ExposedPortModel> getExposedPorts() {
return exposedPorts;
}
public void addAvailablePort(final ExposedPortModel port) {
this.exposedPorts.add(port);
}
public void removeAvailablePort(final ExposedPortModel port) {
this.exposedPorts.remove(port);
}
public void setExposedPorts(final List<ExposedPortModel> exposedPorts) {
this.exposedPorts.clear();
this.exposedPorts.addAll(exposedPorts);
// FIXME: also add all given exposedPorts to selectedExposedPorts ?
}
public void addExposedPort(final ExposedPortModel exposedPort) {
if (!this.exposedPorts.contains(exposedPort)) {
this.exposedPorts.add(exposedPort);
}
}
public void removeExposedPort(final ExposedPortModel exposedPort) {
this.exposedPorts.remove(exposedPort);
}
public void removeExposedPorts() {
this.exposedPorts.clear();
}
public Set<ExposedPortModel> getSelectedPorts() {
return this.selectedPorts;
}
public void setSelectedPorts(final Set<ExposedPortModel> ports) {
firePropertyChange(SELECTED_PORTS, this.selectedPorts,
this.selectedPorts = ports);
}
}

View file

@ -0,0 +1,246 @@
/*******************************************************************************
* Copyright (c) 2018 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.cdt.internal.docker.launcher;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import org.eclipse.core.runtime.Assert;
public class ExposedPortModel extends BaseDatabindingModel
implements Comparable<ExposedPortModel> {
private static final String SEPARATOR = ":"; //$NON-NLS-1$
private static final String CONTAINER_TYPE_SEPARATOR = "/"; //$NON-NLS-1$
public static final String SELECTED = "selected"; //$NON-NLS-1$
public static final String CONTAINER_PORT = "containerPort"; //$NON-NLS-1$
public static final String PORT_TYPE = "portType"; //$NON-NLS-1$
public static final String HOST_ADDRESS = "hostAddress"; //$NON-NLS-1$
public static final String HOST_PORT = "hostPort"; //$NON-NLS-1$
private final String id = UUID.randomUUID().toString();
private boolean selected;
private String containerPort;
private String portType;
private String hostAddress;
private String hostPort;
/**
* Parses and converts the {@link List} of the given {@link String} values
* into a {@link List} of {@link ExposedPortModel}
*
* @param exposedPortInfos
* the input values
* @return the corresponding {@link ExposedPortModel}s
*/
public static List<ExposedPortModel> fromStrings(
final Collection<String> exposedPortInfos) {
final List<ExposedPortModel> exposedPorts = new ArrayList<>();
for (String exposedPortInfo : exposedPortInfos) {
final ExposedPortModel exposedPort = ExposedPortModel
.fromString(exposedPortInfo);
if (exposedPort != null) {
exposedPorts.add(exposedPort);
}
}
return exposedPorts;
}
/**
* Converts a collection of ExposedPortModel to a {@link List} of
* {@link String} values
*
*
* @param exposedPorts
* collection of ExposedPortModel instances
* @return the corresponding {@link List} of {@link String}s
*/
public static List<String> toArrayString(
final Collection<ExposedPortModel> exposedPorts) {
final List<String> exposedPortList = new ArrayList<>();
for (ExposedPortModel exposedPort : exposedPorts) {
final String exposedPortString = exposedPort.toString();
if (exposedPort != null) {
exposedPortList.add(exposedPortString);
}
}
return exposedPortList;
}
/**
* Parse the given value and returns an instance of
* {@link ExposedPortModel}.
*
* @param exposedPortInfo
* the value to parse
* @return the corresponding {@link ExposedPortModel}
*/
public static ExposedPortModel fromString(final String exposedPortInfo) {
final String privatePort = exposedPortInfo.substring(0,
exposedPortInfo.indexOf(CONTAINER_TYPE_SEPARATOR));
// exposed ports without host IP/port info
final int firstColumnSeparator = exposedPortInfo.indexOf(SEPARATOR);
if (firstColumnSeparator == -1
&& exposedPortInfo.indexOf(CONTAINER_TYPE_SEPARATOR) != -1) {
final String type = exposedPortInfo.substring(
exposedPortInfo.indexOf(CONTAINER_TYPE_SEPARATOR)); // $NON-NLS-1$
final ExposedPortModel exposedPort = new ExposedPortModel(
privatePort, type, "", privatePort); // $NON-NLS-1$
return exposedPort; // $NON-NLS-1$
} else {
final int secondColumnSeparator = exposedPortInfo.indexOf(SEPARATOR,
firstColumnSeparator + 1);
final String type = exposedPortInfo.substring(
exposedPortInfo.indexOf(CONTAINER_TYPE_SEPARATOR), // $NON-NLS-1$
firstColumnSeparator); // $NON-NLS-1$
final String hostIP = exposedPortInfo
.substring(firstColumnSeparator + 1, secondColumnSeparator);
final String hostPort = exposedPortInfo
.substring(secondColumnSeparator + 1);
final ExposedPortModel exposedPort = new ExposedPortModel(
privatePort, type, hostIP, hostPort); // $NON-NLS-1$
return exposedPort; // $NON-NLS-1$
}
}
/**
* Full constructor
*
* @param privatePort
* @param portType
* @param hostAddress
* @param hostPort
*/
public ExposedPortModel(final String privatePort, final String type,
final String hostAddress, final String hostPort) {
Assert.isNotNull(privatePort,
"Port Mapping privatePort cannot be null"); //$NON-NLS-1$
Assert.isNotNull(type, "Port Mapping portType cannot be null"); //$NON-NLS-1$
this.containerPort = privatePort;
this.hostPort = hostPort;
this.portType = type;
this.hostAddress = hostAddress;
}
/**
* Create an ExposedPortModel from its toString output
*
* @param stringValue
* @return ExposedPortModel
*/
static public ExposedPortModel createPortModel(String stringValue) {
final String[] elements = stringValue.split(SEPARATOR);
final String[] containerPortElements = elements[0]
.split(CONTAINER_TYPE_SEPARATOR);
ExposedPortModel model = new ExposedPortModel(containerPortElements[0],
containerPortElements[1], elements[1], elements[2]);
// check the last argument if exists otherwise assume 'true'
model.selected = (elements.length == 4) ? Boolean.valueOf(elements[3])
: true;
return model;
}
public String getContainerPort() {
return containerPort;
}
public void setContainerPort(final String containerPort) {
firePropertyChange(CONTAINER_PORT, this.containerPort,
this.containerPort = containerPort);
}
public String getPortType() {
return portType;
}
public void setPortType(final String type) {
firePropertyChange(PORT_TYPE, this.portType, this.portType = type);
}
public boolean getSelected() {
return selected;
}
public void setSelected(final boolean selected) {
firePropertyChange(SELECTED, this.selected, this.selected = selected);
}
public String getHostPort() {
return hostPort;
}
public void setHostPort(final String hostPort) {
firePropertyChange(HOST_PORT, this.hostPort, this.hostPort = hostPort);
}
public String getHostAddress() {
return hostAddress;
}
public void setHostAddress(final String hostAddress) {
firePropertyChange(HOST_ADDRESS, this.hostAddress,
this.hostAddress = hostAddress);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ExposedPortModel other = (ExposedPortModel) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
@Override
public int compareTo(final ExposedPortModel other) {
return this.containerPort.compareTo(other.containerPort);
}
// FIXME we should have a dedicated method to serialize the bean
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append(containerPort + CONTAINER_TYPE_SEPARATOR + portType
+ SEPARATOR + (hostAddress != null ? hostAddress : "")
+ SEPARATOR + hostPort + SEPARATOR + selected);
return buffer.toString();
}
}

View file

@ -21,6 +21,9 @@ public interface ILaunchConstants {
public final static String ATTR_ADDITIONAL_DIRS = DockerLaunchUIPlugin
.getUniqueIdentifier() + ".additional_dirs"; //$NON-NLS-1$
public final static String ATTR_EXPOSED_PORTS = DockerLaunchUIPlugin
.getUniqueIdentifier() + ".exposed_ports"; //$NON-NLS-1$
public final static String ATTR_IMAGE = DockerLaunchUIPlugin.getUniqueIdentifier()
+ ".image"; //$NON-NLS-1$

View file

@ -15,8 +15,6 @@ import org.eclipse.osgi.util.NLS;
public class Messages extends NLS {
private static final String BUNDLE_NAME = "org.eclipse.cdt.internal.docker.launcher.messages"; //$NON-NLS-1$
public static String LaunchShortcut_Binaries;
public static String LaunchShortcut_Binary_not_found;
public static String LaunchShortcut_Choose_a_launch_configuration;
@ -34,14 +32,25 @@ public class Messages extends NLS {
public static String ContainerTab_Name;
public static String ContainerTab_Group_Name;
public static String ContainerTab_Option_Group_Name;
public static String ContainerTab_Ports_Group_Name;
public static String ContainerTab_Specify_Ports_Label;
public static String ContainerTab_Add_Button;
public static String ContainerTab_Edit_Button;
public static String ContainerTab_New_Button;
public static String ContainerTab_Remove_Button;
public static String ContainerTab_Keep_Label;
public static String ContainerTab_Publish_All_Ports_Label;
public static String ContainerTab_Stdin_Support_Label;
public static String ContainerTab_Privileged_Mode_Label;
public static String ContainerTab_Error_Reading_Configuration;
public static String ContainerTab_Connection_Selector_Label;
public static String ContainerTab_Image_Selector_Label;
public static String ContainerTab_Port_Column;
public static String ContainerTab_Type_Column;
public static String ContainerTab_HostAddress_Column;
public static String ContainerTab_HostPort_Column;
public static String ContainerTab_Error_No_Connections;
public static String ContainerTab_Error_No_Images;
public static String ContainerTab_Warning_Connection_Not_Found;
@ -113,6 +122,12 @@ public class Messages extends NLS {
public static String Gdbserver_Settings_Remotetimeout_tooltip;
public static String ContainerPortDialog_hostAddressLabel;
public static String ContainerPortDialog_hostPortLabel;
public static String ContainerPortDialog_shellTitle;
public static String ContainerPortDialog_containerLabel;
public static String ContainerPortDialog_explanationLabel;
static {
// initialize resource bundle
NLS.initializeMessages(BUNDLE_NAME, Messages.class);

View file

@ -26,20 +26,35 @@ Keep_Container_After_Launch=Keep Container after launch
ContainerTab_Name=Container
ContainerTab_New_Button=New...
ContainerTab_Add_Button=Add...
ContainerTab_Edit_Button=Edit...
ContainerTab_Remove_Button=Remove
ContainerTab_Keep_Label=Keep Container after launch
ContainerTab_Stdin_Support_Label=Support stdin input
ContainerTab_Privileged_Mode_Label=Run in privileged mode
ContainerTab_Group_Name=Required host directories
ContainerTab_Ports_Group_Name=Ports
ContainerTab_Specify_Ports_Label=Manually specify ports and only publish selected entries to the host:
ContainerTab_Publish_All_Ports_Label=Publish all default exposed ports for image to random ports on the host
ContainerTab_Option_Group_Name=Additional Options
ContainerTab_Connection_Selector_Label=Connection:
ContainerTab_Image_Selector_Label=Image:
ContainerTab_Port_Column=Container Port
ContainerTab_Type_Column=Type
ContainerTab_HostAddress_Column=Host Address
ContainerTab_HostPort_Column=Host Port
ContainerTab_Error_Reading_Configuration=Error occurred reading the launch configuration: {0}
ContainerTab_Error_No_Connections=No Docker Connections exist
ContainerTab_Error_No_Images=No Docker Images exist
ContainerTab_Warning_Connection_Not_Found=Docker Connection: {0} for Launch Configuration not found: defaulting to {1}
ContainerTab_Warning_Image_Not_Found=Docker Image: {0} is not a valid pulled image in current Connection: {1}
ContainerPortDialog_shellTitle=Exposing a Container Port
ContainerPortDialog_explanationLabel=Specify the container port to expose:
ContainerPortDialog_containerLabel=Container port:
ContainerPortDialog_hostAddressLabel=Host address:
ContainerPortDialog_hostPortLabel=Host port:
ContainerPropertyTab_Title=Container Settings
ContainerPropertyTab_Enable_Msg=Build inside Docker Image
ContainerPropertyTab_Run_Autotools_In_Container_Msg=Run all Autotools in Container