1 module beangle.task.agent; 2 3 import vibe.core.net; 4 import vibe.core.core; 5 import vibe.core.log; 6 import vibe.http.client; 7 import vibe.http.websockets : WebSocket, connectWebSocket; 8 import std.stdio; 9 import std.file; 10 import std.path; 11 import std.string; 12 import std.algorithm; 13 import std.array; 14 import core.time; 15 import beangle.task.config; 16 import beangle.util; 17 18 int startAgent(string xml, string[] args) { 19 auto agent = Agent.parse(xml); 20 if (agent.serverURL !is null){ 21 runTask({ 22 try{ 23 connectToWS(agent); 24 } catch (Exception e) { 25 } 26 }); 27 } 28 if (agent.tasks.length>0){ 29 runTask({ 30 try{ 31 runLocalTask(agent); 32 } catch (Exception e) { 33 assert(false, e.msg); 34 } 35 }); 36 } 37 scope (exit) exitEventLoop(true); 38 return runApplication(&args); 39 } 40 41 void runLocalTask(Agent agent) @system { 42 while(true){ 43 sleep(1.seconds); 44 auto current = now(); 45 auto interval = 1.seconds; 46 beangle.task.config.Task[] finished; 47 foreach (name, task; agent.tasks){ 48 auto next = task.nextExecuteAt(); 49 if (next < current){ 50 logInfo("finished task: %s", task.name); 51 finished ~= task; 52 }else if ((next-current) <= interval){ 53 logInfo("execute task: %s", task.name); 54 task.execute(); 55 } 56 } 57 agent.removeTasks(finished); 58 } 59 } 60 61 void connectToWS (Agent agent) { 62 if (agent.serverURL is null){ 63 logInfo("Ignore remote server connection"); 64 }else { 65 while(true){ 66 try{ 67 logInfo("connect to " ~ agent.serverURL); 68 auto ws = connectWebSocket(URL(agent.serverURL)); 69 serveSocket(agent, ws); 70 } catch (Throwable e) { 71 logInfo(e.msg); 72 sleep(10.seconds); 73 } 74 } 75 } 76 } 77 78 void serveSocket(Agent agent, WebSocket ws){ 79 ws.send("/login " ~ agent.name ~ " " ~ agent.secret); 80 while (ws.waitForData()) { 81 auto txt = ws.receiveText().strip(); 82 switch (txt){ 83 case "tasks" : 84 auto names = agent.tasks.keys.join(","); 85 ws.send(names); 86 break; 87 case "clean" : 88 agent.tasks.clear(); 89 ws.send("all tasks were removed."); 90 break; 91 default: 92 if (txt.startsWith("<task")){ 93 auto task = parseTask(txt); 94 if (task.repeatable){ 95 agent.addTask(task); 96 ws.send("New task " ~ task.name ~ " was mounted."); 97 logInfo("new task %s was mounted.", task.name); 98 }else { 99 auto res = task.execute(); 100 string[] outputs; 101 auto f = File(task.stdout, "r"); 102 foreach (string l; lines(f)){ 103 outputs ~= l; 104 } 105 ws.send(outputs.join()); 106 if (res ==0 ){ 107 std.file.remove(task.stdout); 108 } 109 } 110 }else if (txt.startsWith("{") && txt.endsWith("}")){ 111 import std.json; 112 JSONValue js = parseJSON(txt); 113 114 auto id = js["id"].str; 115 auto commands = new Commands(js["commands"].str); 116 string[string] envs; 117 auto result = commands.execute(envs, expandTilde("~")); 118 JSONValue jj = ["id":id]; 119 jj.object["data"]=result[1]; 120 ws.send(jj.toString()); 121 }else { 122 logInfo("%s", txt); 123 } 124 } 125 } 126 logFatal("Connection lost!"); 127 }