mirror of
				https://github.com/ohwgiles/laminar.git
				synced 2025-06-13 12:54:29 +00:00 
			
		
		
		
	resolves #34: watch for configuration changes
Reload the configuration if a change is detected without requiring a server restart
This commit is contained in:
		
							parent
							
								
									a5d8b985f1
								
							
						
					
					
						commit
						216ecee7c5
					
				| @ -25,7 +25,7 @@ int StringMap::convert(std::string e) { return atoi(e.c_str()); } | ||||
| 
 | ||||
| StringMap parseConfFile(const char* path) { | ||||
|     StringMap result; | ||||
|     std::fstream f(path); | ||||
|     std::ifstream f(path); | ||||
|     std::string line; | ||||
|     while(std::getline(f, line)) { | ||||
|         if(line[0] == '#') | ||||
|  | ||||
| @ -131,6 +131,9 @@ struct LaminarInterface { | ||||
| 
 | ||||
|     // Callback for laminar to reap child processes.
 | ||||
|     virtual void reapChildren() = 0; | ||||
| 
 | ||||
|     // Callback to handle a configuration modification notification
 | ||||
|     virtual void notifyConfigChanged() = 0; | ||||
| }; | ||||
| 
 | ||||
| #endif // LAMINAR_INTERFACE_H_
 | ||||
|  | ||||
| @ -364,6 +364,8 @@ void Laminar::run() { | ||||
|     const char* listen_http = getenv("LAMINAR_BIND_HTTP") ?: INTADDR_HTTP_DEFAULT; | ||||
| 
 | ||||
|     srv = new Server(*this, listen_rpc, listen_http); | ||||
|     srv->addWatchPath(fs::path(fs::path(homeDir)/"cfg"/"nodes").string().c_str()); | ||||
|     srv->addWatchPath(fs::path(fs::path(homeDir)/"cfg"/"jobs").string().c_str()); | ||||
|     srv->start(); | ||||
| } | ||||
| 
 | ||||
| @ -530,6 +532,13 @@ void Laminar::reapChildren() { | ||||
|     assignNewJobs(); | ||||
| } | ||||
| 
 | ||||
| void Laminar::notifyConfigChanged() | ||||
| { | ||||
|     loadConfiguration(); | ||||
|     // config change may allow stuck jobs to dequeue
 | ||||
|     assignNewJobs(); | ||||
| } | ||||
| 
 | ||||
| void Laminar::abortAll() { | ||||
|     for(std::shared_ptr<Run> run : activeJobs) { | ||||
|         run->abort(); | ||||
|  | ||||
| @ -57,8 +57,9 @@ public: | ||||
|     bool setParam(std::string job, uint buildNum, std::string param, std::string value) override; | ||||
|     bool getArtefact(std::string path, std::string& result) override; | ||||
|     std::string getCustomCss() override; | ||||
|     void reapChildren() override; | ||||
|     void abortAll() override; | ||||
|     void reapChildren() override; | ||||
|     void notifyConfigChanged() override; | ||||
| 
 | ||||
| private: | ||||
|     bool loadConfiguration(); | ||||
|  | ||||
| @ -32,6 +32,7 @@ | ||||
| #include <websocketpp/server.hpp> | ||||
| 
 | ||||
| #include <sys/eventfd.h> | ||||
| #include <sys/inotify.h> | ||||
| #include <sys/signal.h> | ||||
| #include <sys/signalfd.h> | ||||
| 
 | ||||
| @ -401,6 +402,7 @@ Server::Server(LaminarInterface& li, kj::StringPtr rpcBindAddress, | ||||
|     })); | ||||
| 
 | ||||
|     // handle SIGCHLD
 | ||||
|     { | ||||
|         sigset_t mask; | ||||
|         sigemptyset(&mask); | ||||
|         sigaddset(&mask, SIGCHLD); | ||||
| @ -413,6 +415,17 @@ Server::Server(LaminarInterface& li, kj::StringPtr rpcBindAddress, | ||||
|             KJ_ASSERT(siginfo->ssi_signo == SIGCHLD); | ||||
|             laminarInterface.reapChildren(); | ||||
|         }).attach(std::move(event)).attach(std::move(buffer)); | ||||
|     } | ||||
| 
 | ||||
|     // handle watched paths
 | ||||
|     { | ||||
|         inotify_fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); | ||||
|         auto event = ioContext.lowLevelProvider->wrapInputFd(inotify_fd, kj::LowLevelAsyncIoProvider::TAKE_OWNERSHIP); | ||||
|         auto buffer = kj::heapArrayBuilder<char>(PROC_IO_BUFSIZE); | ||||
|         pathWatch = handleFdRead(event, buffer.asPtr().begin(), [this](const char*, size_t){ | ||||
|             laminarInterface.notifyConfigChanged(); | ||||
|         }).attach(std::move(event)).attach(std::move(buffer)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| Server::~Server() { | ||||
| @ -455,6 +468,10 @@ void Server::addDescriptor(int fd, std::function<void(const char*,size_t)> cb) { | ||||
|     childTasks.add(handleFdRead(event, buffer.asPtr().begin(), cb).attach(std::move(event)).attach(std::move(buffer))); | ||||
| } | ||||
| 
 | ||||
| void Server::addWatchPath(const char* dpath) { | ||||
|     inotify_add_watch(inotify_fd, dpath, IN_ONLYDIR | IN_CLOSE_WRITE | IN_CREATE | IN_DELETE); | ||||
| } | ||||
| 
 | ||||
| kj::Promise<void> Server::acceptHttpClient(kj::Own<kj::ConnectionReceiver>&& listener) { | ||||
|     kj::ConnectionReceiver& cr = *listener.get(); | ||||
|     return cr.accept().then(kj::mvCapture(kj::mv(listener), | ||||
|  | ||||
| @ -43,6 +43,9 @@ public: | ||||
|     // invoked with the read data
 | ||||
|     void addDescriptor(int fd, std::function<void(const char*,size_t)> cb); | ||||
| 
 | ||||
|     // add a path to be watched for changes
 | ||||
|     void addWatchPath(const char* dpath); | ||||
| 
 | ||||
| private: | ||||
|     kj::Promise<void> acceptHttpClient(kj::Own<kj::ConnectionReceiver>&& listener); | ||||
|     kj::Promise<void> acceptRpcClient(kj::Own<kj::ConnectionReceiver>&& listener); | ||||
| @ -63,6 +66,8 @@ private: | ||||
|     kj::TaskSet childTasks; | ||||
|     kj::TaskSet httpConnections; | ||||
|     kj::Maybe<kj::Promise<void>> reapWatch; | ||||
|     int inotify_fd; | ||||
|     kj::Maybe<kj::Promise<void>> pathWatch; | ||||
| 
 | ||||
|     // TODO: restructure so this isn't necessary
 | ||||
|     friend class ServerTest; | ||||
|  | ||||
| @ -64,6 +64,7 @@ public: | ||||
|     MOCK_METHOD0(getCustomCss, std::string()); | ||||
|     MOCK_METHOD0(abortAll, void()); | ||||
|     MOCK_METHOD0(reapChildren, void()); | ||||
|     MOCK_METHOD0(notifyConfigChanged, void()); | ||||
| }; | ||||
| 
 | ||||
| class ServerTest : public ::testing::Test { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user