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

com.liferay.jenkins.results.parser.FilePropagator Maven / Gradle / Ivy

/**
 * SPDX-FileCopyrightText: (c) 2000 Liferay, Inc. https://liferay.com
 * SPDX-License-Identifier: LGPL-2.1-or-later OR LicenseRef-Liferay-DXP-EULA-2.0.0-2023-06
 */

package com.liferay.jenkins.results.parser;

import java.io.IOException;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeoutException;

/**
 * @author Peter Yoo
 */
public class FilePropagator {

	public FilePropagator(
		String[] fileNames, String sourceDirName, String targetDirName,
		List targetSlaves) {

		this(fileNames, sourceDirName, targetDirName, null, targetSlaves);
	}

	public FilePropagator(
		String[] fileNames, String sourceDirName, String targetDirName,
		String primaryTargetSlave, List targetSlaves) {

		this(
			fileNames, sourceDirName, targetDirName, primaryTargetSlave,
			targetSlaves, _TIMEOUT_DEFAULT);
	}

	public FilePropagator(
		String[] fileNames, String sourceDirName, String targetDirName,
		String primaryTargetSlave, List targetSlaves, long timeout) {

		_timeout = timeout;

		synchronized (_instanceCount) {
			Thread thread = Thread.currentThread();

			_id = JenkinsResultsParserUtil.combine(
				String.valueOf(thread.getId()), "-",
				String.valueOf(_instanceCount++));
		}

		for (String fileName : fileNames) {
			_filePropagatorTasks.add(
				new FilePropagatorTask(
					sourceDirName + "/" + fileName,
					targetDirName + "/" + fileName));
		}

		if (primaryTargetSlave != null) {
			targetSlaves.remove(primaryTargetSlave);

			_targetSlaves.add(primaryTargetSlave);
		}

		_targetSlaves.addAll(targetSlaves);
	}

	public long getAverageThreadDuration() {
		if (_threadsCompletedCount == 0) {
			return 0;
		}

		return _threadsDurationTotal / _threadsCompletedCount;
	}

	public List getErrorSlaves() {
		return _errorSlaves;
	}

	public void setPostDistCommand(String postDistCommand) {
		_postDistCommand = postDistCommand;
	}

	public void setPreDistCommand(String preDistCommand) {
		_preDistCommand = preDistCommand;
	}

	public void start(int threadCount) {
		_copyFromSource();

		ExecutorService executorService = Executors.newFixedThreadPool(
			threadCount);

		log("File propagation starting with " + threadCount + " threads.");

		try {
			String previousString = null;
			long start = JenkinsResultsParserUtil.getCurrentTimeMillis();

			while (!_targetSlaves.isEmpty() || !_busySlaves.isEmpty()) {
				synchronized (this) {
					for (String mirrorSlave : _mirrorSlaves) {
						if (_targetSlaves.isEmpty()) {
							break;
						}

						String targetSlave = _targetSlaves.remove(0);

						executorService.execute(
							new FilePropagatorThread(
								this, mirrorSlave, targetSlave));

						_busySlaves.add(mirrorSlave);
						_busySlaves.add(targetSlave);
					}

					_mirrorSlaves.removeAll(_busySlaves);
				}

				StringBuffer sb = new StringBuffer();

				sb.append("Average thread duration: ");
				sb.append(
					JenkinsResultsParserUtil.toDurationString(
						getAverageThreadDuration()));
				sb.append("\nBusy slaves:");
				sb.append(_busySlaves.size());
				sb.append("\nMirror slaves:");
				sb.append(_mirrorSlaves.size());
				sb.append("\nTarget slaves:");
				sb.append(_targetSlaves.size());

				String currentString = sb.toString();

				if (Objects.equals(previousString, currentString)) {
					continue;
				}

				sb.append("\nTotal duration: ");

				long currentTime =
					JenkinsResultsParserUtil.getCurrentTimeMillis();

				sb.append(
					JenkinsResultsParserUtil.toDurationString(
						currentTime - start));

				sb.append("\n");

				log(sb.toString());

				previousString = currentString;

				JenkinsResultsParserUtil.sleep(5000);
			}

			long duration =
				JenkinsResultsParserUtil.getCurrentTimeMillis() - start;

			log(
				JenkinsResultsParserUtil.combine(
					"File propagation completed in ",
					JenkinsResultsParserUtil.toDurationString(duration), "."));

			if (!_errorSlaves.isEmpty()) {
				System.out.println(
					_errorSlaves.size() + " slaves failed to respond:\n" +
						_errorSlaves);
			}
		}
		finally {
			executorService.shutdown();
		}
	}

	protected void log(String message) {
		System.out.print("File propagator ID: ");
		System.out.print(_id);

		if (message.contains("\n")) {
			System.out.print("\n");
		}
		else {
			System.out.print(" - ");
		}

		System.out.println(message);
	}

	private void _copyFromSource() {
		if (_filePropagatorTasks.isEmpty() || _targetSlaves.isEmpty()) {
			return;
		}

		List commands = new ArrayList<>();

		for (FilePropagatorTask filePropagatorTask : _filePropagatorTasks) {
			String sourceFileName = filePropagatorTask._sourceFileName;

			log("Copying from source " + sourceFileName);

			String targetFileName = filePropagatorTask._targetFileName;

			commands.add(_getMkdirCommand(targetFileName));

			if (sourceFileName.startsWith("http")) {
				StringBuilder sb = new StringBuilder();

				sb.append("curl ");

				try {
					if (sourceFileName.startsWith(
							"https://release.liferay.com")) {

						sb.append(" -u ");
						sb.append(
							JenkinsResultsParserUtil.getBuildProperty(
								"jenkins.admin.user.name"));
						sb.append(":");
						sb.append(
							JenkinsResultsParserUtil.getBuildProperty(
								"jenkins.admin.user.password"));
					}
				}
				catch (IOException ioException) {
					throw new FilePropagatorRuntimeException(
						this, "Unable to get jenkins-admin user credentials",
						ioException);
				}

				sb.append("-o ");
				sb.append(targetFileName);
				sb.append(" ");
				sb.append(sourceFileName);

				commands.add(sb.toString());
			}
			else {
				commands.add(
					"rsync -Iqs " + sourceFileName + " " + targetFileName);
			}

			String targetDirName = targetFileName.substring(
				0, targetFileName.lastIndexOf("/"));

			commands.add("ls -al " + targetDirName);
		}

		String targetSlave = _targetSlaves.remove(0);

		try {
			if (_executeBashCommands(commands, targetSlave) != 0) {
				_errorSlaves.add(targetSlave);

				_copyFromSource();
			}
			else {
				_mirrorSlaves.add(targetSlave);
			}
		}
		catch (Exception exception) {
			throw new FilePropagatorRuntimeException(
				this, "Unable to copy from source. Executed: " + commands,
				exception);
		}

		log("Finished copying from source.");
	}

	private int _executeBashCommands(List commands, String targetSlave)
		throws IOException, TimeoutException {

		StringBuffer sb = new StringBuffer();

		sb.append("ssh -o ConnectTimeout=");
		sb.append(_timeout / (60 * 1000));
		sb.append(" -o NumberOfPasswordPrompts=0 ");
		sb.append(targetSlave);
		sb.append(" '");

		if (!JenkinsResultsParserUtil.isNullOrEmpty(_preDistCommand)) {
			sb.append(_preDistCommand);
			sb.append(" ; ");
		}

		for (int i = 0; i < commands.size(); i++) {
			sb.append(commands.get(i));

			if (i < (commands.size() - 1)) {
				sb.append(" ; ");
			}
		}

		if (!JenkinsResultsParserUtil.isNullOrEmpty(_postDistCommand)) {
			sb.append(" ; ");
			sb.append(_postDistCommand);
		}

		sb.append("'");

		Process process = JenkinsResultsParserUtil.executeBashCommands(
			_timeout * 60 * 1000, sb.toString());

		return process.exitValue();
	}

	private String _getMkdirCommand(String fileName) {
		String dirName = fileName.substring(0, fileName.lastIndexOf("/") + 1);

		return "mkdir -p " + dirName;
	}

	private static final long _TIMEOUT_DEFAULT = 15 * 60 * 1000;

	private static Integer _instanceCount = 0;

	private final List _busySlaves = new ArrayList<>();
	private final List _errorSlaves = new ArrayList<>();
	private final List _filePropagatorTasks =
		new ArrayList<>();
	private final String _id;
	private final List _mirrorSlaves = new ArrayList<>();
	private String _postDistCommand;
	private String _preDistCommand;
	private final List _targetSlaves = new ArrayList<>();
	private int _threadsCompletedCount;
	private long _threadsDurationTotal;
	private final long _timeout;

	private static class FilePropagatorRuntimeException
		extends RuntimeException {

		public FilePropagatorRuntimeException(
			FilePropagator filePropagator, String message,
			Exception exception) {

			super(filePropagator._id + " - " + message, exception);
		}

	}

	private static class FilePropagatorTask {

		private FilePropagatorTask(
			String sourceFileName, String targetFileName) {

			_sourceFileName = _escapeParentheses(sourceFileName);
			_targetFileName = _escapeParentheses(targetFileName);
		}

		private String _escapeParentheses(String fileName) {
			fileName = fileName.replace(")", "\\)");
			fileName = fileName.replace("(", "\\(");

			return fileName;
		}

		private final String _sourceFileName;
		private final String _targetFileName;

	}

	private static class FilePropagatorThread implements Runnable {

		@Override
		public void run() {
			long start = JenkinsResultsParserUtil.getCurrentTimeMillis();

			List filePropagatorTasks =
				_filePropagator._filePropagatorTasks;

			List commands = new ArrayList<>(filePropagatorTasks.size());

			for (FilePropagatorTask filePropagatorTask : filePropagatorTasks) {
				commands.add(
					_filePropagator._getMkdirCommand(
						filePropagatorTask._targetFileName));

				commands.add(
					"rsync -Iqs " + _mirrorSlave + ":" +
						filePropagatorTask._targetFileName + " " +
							filePropagatorTask._targetFileName);
			}

			try {
				int value = _filePropagator._executeBashCommands(
					commands, _targetSlave);

				_successful = value == 0;
			}
			catch (Exception exception) {
				_successful = false;
			}

			_duration = JenkinsResultsParserUtil.getCurrentTimeMillis() - start;

			synchronized (_filePropagator) {
				_filePropagator._busySlaves.remove(_mirrorSlave);
				_filePropagator._busySlaves.remove(_targetSlave);
				_filePropagator._mirrorSlaves.add(_mirrorSlave);
				_filePropagator._threadsCompletedCount++;
				_filePropagator._threadsDurationTotal += _duration;

				if (!_successful) {
					_filePropagator._errorSlaves.add(_targetSlave);

					return;
				}

				_filePropagator._mirrorSlaves.add(_targetSlave);
			}
		}

		private FilePropagatorThread(
			FilePropagator filePropagator, String mirrorSlave,
			String targetSlave) {

			_filePropagator = filePropagator;
			_mirrorSlave = mirrorSlave;
			_targetSlave = targetSlave;
		}

		private long _duration;
		private final FilePropagator _filePropagator;
		private final String _mirrorSlave;
		private boolean _successful;
		private final String _targetSlave;

	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy