package codeine.command_peer;
import codeine.api.NodeWithPeerInfo;
import codeine.configuration.Links;
import codeine.jsons.command.CommandInfo;
import codeine.jsons.command.CommandInfoForSpecificNode;
import codeine.jsons.project.ProjectJson;
import codeine.model.Constants;
import codeine.model.Constants.UrlParameters;
import codeine.model.ExitStatus;
import codeine.permissions.IUserWithPermissions;
import codeine.utils.ExceptionUtils;
import codeine.utils.ThreadUtils;
import codeine.utils.logging.LogUtils;
import codeine.utils.network.HttpUtils;
import com.google.common.base.Function;
import com.google.gson.Gson;
import org.apache.log4j.Logger;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class PeerCommandWorker implements Runnable {
private static final Logger log = Logger.getLogger(PeerCommandWorker.class);
private NodeWithPeerInfo node;
private AllNodesCommandExecuter allNodesCommandExecuter;
private boolean shouldOutputImmediatly;
private CommandInfo command_info;
private Links links;
private boolean success = false;
private boolean POST = true;
private ProjectJson project;
private static Pattern pattern = Pattern.compile(".*" + Constants.COMMAND_RESULT + "(-?\\d+).*");
private IUserWithPermissions userObject;
private boolean failedReported = false;
public PeerCommandWorker(NodeWithPeerInfo node, AllNodesCommandExecuter allNodesCommandExecuter, CommandInfo command_info, boolean shouldOutputImmediatly, Links links, ProjectJson project, IUserWithPermissions userObject) {
this.node = node;
this.allNodesCommandExecuter = allNodesCommandExecuter;
this.command_info = command_info;
this.shouldOutputImmediatly = shouldOutputImmediatly;
this.links = links;
this.project = project;
this.userObject = userObject;
}
@Override
public void run() {
try {
execute();
} finally {
}
}
private long getSleepTime() {
return 100;
}
private void execute() {
if (noPermissions()) {
announce("no permissions for user " + userObject.user().username() + " on node " + node.alias() + "!");
}
else {
executeInternal();
}
allNodesCommandExecuter.workerFinished();
}
private void executeInternal() {
String url = links.getPeerLink(node.peer_address()) + Constants.COMMAND_NODE_CONTEXT;
log.info("commandNode " + allNodesCommandExecuter.commandString() + " for " + node.alias() + " url is " + url);
try {
ThreadUtils.sleep(getSleepTime());
log.debug("running worker " + node);
final StringBuilder result = new StringBuilder();
Function<String, Void> function = new ReadCommandOutputFunction(result);
if (shouldOutputImmediatly){
writeNodeHeader();
}
if (POST && !command_info.name().equals("upgrade_old_peers")) {
String key = userObject.user().encodedApiTokenWithTime();
CommandInfoForSpecificNode command_info2 = new CommandInfoForSpecificNode(node.name(), node.alias(), null, key, project.environmentVariables());
log.info("Post data of command is " + command_info2.toString());
String postData = UrlParameters.DATA_NAME + "=" + HttpUtils.encodeURL(new Gson().toJson(command_info))
+"&" + UrlParameters.DATA_ADDITIONAL_COMMAND_INFO_NAME + "=" + HttpUtils.encodeURL(new Gson().toJson(command_info2));
HttpUtils.doPOST(url, postData, function,null);
}
else {
String link = links.getPeerCommandLink(node.peer_address(), "codeine", "switch-version", "beta");
HttpUtils.doGET(link, function,null, HttpUtils.READ_TIMEOUT_MILLI);
}
if (result.length() > 0) {
String finishedMessage = "command " + (success ? "succeeded" : "failed") + " on node " + node.alias();
if (!shouldOutputImmediatly){
allNodesCommandExecuter.writeLine(getAnnounceMessage(getHeaderMessage()) + "\n" + result.toString() + "\n" + finishedMessage);
}
else {
announce(finishedMessage);
}
} else {
announce("result is empty for node " + node.alias());
}
if (!success) {
nodeFailed();
}
} catch (Exception ex) {
announce("error in node " + node.alias() + " message: " + ExceptionUtils.getRootCause(ex).getMessage());
log.warn("error in node with link " + url + " ; message "
+ ExceptionUtils.getRootCause(ex).getMessage());
log.debug("error details", ex);
nodeFailed();
}
if (command_info.block_after_execution_minutes() != null && command_info.block_after_execution_minutes() > 0) {
announce("will wait after executing command on " + node.alias() + ". waiting " + command_info.block_after_execution_minutes() + " minute(s)");
ThreadUtils.sleep(TimeUnit.MINUTES.toMillis(command_info.block_after_execution_minutes()));
}
}
private void nodeFailed() {
if (!failedReported) {
allNodesCommandExecuter.fail(node);
failedReported = true;
}
else {
LogUtils.assertFailed(log, "nodeFailed reported more than once");
}
}
private boolean noPermissions() {
return !userObject.canCommand(project.name(), node.alias());
}
private void writeNodeHeader() {
announce(getHeaderMessage());
}
private String getHeaderMessage() {
return "executed on node: " + node.alias() + ", output below";
}
private void announce(String line) {
allNodesCommandExecuter.writeLine(getAnnounceMessage(line));
}
private String getAnnounceMessage(String line) {
return "===> " + line + " <===";
}
private final class ReadCommandOutputFunction implements Function<String, Void> {
private final StringBuilder result;
private ReadCommandOutputFunction(StringBuilder result) {
this.result = result;
}
@Override
public Void apply(String line) {
Matcher matcher = pattern.matcher(line.replace("\n", ""));
if (matcher.matches()) {
int exitStatus = Integer.valueOf(matcher.group(1));
if (ExitStatus.SUCCESS != exitStatus) {
line = "\nCommand failed with exit status " + exitStatus;
if (exitStatus <= 0) {
line += " (" + ExitStatus.fromInt(exitStatus) + ")";
}
line += "\n";
} else {
allNodesCommandExecuter.nodeSuccess(node);
success = true;
}
}
if (!success) {//failed or not finished
if (shouldOutputImmediatly) {
allNodesCommandExecuter.writeLine(line);
}
result.append(line + "\n");
}
return null;
}
}
}