/*************************** GO-LICENSE-START*********************************
* Copyright 2016 ThoughtWorks, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ************************GO-LICENSE-END***********************************/
package com.thoughtworks.go.buildsession;
import com.thoughtworks.go.domain.BuildCommand;
import com.thoughtworks.go.util.SystemUtil;
import com.thoughtworks.go.util.command.CommandLine;
import com.thoughtworks.go.util.command.CommandLineException;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import static java.lang.String.format;
public class ExecCommandExecutor implements BuildCommandExecutor {
private static final Logger LOG = LoggerFactory.getLogger(ExecCommandExecutor.class);
@Override
public boolean execute(BuildCommand command, BuildSession buildSession) {
File workingDir = buildSession.resolveRelativeDir(command.getWorkingDirectory());
if (!workingDir.isDirectory()) {
String message = "Working directory \"" + workingDir.getAbsolutePath() + "\" is not a directory!";
LOG.error(message);
buildSession.println(message);
return false;
}
String cmd = command.getStringArg("command");
String[] args = command.getArrayArg("args");
Map<String, String> secrets = buildSession.getSecretSubstitutions();
Set<String> leftSecrets = new HashSet<>(secrets.keySet());
CommandLine commandLine = createCommandLine(cmd);
for (String arg : args) {
if(secrets.containsKey(arg)) {
leftSecrets.remove(arg);
commandLine.withArg(new SubstitutableCommandArgument(arg, secrets.get(arg)));
} else {
commandLine.withArg(arg);
}
}
for (String secret: leftSecrets) {
commandLine.withNonArgSecret(new SecretSubstitution(secret, secrets.get(secret)));
}
commandLine.withWorkingDir(workingDir);
commandLine.withEnv(buildSession.getEnvs());
return executeCommandLine(buildSession, commandLine) == 0;
}
private CommandLine createCommandLine(String cmd) {
CommandLine commandLine;
if (SystemUtil.isWindows()) {
commandLine = CommandLine.createCommandLine("cmd");
commandLine.withArg("/c");
commandLine.withArg(StringUtils.replace(cmd, "/", "\\"));
} else {
commandLine = CommandLine.createCommandLine(cmd);
}
return commandLine;
}
private int executeCommandLine(final BuildSession buildSession, final CommandLine commandLine) {
final AtomicInteger exitCode = new AtomicInteger(-1);
final CountDownLatch canceledOrDone = new CountDownLatch(1);
buildSession.submitRunnable(new Runnable() {
@Override
public void run() {
try {
exitCode.set(commandLine.run(buildSession.processOutputStreamConsumer(), null));
} catch (CommandLineException e) {
LOG.error("Command failed", e);
String message = format("Error happened while attempting to execute '%s'. \nPlease make sure [%s] can be executed on this agent.\n", commandLine.toStringForDisplay(), commandLine.getExecutable());
String path = System.getenv("PATH");
buildSession.println(message);
buildSession.println(format("[Debug Information] Environment variable PATH: %s", path));
LOG.error(format("[Command Line] %s. Path: %s", message, path));
} finally {
canceledOrDone.countDown();
}
}
});
Future<?> cancelMonitor = buildSession.submitRunnable(new Runnable() {
@Override
public void run() {
try {
buildSession.waitUntilCanceled();
} catch (InterruptedException e) {
// ignore
} finally {
canceledOrDone.countDown();
}
}
});
try {
canceledOrDone.await();
} catch (InterruptedException e) {
LOG.error("Building thread interrupted", e);
}
cancelMonitor.cancel(true);
return exitCode.get();
}
}