diff --git a/src/laminar.cpp b/src/laminar.cpp index 2e686e2..1fdba3a 100644 --- a/src/laminar.cpp +++ b/src/laminar.cpp @@ -314,9 +314,9 @@ void Laminar::sendStatus(LaminarClient* client) { int execTotal = 0; int execBusy = 0; for(const auto& it : nodes) { - const Node& node = it.second; - execTotal += node.numExecutors; - execBusy += node.busyExecutors; + const std::shared_ptr& node = it.second; + execTotal += node->numExecutors; + execBusy += node->busyExecutors; } j.set("executorsTotal", execTotal); j.set("executorsBusy", execBusy); @@ -375,7 +375,7 @@ bool Laminar::loadConfiguration() { if(const char* ndirs = getenv("LAMINAR_KEEP_RUNDIRS")) numKeepRunDirs = static_cast(atoi(ndirs)); - NodeMap nm; + std::set knownNodes; fs::path nodeCfg = fs::path(homeDir)/"cfg"/"nodes"; @@ -386,9 +386,11 @@ bool Laminar::loadConfiguration() { StringMap conf = parseConfFile(it->path().string().c_str()); - Node node; - node.name = it->path().stem().string(); - node.numExecutors = conf.get("EXECUTORS", 6); + std::string nodeName = it->path().stem().string(); + auto existingNode = nodes.find(nodeName); + std::shared_ptr node = existingNode == nodes.end() ? nodes.emplace(nodeName, new Node).first->second : existingNode->second; + node->name = nodeName; + node->numExecutors = conf.get("EXECUTORS", 6); std::string tags = conf.get("TAGS"); if(!tags.empty()) { @@ -397,22 +399,29 @@ bool Laminar::loadConfiguration() { std::string tag; while(std::getline(iss, tag, ',')) tagList.insert(tag); - node.tags = tagList; + node->tags = tagList; } - nm.emplace(node.name, std::move(node)); + knownNodes.insert(nodeName); } } - if(nm.empty()) { - // add a default node - Node node; - node.name = ""; - node.numExecutors = 6; - nm.emplace("", std::move(node)); + // remove any nodes whose config files disappeared. + // if there are no known nodes, take care not to remove and re-add the default node + for(auto it = nodes.begin(); it != nodes.end();) { + if((it->first == "" && knownNodes.size() == 0) || knownNodes.find(it->first) != knownNodes.end()) + it++; + else + it = nodes.erase(it); } - nodes = nm; + // add a default node + if(nodes.empty()) { + std::shared_ptr node(new Node); + node->name = ""; + node->numExecutors = 6; + nodes.emplace("", node); + } fs::path jobsDir = fs::path(homeDir)/"cfg"/"jobs"; if(fs::is_directory(jobsDir)) { @@ -571,9 +580,9 @@ void Laminar::assignNewJobs() { while(it != queuedJobs.end()) { bool assigned = false; for(auto& sn : nodes) { - Node& node = sn.second; + std::shared_ptr node = sn.second; std::shared_ptr run = *it; - if(nodeCanQueue(node, *run)) { + if(nodeCanQueue(*node.get(), *run)) { fs::path cfgDir = fs::path(homeDir)/"cfg"; boost::system::error_code err; @@ -621,8 +630,8 @@ void Laminar::assignNewJobs() { if(fs::exists(cfgDir/"before")) run->addScript((cfgDir/"before").string()); // per-node before-run script - if(fs::exists(cfgDir/"nodes"/node.name+".before")) - run->addScript((cfgDir/"nodes"/node.name+".before").string()); + if(fs::exists(cfgDir/"nodes"/node->name+".before")) + run->addScript((cfgDir/"nodes"/node->name+".before").string()); // job before-run script if(fs::exists(cfgDir/"jobs"/run->name+".before")) run->addScript((cfgDir/"jobs"/run->name+".before").string()); @@ -632,8 +641,8 @@ void Laminar::assignNewJobs() { if(fs::exists(cfgDir/"jobs"/run->name+".after")) run->addScript((cfgDir/"jobs"/run->name+".after").string()); // per-node after-run script - if(fs::exists(cfgDir/"nodes"/node.name+".after")) - run->addScript((cfgDir/"nodes"/node.name+".after").string()); + if(fs::exists(cfgDir/"nodes"/node->name+".after")) + run->addScript((cfgDir/"nodes"/node->name+".after").string()); // global after-run script if(fs::exists(cfgDir/"after")) run->addScript((cfgDir/"after").string()); @@ -641,14 +650,14 @@ void Laminar::assignNewJobs() { // add environment files if(fs::exists(cfgDir/"env")) run->addEnv((cfgDir/"env").string()); - if(fs::exists(cfgDir/"nodes"/node.name+".env")) - run->addEnv((cfgDir/"nodes"/node.name+".env").string()); + if(fs::exists(cfgDir/"nodes"/node->name+".env")) + run->addEnv((cfgDir/"nodes"/node->name+".env").string()); if(fs::exists(cfgDir/"jobs"/run->name+".env")) run->addEnv((cfgDir/"jobs"/run->name+".env").string()); // start the job - node.busyExecutors++; - run->node = &node; + node->busyExecutors++; + run->node = node; run->startedAt = time(nullptr); run->laminarHome = homeDir; run->build = buildNum; @@ -661,7 +670,7 @@ void Laminar::assignNewJobs() { // update next build number buildNums[run->name] = buildNum; - LLOG(INFO, "Queued job to node", run->name, run->build, node.name); + LLOG(INFO, "Queued job to node", run->name, run->build, node->name); // notify clients Json j; @@ -713,7 +722,7 @@ void Laminar::assignNewJobs() { } void Laminar::runFinished(Run * r) { - Node* node = r->node; + std::shared_ptr node = r->node; node->busyExecutors--; LLOG(INFO, "Run completed", r->name, to_string(r->result)); diff --git a/src/laminar.h b/src/laminar.h index e8735d3..d452ad6 100644 --- a/src/laminar.h +++ b/src/laminar.h @@ -27,7 +27,7 @@ #include // Node name to node object map -typedef std::unordered_map NodeMap; +typedef std::unordered_map> NodeMap; struct Server; class Json; diff --git a/src/run.h b/src/run.h index d8c68bb..03f6d1b 100644 --- a/src/run.h +++ b/src/run.h @@ -25,6 +25,7 @@ #include #include #include +#include enum class RunState { UNKNOWN, @@ -75,7 +76,7 @@ public: std::string reason() const; std::function notifyCompletion; - Node* node; + std::shared_ptr node; RunState result; RunState lastResult; std::string laminarHome; diff --git a/test/test-run.cpp b/test/test-run.cpp index 456197b..856ac3a 100644 --- a/test/test-run.cpp +++ b/test/test-run.cpp @@ -25,7 +25,7 @@ class RunTest : public ::testing::Test { protected: void SetUp() override { - run.node = &node; + run.node = node; } void wait() { int state = -1; @@ -55,7 +55,7 @@ protected: } class Run run; - Node node; + std::shared_ptr node = std::shared_ptr(new Node); }; TEST_F(RunTest, WorkingDirectory) {