/******************************************************************************* * Copyright (c) 2015, 2016 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.linuxtools.internal.docker.ui.launch; import static org.eclipse.linuxtools.docker.core.IDockerImageBuildOptions.DOCKER_CONNECTION; import static org.eclipse.linuxtools.docker.core.IDockerImageBuildOptions.FORCE_RM_INTERMEDIATE_CONTAINERS; import static org.eclipse.linuxtools.docker.core.IDockerImageBuildOptions.NO_CACHE; import static org.eclipse.linuxtools.docker.core.IDockerImageBuildOptions.QUIET_BUILD; import static org.eclipse.linuxtools.docker.core.IDockerImageBuildOptions.REPO_NAME; import static org.eclipse.linuxtools.docker.core.IDockerImageBuildOptions.RM_INTERMEDIATE_CONTAINERS; import static org.eclipse.linuxtools.internal.docker.ui.launch.IBuildDockerImageLaunchConfigurationConstants.SOURCE_PATH_LOCATION; import static org.eclipse.linuxtools.internal.docker.ui.launch.IBuildDockerImageLaunchConfigurationConstants.SOURCE_PATH_WORKSPACE_RELATIVE_LOCATION; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.ui.AbstractLaunchConfigurationTab; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.ComboViewer; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.linuxtools.docker.core.DockerConnectionManager; import org.eclipse.linuxtools.docker.ui.Activator; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.DirectoryDialog; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.dialogs.ElementTreeSelectionDialog; import org.eclipse.ui.model.WorkbenchContentProvider; import org.eclipse.ui.model.WorkbenchLabelProvider; import org.eclipse.ui.views.navigator.ResourceComparator; public class BuildDockerImageLaunchConfigurationMainTab extends AbstractLaunchConfigurationTab { private final String TAB_NAME = "BuildDockerImageLaunchConfigurationMainTab.name"; //$NON-NLS-1$ private final String CONNECTION_LABEL = "BuildDockerImageLaunchConfigurationMainTab.connection.group.label"; //$NON-NLS-1$ private final String CONNECTION_TOOLTIP = "BuildDockerImageLaunchConfigurationMainTab.connection.group.tooltip"; //$NON-NLS-1$ private final String CONNECTION_MISSING = "BuildDockerImageLaunchConfigurationMainTab.connection.missing"; //$NON-NLS-1$ private final String BUILD_CONTEXT_PATH_LABEL = "BuildDockerImageLaunchConfigurationMainTab.buildContextPath.group.label"; //$NON-NLS-1$ private final String BUILD_CONTEXT_PATH_MISSING = "BuildDockerImageLaunchConfigurationMainTab.buildContextPath.missing"; //$NON-NLS-1$ private final String BUILD_CONTEXT_PATH_MISSING_DOCKERFILE = "BuildDockerImageLaunchConfigurationMainTab.buildContextPath.missingDockerfile"; //$NON-NLS-1$ private final String BROWSE_WORKSPACE = "BuildDockerImageLaunchConfigurationMainTab.buildContextPath.browseworkspace.button.label"; //$NON-NLS-1$ private final String BROWSE_WORKSPACE_DIALOG_TITLE = "BuildDockerImageLaunchConfigurationMainTab.buildContextPath.browseworkspace.dialog.title"; //$NON-NLS-1$ private final String BROWSE_FILESYSTEM = "BuildDockerImageLaunchConfigurationMainTab.buildContextPath.browsefilesystem.button.label"; //$NON-NLS-1$ private final String REPO_NAME_LABEL = "BuildDockerImageLaunchConfigurationMainTab.repoName.label"; //$NON-NLS-1$ private final String REPO_NAME_MISSING = "BuildDockerImageLaunchConfigurationMainTab.repoName.missing"; //$NON-NLS-1$ private final String OPTIONS_LABEL = "BuildDockerImageLaunchConfigurationMainTab.options.group.label"; //$NON-NLS-1$ private final String OPTION_QUIET_LABEL = "BuildDockerImageLaunchConfigurationMainTab.options.quiet.button.label"; //$NON-NLS-1$ private final String OPTION_NOCACHE_LABEL = "BuildDockerImageLaunchConfigurationMainTab.options.noCache.button.label"; //$NON-NLS-1$ private final String OPTION_RM_LABEL = "BuildDockerImageLaunchConfigurationMainTab.options.rm.button.label"; //$NON-NLS-1$ private final String OPTION_FORCERM_LABEL = "BuildDockerImageLaunchConfigurationMainTab.options.forceRM.button.label"; //$NON-NLS-1$ /** the Docker daemon to use for the image build. */ private ComboViewer connectionSelectionComboViewer; /** the path to the build context . */ private Text buildContextPathText; /** whether buildContextPath is relative */ private boolean buildContextPathWorkspaceRelative; /** build option: name and optional tag. */ private Text repoNameText; /** build option: do not use cache. */ private Button noCacheButton; /** build option: quiet mode. */ private Button quietBuildButton; /** build option: remove intermediate after successful build only. */ private Button removeIntermediateContainersButton; /** build option: always remove intermediate. */ private Button alwaysRemoveIntermediateContainersButton; @Override public void createControl(final Composite parent) { final Composite container = new Composite(parent, SWT.NONE); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL) .grab(true, false).applyTo(container); GridLayoutFactory.fillDefaults().margins(6, 6).applyTo(container); setControl(container); // connection selection final Group connectionGroup = new Group(container, SWT.NONE); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL) .grab(true, false).applyTo(connectionGroup); GridLayoutFactory.fillDefaults().numColumns(2).margins(6, 6) .applyTo(connectionGroup); connectionGroup.setText(LaunchMessages.getString(CONNECTION_LABEL)); connectionGroup .setToolTipText(LaunchMessages.getString(CONNECTION_TOOLTIP)); final Combo connectionSelectionCombo = new Combo(connectionGroup, SWT.NONE); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER) .grab(true, false).applyTo(connectionSelectionCombo); this.connectionSelectionComboViewer = new ComboViewer( connectionSelectionCombo); this.connectionSelectionComboViewer .setContentProvider(new ArrayContentProvider()); this.connectionSelectionComboViewer.setInput( DockerConnectionManager.getInstance().getConnectionNames()); connectionSelectionCombo .addSelectionListener(new LaunchConfigurationChangeListener()); // build context path createBuildContextPathGroup(container); // repository name createRepoNameGroup(container); // dockerfile path // createDockerfilePathGroup(container); // build options createBuildOptionsGroup(container); } private void createBuildContextPathGroup(final Composite container) { final Group buildContextPathLocationGroup = new Group(container, SWT.BORDER); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL) .grab(true, false).applyTo(buildContextPathLocationGroup); GridLayoutFactory.fillDefaults().margins(6, 6).numColumns(3) .applyTo(buildContextPathLocationGroup); buildContextPathLocationGroup .setText(LaunchMessages.getString(BUILD_CONTEXT_PATH_LABEL)); this.buildContextPathText = new Text( buildContextPathLocationGroup, SWT.BORDER); this.buildContextPathText .addModifyListener(new LaunchConfigurationChangeListener()); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER) .grab(true, false).span(3, 1) .applyTo(this.buildContextPathText); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER) .grab(true, false) .applyTo(new Label(buildContextPathLocationGroup, SWT.NONE)); final Button browseWorkspaceButton = new Button( buildContextPathLocationGroup, SWT.NONE); browseWorkspaceButton .setText(LaunchMessages.getString(BROWSE_WORKSPACE)); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER) .grab(false, false).applyTo(browseWorkspaceButton); browseWorkspaceButton .addSelectionListener(onBrowseWorkspace(buildContextPathText, IContainer.class)); final Button browseFileSystemButton = new Button( buildContextPathLocationGroup, SWT.NONE); browseFileSystemButton .setText(LaunchMessages.getString(BROWSE_FILESYSTEM)); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER) .grab(false, false).applyTo(browseFileSystemButton); browseFileSystemButton.addSelectionListener( onBrowseFileSystemForDirectory(this.buildContextPathText)); } private void createRepoNameGroup(final Composite container) { final Group repoNameGroup = new Group(container, SWT.NONE); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL) .grab(true, false).applyTo(repoNameGroup); GridLayoutFactory.fillDefaults().margins(6, 6).numColumns(1) .applyTo(repoNameGroup); repoNameGroup.setText(LaunchMessages.getString(REPO_NAME_LABEL)); this.repoNameText = new Text(repoNameGroup, SWT.BORDER); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER) .grab(true, false).applyTo(this.repoNameText); this.repoNameText .addModifyListener(new LaunchConfigurationChangeListener()); } private void createBuildOptionsGroup(final Composite container) { final Group optionsGroup = new Group(container, SWT.NONE); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL) .grab(true, false).applyTo(optionsGroup); GridLayoutFactory.fillDefaults().margins(6, 6).numColumns(2) .applyTo(optionsGroup); optionsGroup.setText(LaunchMessages.getString(OPTIONS_LABEL)); this.quietBuildButton = new Button(optionsGroup, SWT.CHECK); this.quietBuildButton .setText(LaunchMessages.getString(OPTION_QUIET_LABEL)); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).span(2, 1) .grab(true, false).applyTo(this.quietBuildButton); this.quietBuildButton .addSelectionListener(new LaunchConfigurationChangeListener()); this.noCacheButton = new Button(optionsGroup, SWT.CHECK); this.noCacheButton .setText(LaunchMessages.getString(OPTION_NOCACHE_LABEL)); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).span(2, 1) .grab(true, false).applyTo(this.noCacheButton); this.noCacheButton .addSelectionListener(new LaunchConfigurationChangeListener()); this.removeIntermediateContainersButton = new Button(optionsGroup, SWT.CHECK); this.removeIntermediateContainersButton .setText(LaunchMessages.getString(OPTION_RM_LABEL)); this.removeIntermediateContainersButton .addSelectionListener(new LaunchConfigurationChangeListener()); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).span(2, 1) .grab(true, false) .applyTo(this.removeIntermediateContainersButton); this.alwaysRemoveIntermediateContainersButton = new Button(optionsGroup, SWT.CHECK); this.alwaysRemoveIntermediateContainersButton .setText(LaunchMessages.getString(OPTION_FORCERM_LABEL)); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).span(2, 1) .grab(true, false) .applyTo(this.alwaysRemoveIntermediateContainersButton); this.alwaysRemoveIntermediateContainersButton .addSelectionListener(onAlwaysRemoveIntermediateContainers()); this.alwaysRemoveIntermediateContainersButton .addSelectionListener(new LaunchConfigurationChangeListener()); } private SelectionListener onAlwaysRemoveIntermediateContainers() { return new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { toggleRemoveIntermediateContainersButtonState(); } }; } /** * Opens a dialog to browse the workspace * * @return */ private SelectionListener onBrowseWorkspace(final Text pathText, final Class<?> expectedType) { return new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { final ElementTreeSelectionDialog dialog = new ElementTreeSelectionDialog( getShell(), new WorkbenchLabelProvider(), new WorkbenchContentProvider()); dialog.setInput(ResourcesPlugin.getWorkspace().getRoot()); dialog.setTitle(LaunchMessages .getString(BROWSE_WORKSPACE_DIALOG_TITLE)); dialog.setComparator( new ResourceComparator(ResourceComparator.NAME)); dialog.setAllowMultiple(false); dialog.setValidator(selection -> { if (selection.length == 1 && expectedType .isAssignableFrom(selection[0].getClass())) { return new Status(IStatus.OK, Activator.PLUGIN_ID, null); } return new Status(IStatus.ERROR, Activator.PLUGIN_ID, null); }); if (dialog.open() == IDialogConstants.OK_ID) { final IResource selection = (IResource) dialog .getFirstResult(); pathText.setText(selection.getFullPath().toOSString()); buildContextPathWorkspaceRelative = true; } } }; } /** * Opens a dialog to browse the file system and select a directory * * @return */ private SelectionListener onBrowseFileSystemForDirectory(final Text pathText) { return new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { final DirectoryDialog dialog = new DirectoryDialog(getShell()); final String selection = dialog.open(); if (selection != null) { pathText.setText(selection); buildContextPathWorkspaceRelative = false; } } }; } @Override public void setDefaults( final ILaunchConfigurationWorkingCopy configuration) { } @Override public void initializeFrom(final ILaunchConfiguration configuration) { try { this.connectionSelectionComboViewer .setSelection(new StructuredSelection( configuration.getAttribute(DOCKER_CONNECTION, ""))); this.buildContextPathText.setText( configuration.getAttribute(SOURCE_PATH_LOCATION, "")); this.buildContextPathWorkspaceRelative = configuration.getAttribute( SOURCE_PATH_WORKSPACE_RELATIVE_LOCATION, false); this.repoNameText .setText(configuration.getAttribute(REPO_NAME, "")); this.quietBuildButton.setSelection( configuration.getAttribute(QUIET_BUILD, false)); this.noCacheButton .setSelection(configuration.getAttribute(NO_CACHE, false)); this.removeIntermediateContainersButton.setSelection(configuration .getAttribute(RM_INTERMEDIATE_CONTAINERS, false)); this.alwaysRemoveIntermediateContainersButton.setSelection( configuration.getAttribute(FORCE_RM_INTERMEDIATE_CONTAINERS, false)); toggleRemoveIntermediateContainersButtonState(); } catch (CoreException e) { Activator.log(e); } } @Override public boolean isValid(final ILaunchConfiguration launchConfig) { try { // verify the connection final String dockerConnection = launchConfig .getAttribute(DOCKER_CONNECTION, ""); // $NON-NLS-1$ // verify the source path final String sourcePathLocation = launchConfig .getAttribute(SOURCE_PATH_LOCATION, ""); // $NON-NLS-1$ final boolean sourcePathWorkspaceRelativeLocation = launchConfig.getAttribute(SOURCE_PATH_WORKSPACE_RELATIVE_LOCATION, false); final IPath sourcePath = BuildDockerImageUtils.getPath( sourcePathLocation, sourcePathWorkspaceRelativeLocation); if (dockerConnection.isEmpty() || dockerConnection == null || DockerConnectionManager.getInstance() .findConnection(dockerConnection) == null) { setErrorMessage(LaunchMessages.getString(CONNECTION_MISSING)); return false; } else if (sourcePathLocation.isEmpty() || sourcePath == null) { setErrorMessage( LaunchMessages.getString(BUILD_CONTEXT_PATH_MISSING)); return false; } else if (!sourcePath.append("Dockerfile").toFile().exists()) { setErrorMessage(LaunchMessages.getString(BUILD_CONTEXT_PATH_MISSING_DOCKERFILE)); return false; } else { setErrorMessage(null); } final String repoName = launchConfig.getAttribute(REPO_NAME, ""); // $NON-NLS-1$ if (repoName.isEmpty()) { setWarningMessage(LaunchMessages.getString(REPO_NAME_MISSING)); } else { setWarningMessage(null); } } catch (CoreException e) { Activator.log(e); } return super.isValid(launchConfig); } @Override public void performApply( final ILaunchConfigurationWorkingCopy configuration) { final IStructuredSelection connectionSelection = (IStructuredSelection) this.connectionSelectionComboViewer .getSelection(); if (connectionSelection.getFirstElement() != null) { configuration.setAttribute(DOCKER_CONNECTION, connectionSelection.getFirstElement().toString()); } configuration.setAttribute(SOURCE_PATH_LOCATION, this.buildContextPathText.getText()); configuration.setAttribute(SOURCE_PATH_WORKSPACE_RELATIVE_LOCATION, this.buildContextPathWorkspaceRelative); if (!this.repoNameText.getText().isEmpty()) { configuration.setAttribute(REPO_NAME, this.repoNameText.getText()); } configuration.setAttribute(QUIET_BUILD, this.quietBuildButton.getSelection()); configuration.setAttribute(NO_CACHE, this.noCacheButton.getSelection()); configuration.setAttribute(RM_INTERMEDIATE_CONTAINERS, this.removeIntermediateContainersButton.getSelection()); configuration.setAttribute(FORCE_RM_INTERMEDIATE_CONTAINERS, this.alwaysRemoveIntermediateContainersButton.getSelection()); } @Override public String getName() { return LaunchMessages.getString(TAB_NAME); } /** * Enables or disables the * {@link BuildDockerImageLaunchConfigurationMainTab#removeIntermediateContainersButton} * given the selection of * {@link BuildDockerImageLaunchConfigurationMainTab#alwaysRemoveIntermediateContainersButton} */ private void toggleRemoveIntermediateContainersButtonState() { if (BuildDockerImageLaunchConfigurationMainTab.this.alwaysRemoveIntermediateContainersButton .getSelection()) { BuildDockerImageLaunchConfigurationMainTab.this.removeIntermediateContainersButton .setEnabled(false); } else { BuildDockerImageLaunchConfigurationMainTab.this.removeIntermediateContainersButton .setEnabled(true); } } private class LaunchConfigurationChangeListener extends SelectionAdapter implements ModifyListener { @Override public void modifyText(final ModifyEvent e) { updateLaunchConfigurationDialog(); } @Override public void widgetSelected(final SelectionEvent e) { updateLaunchConfigurationDialog(); } } }