Squashed commit of the following: commit 29571182b1ec3b5be2cec3212c2bea1121a3dac2 Author: Ian Roddis <tech@kinesin.ca> Date: Thu Feb 24 11:29:47 2022 -0400 Adding more elegant handling of tasks with no attempts commit 18c8ccb0863abbf6c9cc0efe5cc68df03a9eb80d Author: Ian Roddis <tech@kinesin.ca> Date: Thu Feb 24 11:18:59 2022 -0400 Better handling of no attempts at all commit 962f9f6e5e17f71bc3766553913774631f66e7ef Author: Ian Roddis <tech@kinesin.ca> Date: Thu Feb 24 11:10:28 2022 -0400 Adding fix for missing attempts commit 19b8203e952b3d21f4ff3f9b97a01c4d567ff1e7 Author: Ian Roddis <tech@kinesin.ca> Date: Wed Feb 23 16:56:37 2022 -0400 Adding webui instructions to readme commit 81383c80f01101828c0c49868916a2712d140f42 Author: Ian Roddis <tech@kinesin.ca> Date: Wed Feb 23 16:48:31 2022 -0400 Adding in route splatting to support static assets commit c9b39b307916c0fb1e88769d6986ddf7c3ba183a Author: Ian Roddis <tech@kinesin.ca> Date: Wed Feb 23 12:11:11 2022 -0400 Cleanup commit 177819a1439cd1a0f32c652abf670f54457e105a Author: Ian Roddis <tech@kinesin.ca> Date: Wed Feb 23 12:09:40 2022 -0400 Setting explicit url for extra CSS commit 78261129511c50657e7902934cee396eb1e4e3a8 Author: Ian Roddis <tech@kinesin.ca> Date: Wed Feb 23 12:08:27 2022 -0400 Moving webui commit 9f8db6e2c2c8a231060217cb82f1b13aabe4eae2 Author: Ian Roddis <tech@kinesin.ca> Date: Wed Feb 23 12:06:25 2022 -0400 Reorganizing elements, adding regex for run list commit f114250c9a506b2c0e9d642cc75749e99cc76cef Author: Ian Roddis <tech@kinesin.ca> Date: Wed Feb 23 10:52:41 2022 -0400 Adding regex filtering to tasks commit 2de2f218416210443119aa88fa49c714197f4b16 Author: Ian Roddis <tech@kinesin.ca> Date: Wed Feb 23 10:42:22 2022 -0400 Adding in task details and getting the plumbing working commit 660a2078e22799ba51b4b8bbe5c12cd0f9315b0a Author: Ian Roddis <tech@kinesin.ca> Date: Wed Feb 23 09:38:13 2022 -0400 Fixing remaining settings commit 1aa0dfe1c971a12dfed183586ee5a3206d452409 Author: Ian Roddis <tech@kinesin.ca> Date: Wed Feb 23 09:36:25 2022 -0400 Playing with settings commit 84cbd11c45651c7c6c96c16714e741b6aee10bc5 Author: Ian Roddis <tech@kinesin.ca> Date: Wed Feb 23 08:52:52 2022 -0400 Removing extra code commit 6e31646b7c62368cab22b3844a70943e0149ddc7 Author: Ian Roddis <tech@kinesin.ca> Date: Tue Feb 22 17:29:47 2022 -0400 Adding linter, renaming components to meet standards, fixing some mixups in settings commit 225442ee5732d007867e485ccea05293e3e5e1b7 Author: Ian Roddis <tech@kinesin.ca> Date: Tue Feb 22 17:25:27 2022 -0400 Fixing sorters commit eb0d7a4c4c30d8e8b43b574ed0c2f97515bb9353 Author: Ian Roddis <tech@kinesin.ca> Date: Tue Feb 22 16:46:41 2022 -0400 Controls are coming together commit b1789d1cc3c0bae170e0ca1a47cccfd344197244 Author: Ian Roddis <tech@kinesin.ca> Date: Tue Feb 22 11:08:09 2022 -0400 More refactoring commit 6d0afce429aad00864482a2cc7dd731a53312e14 Author: Ian Roddis <tech@kinesin.ca> Date: Sun Feb 20 22:29:43 2022 -0400 figuring out layout commit 6af498f3aa7fe2f45121df2278cdfac297165c5c Author: Ian Roddis <tech@kinesin.ca> Date: Sun Feb 20 12:30:49 2022 -0400 Migrating to prop drilling / emiting commit dffe7059ce01209d2def6ef7c03bc750e31fe741 Author: Ian Roddis <tech@kinesin.ca> Date: Fri Feb 18 17:20:46 2022 -0400 Checkpointing work for now commit d6428ad59c9c05ab7fba82ce3c0441ac3f568796 Author: Ian Roddis <tech@kinesin.ca> Date: Fri Feb 18 17:05:37 2022 -0400 Adding in toggling for states commit b9a4f2dc02f327d3529821e217d3b6a00a84f202 Author: Ian Roddis <tech@kinesin.ca> Date: Fri Feb 18 16:43:01 2022 -0400 Reorganizing everything commit d33691d022597d1ff8f588450e147c72555be9f4 Author: Ian Roddis <tech@kinesin.ca> Date: Wed Feb 16 17:04:54 2022 -0400 Removing console logging commit 4537376ccad6fc0c52f0a7cfd2b2bf23f708196c Author: Ian Roddis <tech@kinesin.ca> Date: Wed Feb 16 17:04:27 2022 -0400 Refresh timer working now commit 213a3da4fd07c82cd18cd8c3b2422ddc78bd6fb4 Author: Ian Roddis <tech@kinesin.ca> Date: Wed Feb 16 16:40:45 2022 -0400 Adding timer commit ff495ac69563689ff4fc07119936079e57608ea7 Author: Ian Roddis <tech@kinesin.ca> Date: Wed Feb 16 16:02:53 2022 -0400 Refactoring some code, adding in endpoint to kill a running task commit 97ff28b9b1910e03e0f2725a3f54d2a07e53714c Author: Ian Roddis <tech@kinesin.ca> Date: Wed Feb 16 14:56:15 2022 -0400 Renaming UI commit affab06ad657833b73588eac919250935b353f31 Author: Ian Roddis <tech@kinesin.ca> Date: Wed Feb 16 13:29:31 2022 -0400 moving to bootstrap commit c40a2e58a86362863c905470f4417753aaf0dac2 Author: Ian Roddis <tech@kinesin.ca> Date: Wed Feb 16 12:33:08 2022 -0400 adding task button commit 420463b8d7f964baa0dfc7c87c2e9024bc8284cc Author: Ian Roddis <tech@kinesin.ca> Date: Wed Feb 16 10:51:11 2022 -0400 checkpoint commit a7aa3db731255e7e13bc58d901b8eb1e30ede39c Author: Ian Roddis <tech@kinesin.ca> Date: Wed Feb 16 09:33:01 2022 -0400 Fixing up state commit 361b4cbcd8f1268eb9b494084d6862a6ab8f3a27 Author: Ian Roddis <tech@kinesin.ca> Date: Wed Feb 16 09:29:14 2022 -0400 Fixing event callbacks commit 388cada692dc8d7e0eff611467d4c77ce897a54c Author: Ian Roddis <tech@kinesin.ca> Date: Wed Feb 16 09:24:39 2022 -0400 Adding global state, task view and buttons commit cb5a3acef0bd982621678fbd44a133db56420871 Author: Ian Roddis <tech@kinesin.ca> Date: Wed Feb 16 07:49:30 2022 -0400 Adding RunView commit 4c78ef1250709e7c8f5ef3433640fd8d1d319a8d Author: Ian Roddis <tech@kinesin.ca> Date: Tue Feb 15 17:20:23 2022 -0400 checkpoint commit 2c5b610101e9c18ef1ad8f962d7309b63c80743c Author: Ian Roddis <tech@kinesin.ca> Date: Tue Feb 15 17:10:06 2022 -0400 Adding explicit payload headers, adding vue and react apps commit 95ac6c05903bc83c6934db58b48649eee2038c3d Author: Ian Roddis <tech@kinesin.ca> Date: Tue Feb 15 12:56:57 2022 -0400 Adding CORS support, rough-in of webui
274 lines
8.2 KiB
C++
274 lines
8.2 KiB
C++
#include <curl/curl.h>
|
|
#include <pistache/client.h>
|
|
#include <rapidjson/document.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <catch2/catch.hpp>
|
|
#include <daggy/Serialization.hpp>
|
|
#include <daggy/executors/task/ForkingTaskExecutor.hpp>
|
|
#include <daggy/executors/task/NoopTaskExecutor.hpp>
|
|
#include <daggy/loggers/dag_run/OStreamLogger.hpp>
|
|
#include <daggyd/Server.hpp>
|
|
#include <filesystem>
|
|
#include <iostream>
|
|
#include <thread>
|
|
|
|
namespace rj = rapidjson;
|
|
|
|
using namespace daggy;
|
|
|
|
TEST_CASE("rest_endpoint", "[server_basic]")
|
|
{
|
|
std::stringstream ss;
|
|
daggy::executors::task::ForkingTaskExecutor executor(10);
|
|
daggy::loggers::dag_run::OStreamLogger logger(ss);
|
|
Pistache::Address listenSpec("localhost", Pistache::Port(0));
|
|
|
|
const size_t nDAGRunners = 10, nWebThreads = 10;
|
|
|
|
daggy::daggyd::Server server(listenSpec, logger, executor, nDAGRunners,
|
|
"/dev/null");
|
|
server.init(nWebThreads);
|
|
server.start();
|
|
|
|
const std::string host = "localhost:";
|
|
const std::string baseURL = host + std::to_string(server.getPort());
|
|
|
|
SECTION("Ready Endpoint")
|
|
{
|
|
auto response = HTTP_REQUEST(baseURL + "/ready");
|
|
REQUIRE(response.code == HTTPCode::Ok);
|
|
}
|
|
|
|
SECTION("Querying a non-existent dagrunid should fail ")
|
|
{
|
|
auto response = HTTP_REQUEST(baseURL + "/v1/dagrun/100");
|
|
REQUIRE(response.code != HTTPCode::Ok);
|
|
}
|
|
|
|
SECTION("Simple DAGRun Submission")
|
|
{
|
|
std::string dagRun = R"({
|
|
"tag": "unit_server",
|
|
"parameters": { "FILE": [ "A", "B" ] },
|
|
"tasks": {
|
|
"touch": { "job": { "command": [ "/bin/touch", "dagrun_{{FILE}}" ], "environment": []} },
|
|
"cat": { "job": { "command": [ "/bin/cat", "dagrun_A", "dagrun_B" ], "environment": []},
|
|
"parents": [ "touch" ]
|
|
}
|
|
}
|
|
})";
|
|
|
|
auto dagSpec = daggy::dagFromJSON(dagRun);
|
|
|
|
// Submit, and get the runID
|
|
daggy::DAGRunID runID = 0;
|
|
{
|
|
auto response = HTTP_REQUEST(baseURL + "/v1/dagrun/", dagRun, "POST");
|
|
REQUIRE(response.code == HTTPCode::Ok);
|
|
|
|
rj::Document doc;
|
|
daggy::checkRJParse(doc.Parse(response.body.c_str()));
|
|
REQUIRE(doc.IsObject());
|
|
REQUIRE(doc.HasMember("runID"));
|
|
|
|
runID = doc["runID"].GetUint64();
|
|
}
|
|
|
|
// Ensure our runID shows up in the list of running DAGs
|
|
{
|
|
auto response = HTTP_REQUEST(baseURL + "/v1/dagruns?all=1");
|
|
REQUIRE(response.code == HTTPCode::Ok);
|
|
|
|
rj::Document doc;
|
|
daggy::checkRJParse(doc.Parse(response.body.c_str()));
|
|
REQUIRE(doc.IsArray());
|
|
REQUIRE(doc.Size() >= 1);
|
|
|
|
// Ensure that our DAG is in the list and matches our given DAGRunID
|
|
bool found = false;
|
|
const auto &runs = doc.GetArray();
|
|
for (size_t i = 0; i < runs.Size(); ++i) {
|
|
const auto &run = runs[i];
|
|
REQUIRE(run.IsObject());
|
|
REQUIRE(run.HasMember("tag"));
|
|
REQUIRE(run.HasMember("runID"));
|
|
|
|
std::string runName = run["tag"].GetString();
|
|
if (runName == "unit_server") {
|
|
REQUIRE(run["runID"].GetUint64() == runID);
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
REQUIRE(found);
|
|
}
|
|
|
|
// Ensure we can get one of our tasks
|
|
{
|
|
auto response = HTTP_REQUEST(baseURL + "/v1/dagrun/" +
|
|
std::to_string(runID) + "/task/cat_0");
|
|
REQUIRE(response.code == HTTPCode::Ok);
|
|
|
|
rj::Document doc;
|
|
daggy::checkRJParse(doc.Parse(response.body.c_str()));
|
|
|
|
REQUIRE_NOTHROW(daggy::taskFromJSON("cat", doc));
|
|
auto task = daggy::taskFromJSON("cat", doc);
|
|
|
|
REQUIRE(task == dagSpec.tasks.at("cat"));
|
|
}
|
|
|
|
// Wait until our DAG is complete
|
|
bool complete = true;
|
|
for (auto i = 0; i < 10; ++i) {
|
|
auto response =
|
|
HTTP_REQUEST(baseURL + "/v1/dagrun/" + std::to_string(runID));
|
|
REQUIRE(response.code == HTTPCode::Ok);
|
|
rj::Document doc;
|
|
daggy::checkRJParse(doc.Parse(response.body.c_str()));
|
|
REQUIRE(doc.IsObject());
|
|
|
|
REQUIRE(doc.HasMember("taskStates"));
|
|
const auto &taskStates = doc["taskStates"].GetObject();
|
|
|
|
size_t nStates = 0;
|
|
for (auto it = taskStates.MemberBegin(); it != taskStates.MemberEnd();
|
|
++it) {
|
|
nStates++;
|
|
}
|
|
REQUIRE(nStates == 3);
|
|
|
|
complete = true;
|
|
for (auto it = taskStates.MemberBegin(); it != taskStates.MemberEnd();
|
|
++it) {
|
|
std::string state = it->value.GetString();
|
|
if (state != "COMPLETED") {
|
|
complete = false;
|
|
break;
|
|
}
|
|
}
|
|
if (complete)
|
|
break;
|
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
|
}
|
|
REQUIRE(complete);
|
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(2));
|
|
for (const auto &pth : std::vector<fs::path>{"dagrun_A", "dagrun_B"}) {
|
|
REQUIRE(fs::exists(pth));
|
|
fs::remove(pth);
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_CASE("Server cancels and resumes execution", "[server_resume]")
|
|
{
|
|
std::stringstream ss;
|
|
daggy::executors::task::ForkingTaskExecutor executor(10);
|
|
daggy::loggers::dag_run::OStreamLogger logger(std::cout);
|
|
Pistache::Address listenSpec("localhost", Pistache::Port(0));
|
|
|
|
const size_t nDAGRunners = 10, nWebThreads = 10;
|
|
|
|
daggy::daggyd::Server server(listenSpec, logger, executor, nDAGRunners,
|
|
"/dev/null");
|
|
server.init(nWebThreads);
|
|
server.start();
|
|
|
|
const std::string host = "localhost:";
|
|
const std::string baseURL = host + std::to_string(server.getPort());
|
|
|
|
SECTION("Cancel / Resume DAGRun")
|
|
{
|
|
std::string dagRunJSON = R"({
|
|
"tag": "unit_server",
|
|
"tasks": {
|
|
"touch_A": { "job": { "command": [ "/bin/touch", "resume_touch_a" ]}, "children": ["touch_C"] },
|
|
"sleep_B": { "job": { "command": [ "/bin/sleep", "3" ]}, "children": ["touch_C"] },
|
|
"touch_C": { "job": { "command": [ "/bin/touch", "resume_touch_c" ]} }
|
|
}
|
|
})";
|
|
|
|
auto dagSpec = daggy::dagFromJSON(dagRunJSON);
|
|
|
|
// Submit, and get the runID
|
|
daggy::DAGRunID runID;
|
|
{
|
|
auto response = HTTP_REQUEST(baseURL + "/v1/dagrun/", dagRunJSON, "POST");
|
|
REQUIRE(response.code == HTTPCode::Ok);
|
|
|
|
rj::Document doc;
|
|
daggy::checkRJParse(doc.Parse(response.body.c_str()));
|
|
REQUIRE(doc.IsObject());
|
|
REQUIRE(doc.HasMember("runID"));
|
|
|
|
runID = doc["runID"].GetUint64();
|
|
}
|
|
|
|
std::this_thread::sleep_for(1s);
|
|
|
|
// Stop the current run
|
|
{
|
|
auto response = HTTP_REQUEST(
|
|
baseURL + "/v1/dagrun/" + std::to_string(runID) + "/state/KILLED", "",
|
|
"PATCH");
|
|
REQUIRE(response.code == HTTPCode::Ok);
|
|
REQUIRE(logger.getDAGRunState(runID) == +daggy::RunState::KILLED);
|
|
}
|
|
|
|
// Verify that the run still exists
|
|
{
|
|
auto dagRun = logger.getDAGRun(runID);
|
|
REQUIRE(dagRun.taskRunStates.at("touch_A_0") ==
|
|
+daggy::RunState::COMPLETED);
|
|
REQUIRE(fs::exists("resume_touch_a"));
|
|
|
|
REQUIRE(dagRun.taskRunStates.at("sleep_B_0") ==
|
|
+daggy::RunState::ERRORED);
|
|
REQUIRE(dagRun.taskRunStates.at("touch_C_0") == +daggy::RunState::QUEUED);
|
|
}
|
|
|
|
// Set the errored task state
|
|
{
|
|
auto url = baseURL + "/v1/dagrun/" + std::to_string(runID) +
|
|
"/task/sleep_B_0/state/QUEUED";
|
|
auto response = HTTP_REQUEST(url, "", "PATCH");
|
|
REQUIRE(response.code == HTTPCode::Ok);
|
|
REQUIRE(logger.getTaskState(runID, "sleep_B_0") ==
|
|
+daggy::RunState::QUEUED);
|
|
}
|
|
|
|
// Resume
|
|
{
|
|
struct stat s;
|
|
|
|
lstat("resume_touch_A", &s);
|
|
auto preMTime = s.st_mtim.tv_sec;
|
|
|
|
auto response = HTTP_REQUEST(
|
|
baseURL + "/v1/dagrun/" + std::to_string(runID) + "/state/QUEUED", "",
|
|
"PATCH");
|
|
|
|
// Wait for run to complete
|
|
std::this_thread::sleep_for(3s);
|
|
REQUIRE(logger.getDAGRunState(runID) == +daggy::RunState::COMPLETED);
|
|
|
|
REQUIRE(fs::exists("resume_touch_c"));
|
|
REQUIRE(fs::exists("resume_touch_a"));
|
|
|
|
for (const auto &[taskName, task] : dagSpec.tasks) {
|
|
REQUIRE(logger.getTaskState(runID, taskName + "_0") ==
|
|
+daggy::RunState::COMPLETED);
|
|
}
|
|
|
|
// Ensure "touch_A" wasn't run again
|
|
lstat("resume_touch_A", &s);
|
|
auto postMTime = s.st_mtim.tv_sec;
|
|
REQUIRE(preMTime == postMTime);
|
|
}
|
|
}
|
|
|
|
server.shutdown();
|
|
}
|