Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JBIDE-21175 - ensure more reliable locating of vagrant command #831

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -10,29 +10,51 @@
******************************************************************************/
package org.jboss.tools.openshift.cdk.server.core.internal;

import org.eclipse.debug.core.ILaunchConfiguration;
import java.io.File;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.wst.server.core.IServer;
import org.jboss.tools.openshift.cdk.server.core.internal.adapter.controllers.IExternalLaunchConstants;
import org.jboss.tools.openshift.internal.common.core.util.CommandLocationBinary;

// TODO - allow customization of this location
public class CDKConstantUtility {
private static final String VAGRANT_LOCATION_LINUX = "/usr/bin/vagrant";


// Seems weird? See https://github.com/mitchellh/vagrant/issues/1652
private static final String VAGRANT_LOCATION_WINDOWS = "C:\\HashiCorp\\Vagrant\\bin\\vagrant.exe";

private static CommandLocationBinary binary;

public static String getVagrantLocation() {
return VAGRANT_LOCATION_LINUX;
return findVagrantLocation();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

im wondering if we are better of launching this similar to what @gercan and @ibuziuk been doing for npm and cordova cli tools.

i.e. call out via bash or cmd.exe making it up to the user to have it properly configured in PATH

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are times we call out via Runtime.exec() in which case the existing path is in control. But there are also times we use the external tools launch configuration, in which case we need to know the full location. In general, we use the launch config when we want a console to appear with the output, and we use Runtime.exec() when we just want it done in the background.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did some perf-testing on windows and linux. This method is only a little bit slow when it has to go out to which / where, and it will only perform this once per workspace, and only if vagrant isn't found in the path already. Everything's already cached, and it won't try a second time, so the perf. hit is very very very low and the ability to work around path errors is tremendous.

I believe this patch is for the best.

}

public static String getVagrantLocation(IServer server) {
try {
ILaunchConfiguration lc = server.getLaunchConfiguration(false, new NullProgressMonitor());
if( lc != null ) {
return lc.getAttribute(IExternalLaunchConstants.ATTR_LOCATION, getVagrantLocation());
if( server != null ) {
try {
ILaunchConfiguration lc = server.getLaunchConfiguration(false, new NullProgressMonitor());
if( lc != null ) {
String ret = lc.getAttribute(IExternalLaunchConstants.ATTR_LOCATION, (String)null);
if( ret != null && new File(ret).exists())
return ret;
}
} catch(CoreException ce) {
// ignore, this is non-critical
}
} catch(CoreException ce) {
// ignore, this is non-critical
}
return VAGRANT_LOCATION_LINUX;
return findVagrantLocation();
}

private static String findVagrantLocation() {
if( binary == null ) {
binary = new CommandLocationBinary("vagrant");
binary.addPlatformLocation(Platform.OS_LINUX, VAGRANT_LOCATION_LINUX);
binary.addPlatformLocation(Platform.OS_WIN32, VAGRANT_LOCATION_WINDOWS);
binary.setDefaultPlatform(Platform.OS_LINUX);
}
return binary.findLocation();
}
}
@@ -0,0 +1,81 @@
/*******************************************************************************
* 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 Incorporated - initial API and implementation
******************************************************************************/
package org.jboss.tools.openshift.internal.common.core.util;

import java.util.HashMap;

/**
* A class representing a binary available on multiple platforms.
*/
public class CommandLocationBinary {
private HashMap<String, String> commandMap;
private HashMap<String, String> defaultLocMap;
private String defaultPlatform;
private String commandName;
private String foundLoc = null;
private boolean searchFailed = false;

public CommandLocationBinary(String commandName) {
commandMap = new HashMap<String,String>();
defaultLocMap = new HashMap<String,String>();
this.commandName = commandName;
}

/**
* Add a default command location for a given platform.
*
* @param platform
* @param command
* @param loc
*/
public void addPlatformLocation(String platform, String loc) {
defaultLocMap.put(platform, loc);
}

public void addPlatformCommandName(String platform, String command) {
commandMap.put(platform, command);
}

/**
* Set which command / default location should be used in the event that
* the user is on an unexpected platform such as OS_AIX, it can use the command name
* and default location of a differing platform, such as OS_LINUX
*
* @param platform
*/
public void setDefaultPlatform(String platform) {
this.defaultPlatform = platform;
}

public String getCommand(String platform) {
return commandMap.containsKey(platform) ? commandMap.get(platform) : commandName;
}

public String getDefaultLoc(String platform) {
return defaultLocMap.containsKey(platform) ? defaultLocMap.get(platform) : defaultLocMap.get(defaultPlatform);
}

public String findLocation() {
return findLocation(2000);
}

public String findLocation(int timeout) {
if( foundLoc != null || searchFailed )
return foundLoc;

String searched = CommandLocationLookupStrategy.get().search(this, timeout);
if( searched == null ) {
searchFailed = true;
}
foundLoc = searched;
return searched;
}
}
@@ -0,0 +1,164 @@
/*******************************************************************************
* 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 Incorporated - initial API and implementation
******************************************************************************/
package org.jboss.tools.openshift.internal.common.core.util;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.concurrent.Callable;

import org.apache.commons.lang.StringUtils;
import org.eclipse.core.runtime.Platform;

public class CommandLocationLookupStrategy {
private static final String LINUX_WHICH = "which ";
private static final String WINDOWS_WHERE = "where ";
private static final String LINUX_PATHVAR = "PATH";
private static final String WINDOWS_PATHVAR = "Path";

public static final CommandLocationLookupStrategy WINDOWS_STRATEGY =
new CommandLocationLookupStrategy(WINDOWS_WHERE, ";", WINDOWS_PATHVAR, new String[]{".exe", ".com"});
public static final CommandLocationLookupStrategy LINUX_STRATEGY =
new CommandLocationLookupStrategy(LINUX_WHICH, ":", LINUX_PATHVAR, new String[]{});

public static CommandLocationLookupStrategy get() {
String os = Platform.getOS();
if( Platform.OS_WIN32.equals(os)) {
return WINDOWS_STRATEGY;
}
return LINUX_STRATEGY;
}

private String which, delim, pathvar;
private String[] suffixes;
public CommandLocationLookupStrategy(String which, String delim, String pathvar, String[] suffixes) {
this.which = which;
this.delim = delim;
this.pathvar = pathvar;
this.suffixes = suffixes;
}

public String search(CommandLocationBinary binary) {
return search(binary, 2000);
}

public String search(CommandLocationBinary binary, int timeout) {
String cmd = binary.getCommand(Platform.getOS());
String defaultLoc = binary.getDefaultLoc(Platform.getOS());
return findLocation(defaultLoc, cmd, which, delim, pathvar, timeout);
}

/**
* This method will try to find the given command.
*
* If the default location exists, it will use that.
*
* It will then attempt to search for the command name (with all possible suffixes)
* somewhere in the system path.
*
* If that still fails, it will run one where / which command to locate the command.
* This will be called without the suffix.
*
* @param defaultLoc
* @param cmd
* @param which
* @param delim
* @param pathvar
* @param timeout
* @return
*/
private String findLocation(String defaultLoc, String cmd, String which, String delim, String pathvar, int timeout) {
if( defaultLoc != null && new File(defaultLoc).exists()) {
return defaultLoc;
}
String ret = searchPath(System.getenv(pathvar), delim, cmd);
if( ret == null ) {
ret = runCommand(which + cmd, timeout);
}
return ret;
}

/**
* Get all possible command names by appending the various suffixes to the command name
* @param commandName
* @return
*/
private String[] getPossibleCommandNames(String commandName) {
ArrayList<String> ret = new ArrayList<String>(5);
ret.add(commandName);
for( int i = 0; i < suffixes.length; i++ ) {
ret.add(commandName + suffixes[i]);
}
return (String[]) ret.toArray(new String[ret.size()]);
}

private String searchPath(String path, String delim, String commandName) {
String[] roots = path.split(delim);
String[] withSuffixes = getPossibleCommandNames(commandName);
for( int i = 0; i < roots.length; i++ ) {
for( int j = 0; j < withSuffixes.length; j++ ) {
File test = new File(roots[i], withSuffixes[j]);
if( test.exists()) {
return test.getAbsolutePath();
}
}
}
return null;
}

private String runCommand(final String cmd, int timeout) {
if( timeout == -1 ) {
return runCommand(cmd);
} else {
String path = ThreadUtils.runWithTimeout(timeout, new Callable<String>() {
@Override
public String call() throws Exception {
return runCommand(cmd);
}
});
return path;
}
}

private String runCommand(String cmd) {

Process p = null;
try {
p = Runtime.getRuntime().exec(cmd);
try {
p.waitFor();
} catch(InterruptedException ie) {
// Ignore, expected
}
if(p.exitValue() == 0) {
InputStream is = p.getInputStream();
java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
String cmdOutput = s.hasNext() ? s.next() : "";
if( !cmdOutput.isEmpty()) {
cmdOutput = StringUtils.trim(cmdOutput);
if( new File(cmdOutput).exists())
return cmdOutput;
}
}

} catch(IOException ioe) {
// Ignore this
} finally {
if( p != null ) {
p.destroy();
}
}
return null;
}


}