From 5a549673206d1f51d36c434ad1a4d95d12466c75 Mon Sep 17 00:00:00 2001 From: Ravin Perera <33562092+ravinsp@users.noreply.github.com> Date: Thu, 15 Jul 2021 12:56:02 +0530 Subject: [PATCH] Ability to specify docker image in create msg. (#31) * Added docker image list in config. * Specify docker image in create msg. --- examples/message-board/message-board.js | 14 ++++++++--- src/comm/comm_session.cpp | 2 +- src/conf.cpp | 33 +++++++++++++++++++++++++ src/conf.hpp | 6 +++++ src/hp_manager.cpp | 27 ++++++++++++++------ src/hp_manager.hpp | 4 +-- src/msg/json/msg_json.cpp | 16 +++++++++++- src/msg/msg_common.hpp | 2 ++ 8 files changed, 88 insertions(+), 16 deletions(-) diff --git a/examples/message-board/message-board.js b/examples/message-board/message-board.js index 3039b20..0d651a4 100644 --- a/examples/message-board/message-board.js +++ b/examples/message-board/message-board.js @@ -71,24 +71,30 @@ server.listen(5000, () => { else { switch (inp) { case 'create': - contractId = await askForInput('Contract ID (optional)', uuidv4()); + contractId = await askForInput('Contract ID (default:uuidv4)', uuidv4()); + image = await askForInput('Image: 1=ubuntu(default) | 2=nodejs', "1"); + if (image != "1" && image != "2") { + console.error('Invalid image. (Should be "1" or "2").') + break; + } sendToAll(JSON.stringify({ id: uuidv4(), type: 'create', owner_pubkey: 'ed5cb83404120ac759609819591ef839b7d222c84f1f08b3012f490586159d2b50', - contract_id: contractId + contract_id: contractId, + image: (image == "1" ? "ubt.20.04" : "ubt.20.04-njs.14") })); break; case 'initiate': containerName = await askForInput('Container Name'); - role = await askForInput('Role or ', "validator"); + role = await askForInput('Role: validator(default) | observer', "validator"); if (role != 'validator' && role != 'observer') { console.error('Invalid role. (Should be "validator" or "observer").') break; } - history = await askForInput('History ', "custom,1,1"); + history = await askForInput('History <{full|custom},max_primary_shards,max_raw_shards> (custom,1,1)', "custom,1,1"); split = []; if (history) { split = history.split(','); diff --git a/src/comm/comm_session.cpp b/src/comm/comm_session.cpp index b32a144..607a0cb 100644 --- a/src/comm/comm_session.cpp +++ b/src/comm/comm_session.cpp @@ -181,7 +181,7 @@ namespace comm __HANDLE_RESPONSE(msg.id, msg::MSGTYPE_CREATE_RES, "format_error", -1); hp::instance_info info; - if (hp::create_new_instance(info, msg.pubkey, msg.contract_id) == -1) + if (hp::create_new_instance(info, msg.pubkey, msg.contract_id, msg.image) == -1) __HANDLE_RESPONSE(msg.id, msg::MSGTYPE_CREATE_RES, "create_error", -1); std::string create_res; diff --git a/src/conf.cpp b/src/conf.cpp index 7659b4b..78d1995 100644 --- a/src/conf.cpp +++ b/src/conf.cpp @@ -67,6 +67,9 @@ namespace conf cfg.system.max_cpu_us = 5000000; // CPU cfs period cannot be less than 1ms (i.e. 1000) or larger than 1s (i.e. 1000000) per instance. cfg.system.max_storage_kbytes = 2048000; // Total 2GB + cfg.docker.images["ubt.20.04"] = "hotpocketdev/sashimono:hp-ubt.20.04"; + cfg.docker.images["ubt.20.04-njs.14"] = "hotpocketdev/sashimono:hp-ubt.20.04-njs.14"; + cfg.log.max_file_count = 50; cfg.log.max_mbytes_per_file = 10; cfg.log.log_level = "inf"; @@ -280,6 +283,24 @@ namespace conf } } + // docker + { + jpath = "docker"; + + try + { + const jsoncons::ojson &docker = d["docker"]; + + for (const auto &elem : docker["images"].object_range()) + cfg.docker.images[elem.key()] = elem.value().as(); + } + catch (const std::exception &e) + { + print_missing_field_error(jpath, e); + return -1; + } + } + // log { jpath = "log"; @@ -350,6 +371,18 @@ namespace conf d.insert_or_assign("system", system_config); } + // Docker configs. + { + jsoncons::ojson docker_config; + + jsoncons::ojson images; + for (const auto &[key, name] : cfg.docker.images) + images.insert_or_assign(key, name); + docker_config.insert_or_assign("images", images); + + d.insert_or_assign("docker", docker_config); + } + // Log configs. { jsoncons::ojson log_config; diff --git a/src/conf.hpp b/src/conf.hpp index 8615c61..9bfc63e 100644 --- a/src/conf.hpp +++ b/src/conf.hpp @@ -69,12 +69,18 @@ namespace conf size_t max_instance_count = 0; // Max number of instances that can be created. }; + struct docker_config + { + std::unordered_map images; + }; + struct sa_config { std::string version; hp_config hp; server_config server; system_config system; + docker_config docker; log_config log; }; diff --git a/src/hp_manager.cpp b/src/hp_manager.cpp index e2f2067..34008e7 100644 --- a/src/hp_manager.cpp +++ b/src/hp_manager.cpp @@ -27,7 +27,7 @@ namespace hp // We instruct the demon to restart the container automatically once the container exits except manually stopping. constexpr const char *DOCKER_CREATE = "DOCKER_HOST=unix:///run/user/$(id -u %s)/docker.sock %s/dockerbin/docker create -t -i --stop-signal=SIGINT --name=%s -p %s:%s -p %s:%s \ - --restart unless-stopped --mount type=bind,source=%s,target=/contract hotpocketdev/sashimono:hp-ubt.20.04 run /contract"; + --restart unless-stopped --mount type=bind,source=%s,target=/contract %s run /contract"; constexpr const char *DOCKER_START = "DOCKER_HOST=unix:///run/user/$(id -u %s)/docker.sock %s/dockerbin/docker start %s"; constexpr const char *DOCKER_STOP = "DOCKER_HOST=unix:///run/user/$(id -u %s)/docker.sock %s/dockerbin/docker stop %s"; constexpr const char *DOCKER_REMOVE = "DOCKER_HOST=unix:///run/user/$(id -u %s)/docker.sock %s/dockerbin/docker rm -f %s"; @@ -131,9 +131,10 @@ namespace hp * @param info Structure holding the generated instance info. * @param owner_pubkey Public key of the instance owner. * @param contract_id Contract id to be configured. + * @param image_key Docker image name to use (must exist in the config iamge list). * @return 0 on success and -1 on error. */ - int create_new_instance(instance_info &info, std::string_view owner_pubkey, const std::string &contract_id) + int create_new_instance(instance_info &info, std::string_view owner_pubkey, const std::string &contract_id, const std::string &image_key) { // If the max alloved instance count is already allocated. We won't allow more. const int allocated_count = sqlite::get_allocated_instance_count(db); @@ -157,6 +158,14 @@ namespace hp return -1; } + const auto img_itr = conf::cfg.docker.images.find(image_key); + if (img_itr == conf::cfg.docker.images.end()) + { + LOG_ERROR << "Provided docker image is not allowed."; + return -1; + } + const std::string image_name = img_itr->second; + std::string container_name = crypto::generate_uuid(); // This will be the docker container name as well as the contract folder name. int retries = 0; // If the generated uuid is already assigned to a container, we try generating a @@ -197,7 +206,7 @@ namespace hp const std::string contract_dir = util::get_user_contract_dir(username, container_name); if (create_contract(username, owner_pubkey, contract_id, contract_dir, instance_ports, info) == -1 || - create_container(username, container_name, contract_dir, instance_ports, info) == -1) + create_container(username, image_name, container_name, contract_dir, instance_ports, info) == -1) { LOG_ERROR << "Error creating hp instance for " << owner_pubkey; // Remove user if instance creation failed. @@ -262,7 +271,7 @@ namespace hp read_json_values(d, hpfs_log_level, is_full_history) == -1 || util::write_json_file(config_fd, d) == -1 || hpfs::update_service_conf(info.username, hpfs_log_level, is_full_history) == -1 || - hpfs::start_hpfs_systemd(info.username) == -1) + hpfs::start_hpfs_systemd(info.username) == -1) { LOG_ERROR << "Error when setting up container. name: " << container_name; close(config_fd); @@ -293,18 +302,20 @@ namespace hp /** * Creates a hotpocket docker image on the given contract and the ports. * @param username Username of the instance user. + * @param image_name Conatiner image name to use. * @param container_name Name of the container. * @param contract_dir Directory for the contract. * @param assigned_ports Assigned ports to the container. * @return 0 on success execution or relavent error code on error. */ - int create_container(std::string_view username, std::string_view container_name, std::string_view contract_dir, const ports &assigned_ports, instance_info &info) + int create_container(std::string_view username, std::string_view image_name, std::string_view container_name, std::string_view contract_dir, const ports &assigned_ports, instance_info &info) { const std::string user_port = std::to_string(assigned_ports.user_port); const std::string peer_port = std::to_string(assigned_ports.peer_port); - const int len = 268 + username.length() + conf::ctx.exe_dir.length() + container_name.length() + (user_port.length() * 2) + (peer_port.length() * 2) + contract_dir.length(); + const int len = 268 + username.length() + conf::ctx.exe_dir.length() + container_name.length() + (user_port.length() * 2) + (peer_port.length() * 2) + contract_dir.length() + image_name.length(); char command[len]; - sprintf(command, DOCKER_CREATE, username.data(), conf::ctx.exe_dir.data(), container_name.data(), user_port.data(), user_port.data(), peer_port.data(), peer_port.data(), contract_dir.data()); + sprintf(command, DOCKER_CREATE, username.data(), conf::ctx.exe_dir.data(), container_name.data(), + user_port.data(), user_port.data(), peer_port.data(), peer_port.data(), contract_dir.data(), image_name.data()); if (system(command) != 0) { LOG_ERROR << "Error when running container. name: " << container_name; @@ -384,7 +395,7 @@ namespace hp read_json_values(d, hpfs_log_level, is_full_history) == -1 || hpfs::update_service_conf(info.username, hpfs_log_level, is_full_history) == -1 || hpfs::start_hpfs_systemd(info.username) == -1 || - docker_start(info.username, container_name) == -1) + docker_start(info.username, container_name) == -1) { LOG_ERROR << "Error when starting container. name: " << container_name; close(config_fd); diff --git a/src/hp_manager.hpp b/src/hp_manager.hpp index 7b8769b..333691a 100644 --- a/src/hp_manager.hpp +++ b/src/hp_manager.hpp @@ -58,11 +58,11 @@ namespace hp void hp_monitor_loop(); - int create_new_instance(instance_info &info, std::string_view owner_pubkey, const std::string &contract_id); + int create_new_instance(instance_info &info, std::string_view owner_pubkey, const std::string &contract_id, const std::string &image_key); int initiate_instance(std::string_view container_name, const msg::initiate_msg &config_msg); - int create_container(std::string_view username, std::string_view container_name, std::string_view contract_dir, const ports &assigned_ports, instance_info &info); + int create_container(std::string_view username, std::string_view image_name, std::string_view container_name, std::string_view contract_dir, const ports &assigned_ports, instance_info &info); int start_container(std::string_view container_name); diff --git a/src/msg/json/msg_json.cpp b/src/msg/json/msg_json.cpp index c249e0a..de5da5e 100644 --- a/src/msg/json/msg_json.cpp +++ b/src/msg/json/msg_json.cpp @@ -122,7 +122,8 @@ namespace msg::json * { * "type": "create", * "owner_pubkey": "" - * "contract_id": "" + * "contract_id": "", + * "image": "" * } * @return 0 on successful extraction. -1 for failure. */ @@ -137,13 +138,26 @@ namespace msg::json return -1; } + if (!d.contains(msg::FLD_IMAGE)) + { + LOG_ERROR << "Field image is missing."; + return -1; + } + if (!d[msg::FLD_CONTRACT_ID].is()) { LOG_ERROR << "Invalid contract_id value."; return -1; } + if (!d[msg::FLD_IMAGE].is()) + { + LOG_ERROR << "Invalid image value."; + return -1; + } + msg.contract_id = d[msg::FLD_CONTRACT_ID].as(); + msg.image = d[msg::FLD_IMAGE].as(); return 0; } diff --git a/src/msg/msg_common.hpp b/src/msg/msg_common.hpp index 3df1900..0af6479 100644 --- a/src/msg/msg_common.hpp +++ b/src/msg/msg_common.hpp @@ -12,6 +12,7 @@ namespace msg std::string type; std::string pubkey; std::string contract_id; + std::string image; }; // Keep numerical config valus as optional so when updating the config if the value is empty @@ -61,6 +62,7 @@ namespace msg constexpr const char *FLD_PUBKEY = "owner_pubkey"; constexpr const char *FLD_CONTAINER_NAME = "container_name"; constexpr const char *FLD_CONTRACT_ID = "contract_id"; + constexpr const char *FLD_IMAGE = "image"; constexpr const char *FLD_ID = "id"; constexpr const char *FLD_PEERS = "peers"; constexpr const char *FLD_UNL = "unl";