/******************************************************************************* * Copyright (c) 2015, 2016 Pivotal Software, 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 Software, Inc. - initial API and implementation *******************************************************************************/ package org.springframework.ide.eclipse.boot.dash.cloudfoundry.debug.ssh; import static org.springframework.ide.eclipse.boot.dash.cloudfoundry.debug.ssh.SshDebugLaunchConfigurationDelegate.getApp; import static org.springframework.ide.eclipse.boot.dash.cloudfoundry.debug.ssh.SshDebugLaunchConfigurationDelegate.getLaunchType; import java.util.Map; import org.eclipse.core.runtime.CoreException; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationType; import org.eclipse.debug.core.model.IDebugTarget; import org.springframework.ide.eclipse.boot.core.BootActivator; import org.springframework.ide.eclipse.boot.dash.BootDashActivator; import org.springframework.ide.eclipse.boot.dash.cloudfoundry.CloudAppDashElement; import org.springframework.ide.eclipse.boot.dash.cloudfoundry.CloudFoundryRunTarget; import org.springframework.ide.eclipse.boot.dash.cloudfoundry.debug.DebugSupport; import org.springframework.ide.eclipse.boot.dash.cloudfoundry.ops.Operation; import org.springframework.ide.eclipse.boot.dash.model.BootDashViewModel; import org.springframework.ide.eclipse.boot.dash.model.UserInteractions; import org.springframework.ide.eclipse.boot.dash.util.CancelationTokens.CancelationToken; import org.springframework.ide.eclipse.boot.launch.util.BootLaunchUtils; import org.springframework.ide.eclipse.editor.support.util.StringUtil; import org.springsource.ide.eclipse.commons.livexp.util.ExceptionUtil; /** * Uses ssh tunnelling on Diego to support debugging of app running on CF. * * @author Kris De Volder */ public class SshDebugSupport extends DebugSupport { public static final SshDebugSupport INSTANCE = new SshDebugSupport(); private static final int REMOTE_DEBUG_PORT = 47822; private static final String REMOTE_DEBUG_JVM_ARGS = "-Xdebug -Xrunjdwp:server=y,transport=dt_socket,suspend=n,address="+REMOTE_DEBUG_PORT; private static final String JAVA_OPTS = "JAVA_OPTS"; private SshDebugSupport() {} @Override public boolean isSupported(CloudAppDashElement app) { String notSupportedMessage = getNotSupportedMessage(app); return notSupportedMessage==null; } @Override public String getNotSupportedMessage(CloudAppDashElement app) { //TODO: There are a number of different ways that ssh and/or diego might be disabled for // an app. e.g. it can be disabled on the app itself, on the space, on the org or // on the whole CF installation. This check should recognize these situation. // At the moment it pretty much returns true if the CF has global info about the 'ssh-host', // but this probably usually the case on any recent enough version of PCF, even if // ssh has been explicitly disabled. CloudFoundryRunTarget target = app.getTarget(); try { if (target.getSshClientSupport().getSshHost()==null) { return "Cloud controller doesn't specify an ssh-host. This probably means your version of CloudFoundry doesn't support SSH."; } return null; } catch (Exception e) { BootDashActivator.log(e); //for traceability String msg = ExceptionUtil.getMessage(e); if (!StringUtil.hasText(msg)) { msg = "Exception: "+e.getClass().getName(); } return msg; } } @Override public boolean isDebuggerAttached(CloudAppDashElement app) { ILaunchConfiguration conf = SshDebugLaunchConfigurationDelegate.findConfig(app); if (conf!=null) { for (ILaunch l : BootLaunchUtils.getLaunches(conf)) { if (!l.isTerminated()) { for (IDebugTarget dt : l.getDebugTargets()) { if (!dt.isTerminated()) { //Active debug target found, so debugger is attached. return true; } } return true; } } } return false; } @Override public Operation<?> createOperation(CloudAppDashElement app, String opName, UserInteractions ui, CancelationToken cancelationToken) { return new SshDebugStartOperation(app, this, cancelationToken); } @Override public void setupEnvVars(Map<String, String> env) { String javaOpts = clearJavaOpts(env.get(JAVA_OPTS)); StringBuilder sb = new StringBuilder(javaOpts); if (sb.length() > 0) { sb.append(' '); } sb.append(REMOTE_DEBUG_JVM_ARGS); env.put(JAVA_OPTS, sb.toString()); } private static String clearJavaOpts(String opts) { if (opts!=null) { opts = opts.replaceAll(REMOTE_DEBUG_JVM_ARGS + "\\s*", ""); return opts; } else { return ""; } } @Override public void clearEnvVars(Map<String, String> env) { String jopts = clearJavaOpts(env.get(JAVA_OPTS)); if (StringUtil.hasText(jopts)) { env.put(JAVA_OPTS, clearJavaOpts(env.get(JAVA_OPTS))); } else { env.remove(JAVA_OPTS); } } public int getRemotePort() { return REMOTE_DEBUG_PORT; } @Override public CloudAppDashElement getElementFor(ILaunch l, BootDashViewModel context) { try { ILaunchConfigurationType interestingType = getLaunchType(); ILaunchConfiguration conf = l.getLaunchConfiguration(); if (interestingType.equals(conf.getType())) { return getApp(conf, context); } } catch (CoreException e) { BootActivator.log(e); } return null; } }