All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.kuali.common.deploy.DeployUtils Maven / Gradle / Ivy

The newest version!
package org.kuali.common.deploy;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Properties;

import org.apache.commons.io.IOUtils;
import org.codehaus.plexus.util.StringUtils;
import org.kuali.common.util.Assert;
import org.kuali.common.util.FormatUtils;
import org.kuali.common.util.LocationUtils;
import org.kuali.common.util.MonitorTextFileResult;
import org.kuali.common.util.ThreadUtils;
import org.kuali.common.util.UnixCmds;
import org.kuali.common.util.UnixProcess;
import org.kuali.common.util.log.LoggerLevel;
import org.kuali.common.util.log.LoggerUtils;
import org.kuali.common.util.property.Constants;
import org.kuali.common.util.secure.channel.RemoteFile;
import org.kuali.common.util.secure.channel.Result;
import org.kuali.common.util.secure.channel.SecureChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.util.PropertyPlaceholderHelper;

public class DeployUtils {

	private static final Logger logger = LoggerFactory.getLogger(DeployUtils.class);

	private static final String CMD = "CMD";
	private static final String TRAVERSE_SYMBOLIC_LINKS = "-L";
	private static final UnixCmds CMDS = new UnixCmds();
	private static final PropertyPlaceholderHelper HELPER = Constants.DEFAULT_PROPERTY_PLACEHOLDER_HELPER;

	/**
	 * Examine the contents of a text file, stopping as soon as it contains token, or timeout is exceeded, whichever comes first.
	 */
	public static MonitorTextFileResult monitorTextFile(SecureChannel channel, String path, String token, int intervalMillis, int timeoutMillis, String encoding) {

		// Make sure we are configured correctly
		Assert.notNull(channel, "channel is null");
		Assert.notNull(path, "path is null");
		Assert.hasText(token, "token has no text");
		Assert.hasText(encoding, "encoding has no text");
		Assert.isTrue(intervalMillis > 0, "interval must be a positive integer");
		Assert.isTrue(timeoutMillis > 0, "timeout must be a positive integer");

		// Setup some member variables to record what happens
		long start = System.currentTimeMillis();
		long stop = start + timeoutMillis;
		boolean exists = false;
		boolean contains = false;
		boolean timeoutExceeded = false;
		long now = -1;
		String content = null;

		// loop until timeout is exceeded or we find the token inside the file
		for (;;) {

			// Always pause (unless this is the first iteration)
			if (now != -1) {
				ThreadUtils.sleep(intervalMillis);
			}

			// Check to make sure we haven't exceeded our timeout limit
			now = System.currentTimeMillis();
			if (now > stop) {
				timeoutExceeded = true;
				break;
			}

			// If the file does not exist yet, there is nothing more we can do
			exists = channel.exists(path);
			if (!exists) {
				continue;
			}

			// The file exists, check to see if the token we are looking for is present in the file
			RemoteFile remoteFile = new RemoteFile.Builder(path).build();
			content = channel.toString(remoteFile);
			contains = StringUtils.contains(content, token);
			if (contains) {
				// We found what we are looking for, we are done
				break;
			}
		}

		// Record how long the overall process took
		long elapsed = now - start;

		// Fill in a pojo detailing what happened
		MonitorTextFileResult mtfr = new MonitorTextFileResult(exists, contains, timeoutExceeded, elapsed);
		mtfr.setAbsolutePath(path);
		mtfr.setContent(content);
		return mtfr;
	}

	public static void killMatchingProcesses(SecureChannel channel, String user, String cmd, String processLabel) {
		List processes = getUnixProcesses(channel, user);

		// No existing processes, we are done
		if (processes.size() == 0) {
			logger.info("  no running processes for user [{}]", user);
			return;
		}

		// Figure out if any of the running processes are matches
		List matches = getMatchingProcesses(processes, cmd);

		if (CollectionUtils.isEmpty(matches)) {
			// Nothing to do
			logger.info("  no machine agents detected. total running processes - {}", processes.size());
			return;
		} else {
			// Kill any matching processes
			for (UnixProcess match : matches) {
				logger.info("  killing {} - [pid:{}]", processLabel, match.getProcessId());
				kill(channel, match);
			}
		}
	}

	/**
	 * Execute cmd as user using nohup and running it in the background.
	 */
	public static String getNohupBackgroundProcess(String user, String cmd) {
		StringBuilder sb = new StringBuilder();
		sb.append("su");
		sb.append(" - ");
		sb.append(user);
		sb.append(" ");
		sb.append("--command");
		sb.append("=");
		sb.append("'");
		sb.append(CMDS.nohup(cmd));
		sb.append(" ");
		sb.append("&");
		sb.append("'");
		return sb.toString();
	}

	public static void copyFiles(SecureChannel channel, List deployables, Properties filterProperties) {
		if (CollectionUtils.isEmpty(deployables)) {
			return;
		}
		for (Deployable deployable : deployables) {
			RemoteFile destination = new RemoteFile.Builder(deployable.getRemote()).build();
			String location = deployable.getLocal();
			logger.info("  creating -> [{}]", destination.getAbsolutePath());
			if (deployable.isFilter()) {
				long start = System.currentTimeMillis();
				String originalContent = LocationUtils.toString(location);
				String resolvedContent = HELPER.replacePlaceholders(originalContent, filterProperties);
				channel.copyStringToFile(resolvedContent, destination);
				String elapsed = FormatUtils.getTime(System.currentTimeMillis() - start);
				Object[] args = { filterProperties.size(), location, destination.getAbsolutePath(), elapsed };
				logger.debug("Used {} properties to filter [{}] -> [{}] - {}", args);
			} else {
				long start = System.currentTimeMillis();
				channel.copyLocationToFile(location, destination);
				logCopy(location, destination.getAbsolutePath(), System.currentTimeMillis() - start);
			}
			if (deployable.getPermissions().isPresent()) {
				String path = deployable.getRemote();
				String perms = deployable.getPermissions().get();
				String command = CMDS.chmod(perms, path);
				executePathCommand(channel, command, path);
			}
		}
	}

	protected static void logCopy(String src, String dst, long elapsed) {
		String rate = "";
		String size = "";
		if (LocationUtils.isExistingFile(src)) {
			long bytes = new File(src).length();
			rate = FormatUtils.getRate(elapsed, bytes);
			size = FormatUtils.getSize(bytes);
		}
		Object[] args = { dst, size, FormatUtils.getTime(elapsed), rate };
		logger.debug("Source -> [{}]", src);
		logger.debug("  created [{}] - [{} {} {}]", args);
	}

	/**
	 * Return a list of any processes where the command exactly matches the command passed in.
	 */
	public static List getMatchingProcesses(List processes, String command) {
		List matches = new ArrayList();
		for (UnixProcess process : processes) {
			if (StringUtils.equals(process.getCommand(), command)) {
				matches.add(process);
			}
		}
		return matches;
	}

	/**
	 * Output looks like this:
	 * 
	 * 
	 *   UID        PID  PPID  C STIME TTY          TIME CMD
	 * 	 tomcat   15461 15460  0 22:51 pts/0    00:00:00 -bash
	 * 	 tomcat   15480 15461  0 22:52 pts/0    00:00:02 java -jar /usr/local/machine-agent/machineagent.jar
	 * 
*/ public static List getUnixProcesses(Result result) { // Convert stdout to a list of strings List lines = getOutputLines(result); // Make sure there is at least a header line Assert.isFalse(CollectionUtils.isEmpty(lines), "There should be a header line"); // If there are no processes running, exit value is 1 if (lines.size() == 1 && result.getExitValue() == 1) { // return an empty list return Collections.emptyList(); } // Make sure exit value was zero validateResult(result); // Need the header line in order to parse the process lines String header = lines.get(0); // Setup some storage for the list of running processes List processes = new ArrayList(); // Convert each line into a UnixProcess pojo for (int i = 1; i < lines.size(); i++) { // Extract a line String line = lines.get(i); // Convert to a pojo UnixProcess process = getUnixProcess(header, line); // Add to the list processes.add(process); } // return what we've found return processes; } /** * Output looks like this: * *
	 *   UID        PID  PPID  C STIME TTY          TIME CMD
	 * 	 tomcat   15461 15460  0 22:51 pts/0    00:00:00 -bash
	 * 	 tomcat   15480 15461  0 22:52 pts/0    00:00:02 java -jar /usr/local/machine-agent/machineagent.jar
	 * 
*/ public static UnixProcess getUnixProcess(String header, String line) { // Split the strings up into tokens String[] tokens = StringUtils.split(line, " "); // First token is the user id String userId = StringUtils.trim(tokens[0]); // Second token is the process id String processId = StringUtils.trim(tokens[1]); // The command starts where "CMD" starts in the header line int pos = header.indexOf(CMD); // Make sure we found the string "CMD" Assert.isFalse(pos == -1, "[" + line + "] does not contain [" + CMD + "]"); // This is the command used to launch the process String command = StringUtils.trim(StringUtils.substring(line, pos)); // Store the information we've parsed out into pojo UnixProcess process = new UnixProcess(); process.setUserId(userId); process.setProcessId(Integer.parseInt(processId)); process.setCommand(command); return process; } public static Result executeCommand(SecureChannel channel, String command, boolean validateResult) { Result result = channel.executeCommand(command); if (validateResult) { validateResult(result); } return result; } public static void kill(SecureChannel channel, UnixProcess process) { String command = CMDS.kill(process.getProcessId()); Result result = channel.executeCommand(command); logResult(result, logger, LoggerLevel.DEBUG); validateResult(result); } public static List getUnixProcesses(SecureChannel channel, String user) { String command = CMDS.psf(user); Result result = channel.executeCommand(command); return getUnixProcesses(result); } public static Result runscript(SecureChannel channel, String username, String script) { return executeCommand(channel, CMDS.su(username, script), true); } public static Result runscript(SecureChannel channel, String username, String script, boolean validateExitValue) { return executeCommand(channel, CMDS.su(username, script), validateExitValue); } public static Result delete(SecureChannel channel, List paths) { return executePathCommand(channel, CMDS.rmrf(paths), paths); } public static Result mkdirs(SecureChannel channel, List paths) { return executePathCommand(channel, CMDS.mkdirp(paths), paths); } public static Result chown(SecureChannel channel, String owner, String group, List paths) { List options = Arrays.asList(TRAVERSE_SYMBOLIC_LINKS); String cmd = CMDS.chownr(options, owner, group, paths); return executePathCommand(channel, cmd, paths); } public static void executePathCommand(SecureChannel channel, String command, String path) { executePathCommand(channel, command, Collections.singletonList(path)); } public static Result executePathCommand(SecureChannel channel, String command, List paths) { Result result = channel.executeCommand(command); validateResult(result); return result; } public static List getOutputLines(Result result) { try { return IOUtils.readLines(LocationUtils.getBufferedReaderFromString(result.getStdout())); } catch (IOException e) { throw new IllegalArgumentException("Unexpected IO error", e); } } public static void logResult(Result result, Logger logger, LoggerLevel level) { LoggerUtils.logLines("[" + result.getCommand() + "] - " + FormatUtils.getTime(result.getElapsed()), logger, level); LoggerUtils.logLines(result.getStdout(), logger, level); LoggerUtils.logLines(result.getStderr(), logger, LoggerLevel.WARN); if (result.getExitValue() != 0) { logger.warn("Exit value = {}", result.getExitValue()); } } public static void logResult(Result result, Logger logger) { logResult(result, logger, LoggerLevel.INFO); } public static void validateResult(Result result) { validateResult(result, Arrays.asList(0)); logger.trace("Result is valid"); } public static void validateResult(Result result, List exitValues) { for (Integer exitValue : exitValues) { if (exitValue.equals(result.getExitValue())) { return; } } throw new IllegalStateException("Exit value " + result.getExitValue() + " is not allowed"); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy