Added ability to copy artifacts, CrossJeeves is now working

This commit is contained in:
Flare Microsystems
2024-11-14 19:44:01 -08:00
parent db4d12c47a
commit f67892eac7
9 changed files with 331 additions and 35 deletions

View File

@@ -1,10 +1,14 @@
package com.flaremicro.crossjeeves; package com.flaremicro.crossjeeves;
import java.io.BufferedInputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.io.StringReader; import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Properties; import java.util.Properties;
@@ -25,7 +29,10 @@ import com.flaremicro.crossjeeves.net.NetworkHandler;
import com.flaremicro.crossjeeves.net.StreamForwarder; import com.flaremicro.crossjeeves.net.StreamForwarder;
import com.flaremicro.crossjeeves.net.packet.Packet; import com.flaremicro.crossjeeves.net.packet.Packet;
import com.flaremicro.crossjeeves.net.packet.Packet3Clone; import com.flaremicro.crossjeeves.net.packet.Packet3Clone;
import com.flaremicro.crossjeeves.net.packet.Packet4FileData;
import com.flaremicro.crossjeeves.net.packet.Packet5Artifact;
import com.flaremicro.crossjeeves.net.packet.Packet7LogEntry; import com.flaremicro.crossjeeves.net.packet.Packet7LogEntry;
import com.flaremicro.util.FileUtils;
import com.flaremicro.util.Util; import com.flaremicro.util.Util;
import com.flaremicro.util.ZipUtils; import com.flaremicro.util.ZipUtils;
@@ -70,7 +77,7 @@ public class ScriptProcessor {
public void processScript(String script) throws ScriptProcessingException { public void processScript(String script) throws ScriptProcessingException {
workingDirectory = workspace; workingDirectory = workspace;
if(terminated) if (terminated)
throw new ScriptProcessingException("Processor has been terminated"); throw new ScriptProcessingException("Processor has been terminated");
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
try try
@@ -89,14 +96,14 @@ public class ScriptProcessor {
} }
catch (Exception ex) catch (Exception ex)
{ {
throw new ScriptProcessingException("Script threw an exception during processing" ,ex); throw new ScriptProcessingException("Script threw an exception during processing", ex);
} }
} }
private void processScriptNodes(NodeList nodeList) throws ScriptProcessingException { private void processScriptNodes(NodeList nodeList) throws ScriptProcessingException {
for (int i = 0; i < nodeList.getLength(); i++) for (int i = 0; i < nodeList.getLength(); i++)
{ {
if(terminated) if (terminated)
throw new ScriptProcessingException("Processor has been terminated"); throw new ScriptProcessingException("Processor has been terminated");
Node node = nodeList.item(i); Node node = nodeList.item(i);
Element e = getElement(node); Element e = getElement(node);
@@ -189,7 +196,7 @@ public class ScriptProcessor {
File file = netHandler.waitForFile(id); File file = netHandler.waitForFile(id);
if (file == null) if (file == null)
throw new ScriptProcessingException("File failed to transfer"); throw new ScriptProcessingException("File failed to transfer");
if(!ZipUtils.unzipDirectory(file, workspace)) if (!ZipUtils.unzipDirectory(file, workspace))
throw new ScriptProcessingException("File failed to decompress"); throw new ScriptProcessingException("File failed to decompress");
} }
@@ -229,7 +236,6 @@ public class ScriptProcessor {
processBuilder.directory(workspace); processBuilder.directory(workspace);
Map<String, String> environment = processBuilder.environment(); Map<String, String> environment = processBuilder.environment();
for (Entry<String, String> set : currentEnvironment.entrySet()) for (Entry<String, String> set : currentEnvironment.entrySet())
{ {
environment.put(set.getKey(), set.getValue()); environment.put(set.getKey(), set.getValue());
@@ -296,9 +302,94 @@ public class ScriptProcessor {
throw new ScriptProcessingException("Process returned exit code " + returnCode); throw new ScriptProcessingException("Process returned exit code " + returnCode);
} }
private void artifactProcessor(Element e) { private void artifactProcessor(Element e) throws ScriptProcessingException {
// TODO Auto-generated method stub NodeList nodeList = e.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++)
{
Element var = getElement(nodeList.item(i));
if (var == null)
continue;
String name = var.getTagName().trim();
if (name.equalsIgnoreCase("workspace"))
{
System.out.println("Collecting workspace artifacts...");
List<String> filePaths = new ArrayList<String>();
NodeList workspaceNodes = var.getChildNodes();
for (int j = 0; j < workspaceNodes.getLength(); j++)
{
workspaceNodes.item(j);
Element fileElement = getElement(workspaceNodes.item(j));
if (fileElement == null)
continue;
String fileCommand = fileElement.getTagName().trim();
if (fileCommand.equalsIgnoreCase("file"))
{
filePaths.add(fileElement.getTextContent());
}
else if (fileCommand.equalsIgnoreCase("files"))
{
String resolver = this.getAttribute(fileElement, "resolver", "wildcard").trim();
try
{
if (resolver.equalsIgnoreCase("wildcard"))
{
FileUtils.resolvePathsWildcard(workspace, fileElement.getTextContent(), filePaths);
}
else if (resolver.equalsIgnoreCase("regex"))
{
FileUtils.resolvePathsRegex(workspace, fileElement.getTextContent(), filePaths);
}
else
{
throw new ScriptProcessingException("Invalid file resolver: " + resolver);
}
}
catch (Exception ex)
{
throw new ScriptProcessingException("Failed to process files directive " + fileElement.getTextContent() + " with resolver " + resolver, ex);
}
}
}
if (filePaths.size() <= 0)
{
System.out.println("No workspace artifacts!");
continue;
}
BufferedInputStream fileStream = null;
try
{
System.out.println("Sending artifacts...");
long fileId = random.nextLong();
File zipFile = File.createTempFile("workspace-" + fileId, ".zip");
if (!ZipUtils.zipList(workspace, filePaths, zipFile))
throw new ScriptProcessingException("Failed to compress workspace artifacts");
Packet packet = new Packet5Artifact(fileId, zipFile.length(), ".");
netHandler.enqueue(packet);
fileStream = new BufferedInputStream(new FileInputStream(zipFile));
int read;
byte[] buffer = new byte[4096];
while ((read = fileStream.read(buffer)) > -1)
{
if (read == 0)
continue;
Packet4FileData dataPacket = new Packet4FileData(fileId, (short) read, buffer);
netHandler.enqueue(dataPacket);
}
packet = new Packet4FileData(fileId, (short) 0, new byte[] {});
netHandler.enqueue(packet);
netHandler.waitForPacketSend(packet);
System.out.println("Sent!");
}
catch (IOException ex)
{
throw new ScriptProcessingException("Failed to upload workspace artifacts", ex);
}
finally
{
Util.cleanClose(fileStream);
}
}
}
} }
public void processAsync(final String script) { public void processAsync(final String script) {
@@ -328,9 +419,9 @@ public class ScriptProcessor {
public void terminate() { public void terminate() {
terminated = true; terminated = true;
if(runningProcess != null) if (runningProcess != null)
runningProcess.destroy(); runningProcess.destroy();
if(this.executionThread != null) if (this.executionThread != null)
{ {
this.executionThread.interrupt(); this.executionThread.interrupt();
} }

View File

@@ -8,6 +8,8 @@ import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.Socket; import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import com.flaremicro.crossjeeves.net.packet.Packet; import com.flaremicro.crossjeeves.net.packet.Packet;
import com.flaremicro.crossjeeves.net.packet.Packet0Identify; import com.flaremicro.crossjeeves.net.packet.Packet0Identify;
@@ -25,6 +27,8 @@ import com.flaremicro.util.ZipUtils;
public class ClientHandler extends NetworkHandler { public class ClientHandler extends NetworkHandler {
private String script; private String script;
private List<Thread> runningThreads = new ArrayList<Thread>();
public ClientHandler(Socket socket, String script) throws IOException { public ClientHandler(Socket socket, String script) throws IOException {
super(socket); super(socket);
this.script = script; this.script = script;
@@ -42,6 +46,18 @@ public class ClientHandler extends NetworkHandler {
disconnect(INVALID_PACKET_RECIEVED.id, "Recieved invalid packet " + packet.getId() + " (Unknown)"); disconnect(INVALID_PACKET_RECIEVED.id, "Recieved invalid packet " + packet.getId() + " (Unknown)");
} }
@Override
public void doDisconnect(int err) {
super.doDisconnect(err);
synchronized (runningThreads)
{
for (Thread t : runningThreads)
{
t.interrupt();
}
}
}
@Override @Override
public void handlePacket(Packet0Identify packet) { public void handlePacket(Packet0Identify packet) {
if (packet.getProtocolVersion() != Packet.PROTOCOL_VERSION) if (packet.getProtocolVersion() != Packet.PROTOCOL_VERSION)
@@ -57,7 +73,7 @@ public class ClientHandler extends NetworkHandler {
@Override @Override
public void handlePacket(Packet1Status packet) { public void handlePacket(Packet1Status packet) {
if((packet.getFlags() & Packet1Status.BUSY) != 0) if ((packet.getFlags() & Packet1Status.BUSY) != 0)
{ {
disconnect(OUTDATED_AGENT.id, "Agent is too busy"); disconnect(OUTDATED_AGENT.id, "Agent is too busy");
} }
@@ -120,8 +136,50 @@ public class ClientHandler extends NetworkHandler {
} }
@Override @Override
public void handlePacket(Packet5Artifact packet) { public void handlePacket(final Packet5Artifact packet) {
try
{
System.out.println("Getting artifacts!");
final String workspace = System.getenv("WORKSPACE");
if (workspace == null || workspace.trim().length() <= 0)
{
disconnect(INVALID_SYSTEM_STATE.id, "Workspace is not defined");
}
this.beginFile(packet.getFileId(), packet.getFileSize());
Thread thread = new Thread(new Runnable() {
public void run() {
File file = waitForFile(packet.getFileId());
System.out.println("Got artifacts!");
if (file == null)
{
disconnect(FILE_DOWNLOAD_FAILURE.id, "Failed to download transferred file");
}
if (!ZipUtils.unzipDirectory(file, new File(workspace)))
{
disconnect(FILE_DOWNLOAD_FAILURE.id, "Failed to extract transferred file");
}
else
{
System.out.println("Extracted artifacts!");
}
synchronized (runningThreads)
{
runningThreads.remove(this);
}
}
});
synchronized (runningThreads)
{
runningThreads.add(thread);
}
thread.start();
}
catch (IOException e)
{
disconnect(FILE_DOWNLOAD_FAILURE.id, "Failed to create file for transfer");
}
} }
@Override @Override
@@ -152,10 +210,9 @@ public class ClientHandler extends NetworkHandler {
@Override @Override
public void handlePacket(Packet7LogEntry packet) { public void handlePacket(Packet7LogEntry packet) {
if(packet.getStdOutput() == Packet7LogEntry.STD_ERR) if (packet.getStdOutput() == Packet7LogEntry.STD_ERR)
System.err.println(packet.getLogEntry()); System.err.println("[AGENT] " + packet.getLogEntry());
else else System.out.println("[AGENT] " + packet.getLogEntry());
System.out.println(packet.getLogEntry());
} }
} }

View File

@@ -46,6 +46,22 @@ public abstract class NetworkHandler {
this.out = new DataOutputStream(socket.getOutputStream()); this.out = new DataOutputStream(socket.getOutputStream());
} }
public void waitForPacketSend(Packet packet) {
synchronized (packet)
{
if (!outbox.contains(packet))
return;
try
{
packet.wait();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
public File waitForFile(long fileId) { public File waitForFile(long fileId) {
try try
{ {
@@ -103,6 +119,11 @@ public abstract class NetworkHandler {
Util.cleanClose(in); Util.cleanClose(in);
Util.cleanClose(out); Util.cleanClose(out);
Util.cleanClose(socket); Util.cleanClose(socket);
for (FileTransferInfo transferInfo : downloadQueue.values())
{
Util.cleanClose(transferInfo.outputStream);
transferInfo.file.delete();
}
} }
public int getExitCode() { public int getExitCode() {
@@ -161,6 +182,10 @@ public abstract class NetworkHandler {
try try
{ {
packet.sendPacket(out); packet.sendPacket(out);
synchronized (packet)
{
packet.notifyAll();
}
} }
catch (IOException e) catch (IOException e)
{ {

View File

@@ -20,6 +20,7 @@ import com.flaremicro.crossjeeves.net.packet.Packet5Artifact;
import com.flaremicro.crossjeeves.net.packet.Packet4FileData; import com.flaremicro.crossjeeves.net.packet.Packet4FileData;
import com.flaremicro.crossjeeves.net.packet.Packet6Disconnect; import com.flaremicro.crossjeeves.net.packet.Packet6Disconnect;
import com.flaremicro.crossjeeves.net.packet.Packet7LogEntry; import com.flaremicro.crossjeeves.net.packet.Packet7LogEntry;
import com.flaremicro.util.FileUtils;
public class ServerHandler extends NetworkHandler { public class ServerHandler extends NetworkHandler {
private CrossJeevesHost host; private CrossJeevesHost host;
@@ -52,6 +53,7 @@ public class ServerHandler extends NetworkHandler {
scriptProcessor.terminate(); scriptProcessor.terminate();
host.removeConnection(this); host.removeConnection(this);
super.doDisconnect(exitCode); super.doDisconnect(exitCode);
FileUtils.deleteDirectory(workspace);
} }
@Override @Override

View File

@@ -44,7 +44,7 @@ public class StreamForwarder {
} }
public void start() { public void start() {
if (!isRunning && parent != null) if (!isRunning && parent == null)
{ {
isRunning = true; isRunning = true;
parent = new Thread(this); parent = new Thread(this);

View File

@@ -8,24 +8,28 @@ import com.flaremicro.crossjeeves.net.NetworkHandler;
public class Packet5Artifact extends Packet{ public class Packet5Artifact extends Packet{
private long fileId; private long fileId;
private long fileSize;
private String relativeFile; private String relativeFile;
public Packet5Artifact(){ public Packet5Artifact(){
} }
public Packet5Artifact(long fileId, String relativeFile){ public Packet5Artifact(long fileId, long fileSize, String relativeFile){
this.fileId = fileId; this.fileId = fileId;
this.fileSize = fileSize;
this.relativeFile = relativeFile; this.relativeFile = relativeFile;
} }
public void recievePacket(DataInputStream in) throws IOException { public void recievePacket(DataInputStream in) throws IOException {
fileId = in.readLong(); fileId = in.readLong();
fileSize = in.readLong();
relativeFile = in.readUTF(); relativeFile = in.readUTF();
} }
public void sendPacket(DataOutputStream out) throws IOException { public void sendPacket(DataOutputStream out) throws IOException {
super.sendPacket(out); super.sendPacket(out);
out.writeLong(fileId); out.writeLong(fileId);
out.writeLong(fileSize);
out.writeUTF(relativeFile); out.writeUTF(relativeFile);
} }
@@ -33,6 +37,12 @@ public class Packet5Artifact extends Packet{
networkHandler.handlePacket(this); networkHandler.handlePacket(this);
} }
public long getFileSize()
{
return fileSize;
}
public long getFileId() public long getFileId()
{ {
return fileId; return fileId;

View File

@@ -0,0 +1,55 @@
package com.flaremicro.util;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.regex.Pattern;
public class FileUtils {
public static void resolvePathsWildcard(File baseDir, String wildcard, List<String> matchedPaths)
{
throw new RuntimeException("Resolving with wildcards is not yet supported");
}
public static void resolvePathsRegex(File baseDir, String regex, List<String> matchedPaths) throws IOException {
Pattern pattern = Pattern.compile(regex);
resolvePathsRecursiveRegex(baseDir, baseDir, pattern, matchedPaths);
}
private static void resolvePathsRecursiveRegex(File baseDir, File currDir, Pattern pattern, List<String> matchedPaths) throws IOException {
String baseDirPath = baseDir.getCanonicalPath();
for (File file : currDir.listFiles())
{
String filePath = file.getCanonicalPath();
//Likely a link, ignore
if (!filePath.startsWith(baseDirPath))
continue;
filePath = filePath.substring(baseDirPath.length());
if (file.isDirectory())
{
resolvePathsRecursiveRegex(baseDir, file, pattern, matchedPaths);
}
else
{
if (pattern.matcher(filePath).matches())
{
matchedPaths.add(filePath);
}
}
}
}
public static boolean deleteDirectory(File directory) {
File[] allContents = directory.listFiles();
if (allContents != null) {
for (File file : allContents) {
deleteDirectory(file);
}
}
directory.deleteOnExit();
return directory.delete();
}
}

View File

@@ -6,6 +6,7 @@ import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipException; import java.util.zip.ZipException;
import java.util.zip.ZipFile; import java.util.zip.ZipFile;
@@ -13,17 +14,68 @@ import java.util.zip.ZipOutputStream;
public class ZipUtils { public class ZipUtils {
public static void zipDirectory(File sourceDir, File zipFile) throws IOException { public static boolean zipList(File baseDir, List<String> filePaths, File zipFile) {
FileOutputStream fos = new FileOutputStream(zipFile); FileOutputStream fos = null;
ZipOutputStream zos = new ZipOutputStream(fos); ZipOutputStream zos = null;
FileInputStream fis = null;
try try
{ {
zipFilesRecursively(sourceDir, sourceDir, zos); fos = new FileOutputStream(zipFile);
zos = new ZipOutputStream(fos);
for (String filePath : filePaths)
{
File file = new File(baseDir, filePath);
if (file.isDirectory() || !file.exists())
continue;
fis = new FileInputStream(file);
String zipEntryName = filePath.replace("\\", "/");
ZipEntry zipEntry = new ZipEntry(zipEntryName);
zos.putNextEntry(zipEntry);
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1)
{
zos.write(buffer, 0, bytesRead);
}
zos.closeEntry();
fis.close();
fis = null;
}
return true;
}
catch (IOException ex)
{
ex.printStackTrace();
} }
finally finally
{ {
zos.close(); Util.cleanClose(fis);
Util.cleanClose(zos);
} }
return false;
}
public static boolean zipDirectory(File sourceDir, File zipFile) {
FileOutputStream fos = null;
ZipOutputStream zos = null;
try
{
fos = new FileOutputStream(zipFile);
zos = new ZipOutputStream(fos);
zipFilesRecursively(sourceDir, sourceDir, zos);
return true;
}
catch (IOException ex)
{
ex.printStackTrace();
}
finally
{
Util.cleanClose(zos);
}
return false;
} }
private static void zipFilesRecursively(File rootDir, File source, ZipOutputStream zos) throws IOException { private static void zipFilesRecursively(File rootDir, File source, ZipOutputStream zos) throws IOException {

View File

@@ -22,13 +22,15 @@ public class Packet5ArtifactTest extends PacketTestBase {
@Test @Test
public void testWrite() throws IOException public void testWrite() throws IOException
{ {
Packet5Artifact packet = new Packet5Artifact(1, "file.txt"); Packet5Artifact packet = new Packet5Artifact(1, 2, "file.txt");
assertEquals(packet.getFileId(), 1); assertEquals(packet.getFileId(), 1);
assertEquals(packet.getFileSize(), 2);
assertEquals("file.txt", packet.getRelativeFile()); assertEquals("file.txt", packet.getRelativeFile());
packet.sendPacket(output()); packet.sendPacket(output());
assertEquals(5, input().readByte()); assertEquals(5, input().readByte());
assertEquals(1, input().readLong()); assertEquals(1, input().readLong());
assertEquals(2, input().readLong());
assertEquals("file.txt", input().readUTF()); assertEquals("file.txt", input().readUTF());
} }
@@ -38,18 +40,20 @@ public class Packet5ArtifactTest extends PacketTestBase {
Packet5Artifact packet = new Packet5Artifact(); Packet5Artifact packet = new Packet5Artifact();
output().writeLong(1); output().writeLong(1);
output().writeLong(2);
output().writeUTF("file.txt"); output().writeUTF("file.txt");
packet.recievePacket(input()); packet.recievePacket(input());
assertEquals(1, packet.getFileId()); assertEquals(1, packet.getFileId());
assertEquals(2, packet.getFileSize());
assertEquals("file.txt", packet.getRelativeFile()); assertEquals("file.txt", packet.getRelativeFile());
} }
@Test @Test
public void testProcess() throws IOException public void testProcess() throws IOException
{ {
Packet5Artifact packet = new Packet5Artifact(1, "file.txt"); Packet5Artifact packet = new Packet5Artifact(1, 2, "file.txt");
packet.processPacket(handler); packet.processPacket(handler);
Mockito.verify(handler, Mockito.times(1)).handlePacket(packet); Mockito.verify(handler, Mockito.times(1)).handlePacket(packet);
} }