Skip to content

Commit

Permalink
Add a daemon handler class
Browse files Browse the repository at this point in the history
This daemon handler class will manage the daemons we configure (start,
stop, reload etc) using dbus (or any other message bus, such as the
test one, to allow easy mocking)

We also add a test message bus to test this daemon handler
  • Loading branch information
arthurmco committed Jul 31, 2024
1 parent 7691ae5 commit d265278
Show file tree
Hide file tree
Showing 4 changed files with 246 additions and 0 deletions.
42 changes: 42 additions & 0 deletions include/cloysterhpc/daemon_handler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#pragma once

#include <memory>
#include <string>

#include <cloysterhpc/messagebus.h>

class DaemonHandler {
private:
std::shared_ptr<MessageBus> m_bus;
const std::string m_name;

template <typename... Ts>
sdbus::ObjectPath callObjectFunction(
const std::string function, Ts... params)
{
MessageReply reply
= m_bus->method("org.freedesktop.systemd1.Manager", function)
->call(m_name, params...);
return reply.get<sdbus::ObjectPath>();
}

static std::string fixServiceName(std::string name);

public:
DaemonHandler(std::shared_ptr<MessageBus> bus, const std::string name)
: m_bus(bus)
, m_name(DaemonHandler::fixServiceName(name))
{
}

static void daemonReload(MessageBus& bus);

bool exists();

void load();
void start();
void stop();
void restart();
void enable();
void disable();
};
54 changes: 54 additions & 0 deletions include/testing/test_message_bus.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#pragma once

#include <cloysterhpc/messagebus.h>
#include <any>
#include <tuple>
#include <vector>
#include <string>

class TestMessageMethod;

using FunctionStore = std::tuple<std::string, std::string>;
using FunctionParameters = std::vector<std::any>;

class TestMessageBus : public MessageBus {
friend class TestMessageMethod;

private:
std::unordered_map<std::string, std::vector<FunctionParameters>> m_functions;

std::string makeStoreName(FunctionStore s) const {
return std::get<0>(s) + "->" + std::get<1>(s);
}

void registerCall(FunctionStore s, FunctionParameters p) {
auto count = this->callCount(s);
m_functions[makeStoreName(s)].push_back(p);
}

public:
std::unique_ptr<MessageBusMethod> method(std::string interface,
std::string method);

void dump() const;

unsigned callCount(FunctionStore s) const;
FunctionParameters calledWith(FunctionStore s, unsigned index) const;
};

class TestMessageMethod : public MessageBusMethod {
private:
TestMessageBus *m_bus;
FunctionStore m_store;
FunctionParameters m_params = {};

protected:
virtual void pushSingleParam(MethodParamVariant param) {
std::visit([this](auto &&arg) { this->m_params.push_back(arg); }, param);
}
virtual MessageReply callMethod();

public:
TestMessageMethod(TestMessageBus *bus, FunctionStore s)
: m_bus(bus), m_store(s) {}
};
96 changes: 96 additions & 0 deletions src/daemon_handler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#include <cloysterhpc/daemon_handler.h>

std::string DaemonHandler::fixServiceName(std::string name)
{
if (!name.ends_with(".service")) {
return name + ".service";
} else {
return name;
}
}

bool DaemonHandler::exists()
{
auto path = this->callObjectFunction("GetUnit");
return path.size() > 0;
}

void DaemonHandler::start()
{
this->callObjectFunction("StartUnit", "replace");
}

void DaemonHandler::stop() { this->callObjectFunction("StopUnit", "replace"); }

void DaemonHandler::load() { this->callObjectFunction("LoadUnit"); }

void DaemonHandler::restart()
{
this->callObjectFunction("RestartUnit", "replace");
}

void DaemonHandler::daemonReload(MessageBus& bus)
{
bus.method("org.freedesktop.systemd1.Manager", "Reload")->call();
}

#ifdef BUILD_TESTING
#include <doctest/doctest.h>
#else
#define DOCTEST_CONFIG_DISABLE
#include <doctest/doctest.h>
#endif

#include <testing/test_message_bus.h>

TEST_SUITE("Test service handler connectivity")
{
TEST_CASE("Service handler testing")
{
auto testBus = std::make_shared<TestMessageBus>();

DaemonHandler sr { testBus, "generic-service" };

SUBCASE("it can start")
{
auto function = std::make_tuple<std::string, std::string>(
"org.freedesktop.systemd1.Manager", "StartUnit");

REQUIRE(testBus->callCount(function) == 0);
sr.start();

REQUIRE(testBus->callCount(function) == 1);
auto params = testBus->calledWith(function, 0);
CHECK(std::any_cast<std::string>(params[0])
== "generic-service.service");
}

SUBCASE("it can stop")
{
auto function = std::make_tuple<std::string, std::string>(
"org.freedesktop.systemd1.Manager", "StopUnit");

REQUIRE(testBus->callCount(function) == 0);
sr.stop();

REQUIRE(testBus->callCount(function) == 1);
auto params = testBus->calledWith(function, 0);
CHECK(std::any_cast<std::string>(params[0])
== "generic-service.service");
}

SUBCASE("it can restart")
{
auto function = std::make_tuple<std::string, std::string>(
"org.freedesktop.systemd1.Manager", "RestartUnit");

REQUIRE(testBus->callCount(function) == 0);
sr.restart();

REQUIRE(testBus->callCount(function) == 1);
auto params = testBus->calledWith(function, 0);
CHECK(std::any_cast<std::string>(params[0])
== "generic-service.service");
}
}
}
54 changes: 54 additions & 0 deletions test/test_message_bus.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#include <cstdio>
#include <testing/test_message_bus.h>

unsigned TestMessageBus::callCount(FunctionStore s) const
{
auto sname = makeStoreName(s);
if (!m_functions.contains(sname)) {
return 0;
} else {
return m_functions.at(sname).size();
}
}

FunctionParameters TestMessageBus::calledWith(
FunctionStore s, unsigned index) const
{
if (this->callCount(s) == 0 || index >= this->callCount(s)) {
return {};
}

auto sname = makeStoreName(s);
return m_functions.at(sname).at(index);
}

void TestMessageBus::dump() const
{
fprintf(stderr, "???\n");
for (const auto& [k, paramslist] : m_functions) {
fprintf(stderr, "\t%s: %ld calls\n", k.c_str(), paramslist.size());
int i = 0;
for (const auto& params : paramslist) {
fprintf(stderr, "\t\t%d: ", i++);
for (const auto& p : params) {
fprintf(stderr, "<%s> ", p.type().name());
}
fprintf(stderr, "\n");
}
}
}

std::unique_ptr<MessageBusMethod> TestMessageBus::method(
std::string interface, std::string method)
{
auto store = std::make_tuple(interface, method);
return std::unique_ptr<MessageBusMethod>(
new TestMessageMethod { this, store });
}

MessageReply TestMessageMethod::callMethod()
{
m_bus->registerCall(m_store, m_params);
return MessageReply(
std::make_any<sdbus::ObjectPath>(sdbus::ObjectPath("123")));
}

0 comments on commit d265278

Please sign in to comment.