/*******************************************************************************
* Copyright (c) 2015 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is 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, Inc. - initial API and implementation
******************************************************************************/
package org.jboss.tools.openshift.cdk.server.core.internal.adapter.controllers;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.externaltools.internal.IExternalToolConstants;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.IDebugEventSetListener;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.wst.server.core.IServer;
import org.eclipse.wst.server.core.ServerUtil;
import org.eclipse.wst.server.core.internal.Server;
import org.jboss.ide.eclipse.as.core.JBossServerCorePlugin;
import org.jboss.ide.eclipse.as.core.util.ArgsUtil;
import org.jboss.ide.eclipse.as.core.util.JBossServerBehaviorUtils;
import org.jboss.ide.eclipse.as.wtp.core.server.behavior.ControllableServerBehavior;
import org.jboss.ide.eclipse.as.wtp.core.server.behavior.ILaunchServerController;
import org.jboss.ide.eclipse.as.wtp.core.server.launch.AbstractStartJavaServerLaunchDelegate;
import org.jboss.tools.foundation.core.credentials.UsernameChangedException;
import org.jboss.tools.openshift.cdk.server.core.internal.VagrantBinaryUtility;
import org.jboss.tools.openshift.cdk.server.core.internal.CDKConstants;
import org.jboss.tools.openshift.cdk.server.core.internal.CDKCoreActivator;
import org.jboss.tools.openshift.cdk.server.core.internal.MinishiftBinaryUtility;
import org.jboss.tools.openshift.cdk.server.core.internal.adapter.AbstractCDKPoller;
import org.jboss.tools.openshift.cdk.server.core.internal.adapter.CDK3Server;
import org.jboss.tools.openshift.cdk.server.core.internal.adapter.CDKServer;
import org.jboss.tools.openshift.cdk.server.core.internal.adapter.CDKServerBehaviour;
import org.jboss.tools.openshift.cdk.server.core.internal.adapter.MinishiftPoller;
import org.jboss.tools.openshift.cdk.server.core.internal.adapter.VagrantPoller;
import org.jboss.tools.openshift.internal.common.core.util.CommandLocationLookupStrategy;
public class CDK3LaunchController extends AbstractCDKLaunchController implements ILaunchServerController, IExternalLaunchConstants {
@Override
public void initialize(ILaunchConfigurationWorkingCopy wc) throws CoreException {
final IServer s = ServerUtil.getServer(wc);
final CDKServer cdkServer = (CDKServer)s.loadAdapter(CDKServer.class, new NullProgressMonitor());
//for testing purposes.
//we can't mock final methods like getServer(), so we need to be creative
initialize(wc, cdkServer.getUsername(), cdkServer.getServer());
}
//NOT API! Made public for testing purposes
public void initialize(ILaunchConfigurationWorkingCopy wc, String userName, IServer server) throws CoreException {
wc.setAttribute(FLAG_INITIALIZED, true);
String workingDir = JBossServerCorePlugin.getServerStateLocation(server).toOSString();
wc.setAttribute(ATTR_WORKING_DIR, workingDir);
CDKServer cdkServer = (CDKServer)server.loadAdapter(CDKServer.class, new NullProgressMonitor());
boolean passCredentials = cdkServer.passCredentials();
if( passCredentials) {
// These environment variables are visible AND persisted in the launch configuration.
// It is not safe to persist the password here, but rather add it on-the-fly to the
// program launch later on.
HashMap<String, String> env = new HashMap<>();
String userKey = cdkServer.getUserEnvironmentKey();
env.put(userKey, userName);
wc.setAttribute(ENVIRONMENT_VARS_KEY, env);
}
String cmdLoc = server.getAttribute(CDK3Server.MINISHIFT_FILE, (String)null);
wc.setAttribute(ATTR_LOCATION, cmdLoc);
String defaultArgs = "start --vm-driver=" + cdkServer.getServer().getAttribute(CDK3Server.PROP_HYPERVISOR, CDK3Server.getHypervisors()[0]);
String currentVal = wc.getAttribute(ATTR_ARGS, defaultArgs);
wc.setAttribute(ATTR_ARGS, currentVal);
}
@Override
protected void performOverrides(ILaunchConfigurationWorkingCopy workingCopy) throws CoreException {
// Overrides, things that should always match whats in server editor
final IServer s = ServerUtil.getServer(workingCopy);
final CDKServer cdkServer = (CDKServer)s.loadAdapter(CDKServer.class, new NullProgressMonitor());
String workingDir = JBossServerCorePlugin.getServerStateLocation(s).toOSString();
workingCopy.setAttribute(ATTR_WORKING_DIR, workingDir);
Map<String, String> env = workingCopy.getAttribute(ENVIRONMENT_VARS_KEY, (Map)null);
env = (env == null ? new HashMap<>() : new HashMap<>(env));
String userKey = cdkServer.getUserEnvironmentKey();
boolean passCredentials = cdkServer.passCredentials();
if( passCredentials) {
// These environment variables are visible AND persisted in the launch configuration.
// It is not safe to persist the password here, but rather add it on-the-fly to the
// program launch later on.
env.put(userKey, cdkServer.getUsername());
} else {
env.remove(userKey);
}
setMinishiftLocationOnLaunchConfig(cdkServer, workingCopy, env);
workingCopy.setAttribute(ENVIRONMENT_VARS_KEY, env);
// override vm-driver args
String targetedHypervisor = cdkServer.getServer().getAttribute(CDK3Server.PROP_HYPERVISOR, CDK3Server.getHypervisors()[0]);
String defaultArgs = "start --vm-driver=" + targetedHypervisor;
String currentVal = workingCopy.getAttribute(ATTR_ARGS, defaultArgs);
String replaced = ArgsUtil.setArg(currentVal, null, "--vm-driver", targetedHypervisor);
workingCopy.setAttribute(ATTR_ARGS, replaced);
}
private void setMinishiftLocationOnLaunchConfig(CDKServer cdkServer, ILaunchConfigurationWorkingCopy workingCopy, Map<String, String> env ) throws CoreException {
String minishiftLoc = cdkServer.getServer().getAttribute(CDK3Server.MINISHIFT_FILE, (String)null);
if( minishiftLoc == null )
minishiftLoc = MinishiftBinaryUtility.getMinishiftLocation(workingCopy);
if( minishiftLoc != null ) {
String minishiftCmdFolder = new Path(minishiftLoc).removeLastSegments(1).toOSString();
CommandLocationLookupStrategy.get().ensureOnPath(env, minishiftCmdFolder);
workingCopy.setAttribute(IExternalToolConstants.ATTR_LOCATION, minishiftLoc);
}
}
@Override
public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor)
throws CoreException {
final IServer s = ServerUtil.getServer(configuration);
if( s == null ) {
throw new CoreException(CDKCoreActivator.statusFactory().errorStatus("Unable to locate server from launch configuration."));
}
final ControllableServerBehavior beh = (ControllableServerBehavior)JBossServerBehaviorUtils.getControllableBehavior(configuration);
beh.setServerStarting();
String minishiftLoc = MinishiftBinaryUtility.getMinishiftLocation(s);
if(minishiftLoc == null || !(new File(minishiftLoc).exists())) {
beh.setServerStopped();
if( minishiftLoc == null )
throw new CoreException(CDKCoreActivator.statusFactory().errorStatus("Unable to locate minishift command. Please check to ensure that the command is available on your Path environment variable."));
throw new CoreException(CDKCoreActivator.statusFactory().errorStatus("Expected location of minishift command does not exist: " + minishiftLoc));
}
CDKServer cdkServer = (CDKServer)s.loadAdapter(CDKServer.class, new NullProgressMonitor());
boolean passCredentials = cdkServer.passCredentials();
if( passCredentials) {
handleCredentialsDuringLaunch(s, cdkServer, beh);
}
// Poll the server once more
IStatus stat = getCDKPoller().getCurrentStateSynchronous(getServer());
if( stat.isOK()) {
beh.setServerStarted();
((Server)beh.getServer()).setMode("run");
return;
}
String args = configuration.getAttribute(ATTR_ARGS, (String)null);
Process p = null;
try {
p = new CDKLaunchUtility().callMinishiftInteractive(s, args, getStartupLaunchName(s));
} catch(IOException ioe) {
CDKCoreActivator.pluginLog().logError(ioe);
beh.setServerStopped();
throw new CoreException(new Status(IStatus.ERROR, CDKCoreActivator.PLUGIN_ID, ioe.getMessage(), ioe));
}
if( p == null ) {
beh.setServerStopped();
throw new CoreException(new Status(IStatus.ERROR, CDKCoreActivator.PLUGIN_ID, "Call to minishift up has failed."));
}
IProcess process = addProcessToLaunch(p, launch,s);
linkTerminal(p);
IDebugEventSetListener debug = getDebugListener(new IProcess[]{process}, launch);
DebugPlugin.getDefault().addDebugEventListener(debug);
beh.putSharedData(AbstractStartJavaServerLaunchDelegate.PROCESS, process);
beh.putSharedData(AbstractStartJavaServerLaunchDelegate.DEBUG_LISTENER, debug);
}
private void handleCredentialsDuringLaunch(IServer s, CDKServer cdkServer,ControllableServerBehavior beh ) throws CoreException {
String userKey = cdkServer.getUserEnvironmentKey();
String passKey = cdkServer.getPasswordEnvironmentKey();
if( userKey == null || userKey.trim().isEmpty()) {
beh.setServerStopped();
throw new CoreException(CDKCoreActivator.statusFactory().errorStatus(
"Username environment variable id cannot be empty when passing credentials via environment variables."));
}
if( passKey == null || passKey.trim().isEmpty()) {
beh.setServerStopped();
throw new CoreException(CDKCoreActivator.statusFactory().errorStatus(
"Password environment variable id cannot be empty when passing credentials via environment variables."));
}
beh.putSharedData(CDKServerBehaviour.PROP_CACHED_PASSWORD, null);
beh.putSharedData(CDKServerBehaviour.PROP_CACHED_USER, null);
String pass = null;
String user = cdkServer.getUsername();
try {
pass = cdkServer.getPassword();
} catch(UsernameChangedException uce) {
pass = uce.getPassword();
user = uce.getUser();
}
if( user == null ) {
beh.setServerStopped();
throw new CoreException(CDKCoreActivator.statusFactory().errorStatus("The server " + s.getName() + " has no username associated with it. Please open the server editor and configure the credentials."));
}
if( pass == null ) {
beh.setServerStopped();
throw new CoreException(CDKCoreActivator.statusFactory().errorStatus("The server " + s.getName() + " has no password associated with it. Please open the server editor and configure the credentials."));
}
beh.putSharedData(CDKServerBehaviour.PROP_CACHED_PASSWORD, pass);
beh.putSharedData(CDKServerBehaviour.PROP_CACHED_USER, user);
}
@Override
protected AbstractCDKPoller getCDKPoller() {
MinishiftPoller vp = new MinishiftPoller();
return vp;
}
@Override
protected void processTerminatedDelay() {
try {
// sleep to allow vagrant to unlock queries.
Thread.sleep(3000);
} catch( InterruptedException ie) {
// Ignore and continue
}
}
}