Skip to content

Commit

Permalink
fix: better docker runner TOML serialisation
Browse files Browse the repository at this point in the history
Added proper error messages when failing to parse
  • Loading branch information
ABeltramo committed Jun 26, 2023
1 parent 217703e commit 0ba8aeb
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 26 deletions.
68 changes: 42 additions & 26 deletions src/wolf/runners/docker.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,34 +21,50 @@ class RunDocker : public state::Runner {
public:
static RunDocker from_toml(std::shared_ptr<dp::event_bus> ev_bus, const toml::value &runner_obj) {
std::vector<std::string> rec_mounts = toml::find_or<std::vector<std::string>>(runner_obj, "mounts", {});
std::vector<MountPoint> mounts = rec_mounts //
| transform([](const std::string &mount) { //
auto splits = utils::split(mount, ':'); //
return MountPoint{.source = to_string(splits[0]),
.destination = to_string(splits[1]),
.mode = to_string(splits[2])}; //
}) //
| ranges::to_vector; //
std::vector<MountPoint> mounts =
rec_mounts //
| transform([](const std::string &mount) { //
auto splits = utils::split(mount, ':'); //
if (splits.size() < 2) {
throw std::runtime_error(fmt::format("[TOML] Docker, invalid mount point definition: {}", mount));
}
return MountPoint{.source = to_string(splits.at(0)),
.destination = to_string(splits.at(1)),
.mode = to_string(splits.size() > 2 ? splits.at(2) : "rw")}; //
}) //
| ranges::to_vector; //

std::vector<std::string> rec_ports = toml::find_or<std::vector<std::string>>(runner_obj, "ports", {});
std::vector<Port> ports = rec_ports //
| transform([](const std::string &port) { //
auto splits = utils::split(port, ':');
return Port{.private_port = std::stoi(to_string(splits[0])),
.public_port = std::stoi(to_string(splits[1])),
.type = splits[2] == "tcp" ? TCP : UDP};
}) //
| ranges::to_vector;
std::vector<Port> ports =
rec_ports //
| transform([](const std::string &port) { //
auto splits = utils::split(port, ':');
if (splits.size() < 2) {
throw std::runtime_error(fmt::format("[TOML] Docker, invalid port definition: {}", port));
}
PortType port_type = TCP;
if (splits.size() > 2 && to_string(splits.at(2)) == "udp") {
port_type = UDP;
}
return Port{.private_port = std::stoi(to_string(splits.at(0))),
.public_port = std::stoi(to_string(splits.at(1))),
.type = port_type};
}) //
| ranges::to_vector;

std::vector<std::string> rec_devices = toml::find_or<std::vector<std::string>>(runner_obj, "devices", {});
std::vector<Device> devices = rec_devices //
| transform([](const std::string &mount) { //
auto splits = utils::split(mount, ':'); //
return Device{.path_on_host = to_string(splits[0]),
.path_in_container = to_string(splits[1]),
.cgroup_permission = to_string(splits[2])}; //
}) //
| ranges::to_vector; //
std::vector<Device> devices =
rec_devices //
| transform([](const std::string &mount) { //
auto splits = utils::split(mount, ':'); //
if (splits.size() < 2) {
throw std::runtime_error(fmt::format("[TOML] Docker, invalid device definition: {}", mount));
}
return Device{.path_on_host = to_string(splits.at(0)),
.path_in_container = to_string(splits.at(1)),
.cgroup_permission = to_string(splits.size() > 2 ? splits.at(2) : "mrw")}; //
}) //
| ranges::to_vector; //

auto default_socket = utils::get_env("WOLF_DOCKER_SOCKET", "/var/run/docker.sock");
auto docker_socket = toml::find_or<std::string>(runner_obj, "docker_socket", default_socket);
Expand All @@ -57,7 +73,6 @@ class RunDocker : public state::Runner {
toml::find_or<std::string>(runner_obj, "base_create_json", R"({
"HostConfig": {
"IpcMode": "host",
"DeviceRequests": [{"Driver":"","Count":-1,"Capabilities":[["gpu"]]}]
}
})"),
Container{.id = "",
Expand Down Expand Up @@ -86,7 +101,8 @@ class RunDocker : public state::Runner {
container.mounts | transform([](const auto &el) { return fmt::format("{}", el); }) | ranges::to_vector},
{"devices",
container.devices | transform([](const auto &el) { return fmt::format("{}", el); }) | ranges::to_vector},
{"env", container.env}};
{"env", container.env},
{"base_create_json", base_create_json}};
}

protected:
Expand Down
50 changes: 50 additions & 0 deletions tests/docker/testDocker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ using Catch::Matchers::Contains;
using Catch::Matchers::Equals;

#include <docker/docker.hpp>
#include <runners/docker.hpp>

TEST_CASE("Docker API", "DOCKER") {
docker::init();
Expand Down Expand Up @@ -46,4 +47,53 @@ TEST_CASE("Docker API", "DOCKER") {

REQUIRE(!docker_api.remove_by_id(first_container->id)); // This container doesn't exist anymore
REQUIRE(docker_api.remove_by_id(second_container->id));
}

TEST_CASE("Docker TOML", "DOCKER") {
docker::init();
docker::DockerAPI docker_api;

auto event_bus = std::make_shared<dp::event_bus>();
std::string toml_cfg = R"(
type = "docker"
name = "WolfTestHelloWorld"
image = "hello-world"
mounts = [
"/tmp/sockets:/tmp/.X11-unix/",
"/tmp/sockets:/run/user/1000/pulse/:ro"
]
devices = [
"/dev/input/mice:/dev/input/mice:ro",
"/a/b/c:/d/e/f",
"/tmp:/tmp:rw",
]
ports = [
"1234:1235",
"1234:1235:udp"
]
env = [
"LOG_LEVEL=info"
]
base_create_json = "{'HostConfig': {}}"
)";
std::istringstream is(toml_cfg, std::ios_base::binary | std::ios_base::in);
auto container = docker::RunDocker::from_toml(event_bus, toml::parse(is, "std::string")).serialise();

REQUIRE_THAT(container.at("type").as_string(), Equals("docker"));
REQUIRE_THAT(container.at("name").as_string(), Equals("WolfTestHelloWorld"));
REQUIRE_THAT(container.at("image").as_string(), Equals("hello-world"));

REQUIRE_THAT(toml::get<std::vector<std::string>>(container.at("ports")),
Equals(std::vector<std::string>{"1234:1235/tcp", "1234:1235/udp"}));
REQUIRE_THAT(toml::get<std::vector<std::string>>(container.at("devices")),
Equals(std::vector<std::string>{
"/dev/input/mice:/dev/input/mice:ro",
"/a/b/c:/d/e/f:mrw",
"/tmp:/tmp:rw",
}));
REQUIRE_THAT(toml::get<std::vector<std::string>>(container.at("env")),
Equals(std::vector<std::string>{"LOG_LEVEL=info"}));
REQUIRE_THAT(container.at("base_create_json").as_string(), Equals("{'HostConfig': {}}"));
}

0 comments on commit 0ba8aeb

Please sign in to comment.