/******************************************************************************* * Copyright (c) 2015, 2016 Pivotal, Inc. * 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: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ package org.springframework.ide.eclipse.boot.dash.cloudfoundry.ops; import java.io.StringWriter; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Set; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.SubProgressMonitor; import org.springframework.ide.eclipse.boot.dash.cloudfoundry.CloudAppDashElement; import org.springframework.ide.eclipse.boot.dash.cloudfoundry.CloudFoundryBootDashModel; import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.CFApplicationDetail; import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.ClientRequests; import org.springframework.ide.eclipse.boot.dash.cloudfoundry.client.v2.CFPushArguments; import org.springframework.ide.eclipse.boot.dash.cloudfoundry.debug.DebugSupport; import org.springframework.ide.eclipse.boot.dash.cloudfoundry.deployment.CloudApplicationDeploymentProperties; import org.springframework.ide.eclipse.boot.dash.model.BootDashElement; import org.springframework.ide.eclipse.boot.dash.model.BootDashModel; import org.springframework.ide.eclipse.boot.dash.model.LocalRunTarget; import org.springframework.ide.eclipse.boot.dash.model.RunState; import org.springframework.ide.eclipse.boot.dash.model.UserInteractions; import org.springframework.ide.eclipse.boot.dash.util.CancelationTokens; import org.springframework.ide.eclipse.boot.dash.util.CancelationTokens.CancelationToken; import org.springframework.ide.eclipse.boot.util.Log; import org.springsource.ide.eclipse.commons.livexp.util.ExceptionUtil; public class ProjectsDeployer extends CloudOperation { private static final boolean DEBUG = (""+Platform.getLocation()).contains("bamboo") || (""+Platform.getLocation()).contains("kdvolder"); private static void debug(String string) { if (DEBUG) { System.out.println(string); } } private final Set<IProject> projectsToDeploy; private final UserInteractions ui; private final RunState runOrDebug; private final DebugSupport debugSupport; public ProjectsDeployer(CloudFoundryBootDashModel model, UserInteractions ui, Set<IProject> projectsToDeploy, RunState runOrDebug, DebugSupport debugSupport) { super("Deploying projects", model); this.projectsToDeploy = projectsToDeploy; this.ui = ui; this.runOrDebug = runOrDebug; this.debugSupport = debugSupport; } protected void doCloudOp(IProgressMonitor monitor) throws Exception, OperationCanceledException { monitor.beginTask("Deploy projects", projectsToDeploy.size()); try { for (Iterator<IProject> it = projectsToDeploy.iterator(); it.hasNext();) { IProject project = it.next(); if (monitor.isCanceled()) { throw new OperationCanceledException(); } deployProject(project, new SubProgressMonitor(monitor, 1)); } } finally { monitor.done(); } } private void deployProject(IProject project, IProgressMonitor monitor) throws Exception { debug("deployProject["+project.getName()+"] starting"); CloudApplicationDeploymentProperties properties = model.createDeploymentProperties(project, ui, monitor); debug("deployProject["+project.getName()+"] got deployment properties"); CloudAppDashElement cde = model.ensureApplication(properties.getAppName()); debug("deployProject["+project.getName()+"] created cde: "+cde.getName()); model.runAsynch("Deploy project '"+project.getName()+"'", properties.getAppName(), (IProgressMonitor progressMonitor) -> { doDeployProject(cde, properties, project, progressMonitor); }, ui); } protected void doDeployProject(CloudAppDashElement cde, CloudApplicationDeploymentProperties properties, IProject project, IProgressMonitor monitor) throws Exception { ClientRequests client = model.getRunTarget().getClient(); CancelationToken cancelationToken = cde.createCancelationToken(); try { cde.whileStarting(ui, cancelationToken, monitor, () -> { if (client.applicationExists(properties.getAppName())) { CFApplicationDetail existingAppDetails = client.getApplication(properties.getAppName()); if (!confirmOverwriteExisting(properties, existingAppDetails)) { throw new OperationCanceledException(); } } cde.setDeploymentManifestFile(properties.getManifestFile()); cde.setProject(project); copyTags(project, cde); cde.print("Pushing project '"+project.getName()+"'"); try (CFPushArguments args = properties.toPushArguments(model.getCloudDomains(monitor))) { if (isDebugEnabled()) { debugSupport.setupEnvVars(args.getEnv()); } client.push(args, CancelationTokens.merge(cancelationToken, monitor)); cde.print("Pushing project '"+project.getName()+"' SUCCEEDED!"); } if (cde.refresh()!=null) { //Careful... connecting the debugger must be done after the refresh because it needs the app guid which // won't be available for a newly created element if its not yet been populated with data from CF. if (isDebugEnabled()) { debugSupport.createOperation(cde, "Connect Debugger for "+cde.getName() , ui, cancelationToken).runOp(monitor); } } }); } catch (Exception e) { cde.refresh(); cde.printError("Pushing FAILED!"); if (!ExceptionUtil.isCancelation(e)) { Log.log(e); if (ui != null) { String message = e.getMessage() != null && e.getMessage().trim().length() > 0 ? e.getMessage() : "Error type: " + e.getClass().getName() + ". Check Error Log view for further details."; ui.errorPopup("Operation Failure", message); } } } } private boolean isDebugEnabled() { return runOrDebug==RunState.DEBUGGING && debugSupport!=null; } private void copyTags(IProject project, CloudAppDashElement cde) { BootDashElement localElement = findLocalBdeForProject(project); if (localElement!=null) { copyTags(localElement, cde); } } private BootDashElement findLocalBdeForProject(IProject project) { BootDashModel localModel = model.getViewModel().getSectionByTargetId(LocalRunTarget.INSTANCE.getId()); if (localModel != null) { for (BootDashElement bde : localModel.getElements().getValue()) { if (project.equals(bde.getProject())) { return bde; } } } return null; } private static void copyTags(BootDashElement sourceBde, BootDashElement targetBde) { LinkedHashSet<String> tagsToCopy = sourceBde.getTags(); if (tagsToCopy != null && !tagsToCopy.isEmpty()) { LinkedHashSet<String> targetTags = targetBde.getTags(); for (String tag : tagsToCopy) { targetTags.add(tag); } targetBde.setTags(targetTags); } } private boolean confirmOverwriteExisting(CloudApplicationDeploymentProperties properties, CFApplicationDetail existingAppDetails) { String title = "Replace Existing Application"; StringWriter writer = new StringWriter(); writer.append("Replace existing application - "); writer.append(properties.getAppName()); writer.append(" - with the project: "); writer.append(properties.getProject().getName()); writer.append('?'); return ui.confirmApplicationReplacement(title, writer.toString(), existingAppDetails.getServices()); } }