#include "server_env_manager.hpp" #include "utils/directories.hpp" #include "utils/utils.hpp" #include "services.hpp" #include "templates.hpp" #include "utils/utils.hpp" #include "utils/json.hpp" #include "utils/execute.hpp" #include #include #include #include #include #include #include #include #include #include // For potential shell-like expansion if needed namespace dropshell { server_env_manager::server_env_manager(const std::string& server_name) : mValid(false), mServerName(server_name) { if (server_name.empty()) return; // Construct the full path to server.env std::string server_env_path = localfile::server_json(server_name); // Check if file exists if (!std::filesystem::exists(server_env_path)) { std::cerr << "Server environment file not found: " + server_env_path << " for server " << server_name << std::endl; return; } try { // Use envmanager to handle the environment file nlohmann::json server_env_json = nlohmann::json::parse(std::ifstream(server_env_path)); if (server_env_json.empty()) { std::cerr << "Error: Failed to parse server environment file: " + server_env_path << std::endl; return; } // get the variables from the json for (const auto& var : server_env_json.items()) { std::string value; if (var.value().is_string()) value = var.value(); else if (var.value().is_number_integer()) value = std::to_string(var.value().get()); else if (var.value().is_boolean()) value = var.value() ? "true" : "false"; else value = var.value().dump(); mVariables[var.key()] = replace_with_environment_variables_like_bash(value); } // Verify required variables exist for (const auto& var : {"SSH_HOST", "SSH_USER", "SSH_PORT", "DROPSHELL_DIR"}) { if (mVariables.find(var) == mVariables.end()) { // Print the variables identified in the file std::cout << "Variables identified in the file:" << std::endl; for (const auto& v : mVariables) { std::cout << " " << v.first << std::endl; } throw std::runtime_error("Missing required variable: " + std::string(var)); } } mValid = true; } catch (const std::exception& e) { std::cerr << "Failed to parse server environment file: " + std::string(e.what()) << std::endl; } } bool server_env_manager::create_server_env(const std::string &server_env_path, const std::string &SSH_HOST, const std::string &SSH_USER, const std::string &SSH_PORT, const std::string &DROPSHELL_DIR) { nlohmann::json server_env_json; server_env_json["SSH_HOST"] = SSH_HOST; server_env_json["SSH_USER"] = SSH_USER; server_env_json["SSH_PORT"] = SSH_PORT; server_env_json["DROPSHELL_DIR"] = DROPSHELL_DIR; try { std::ofstream server_env_file(server_env_path); server_env_file << server_env_json.dump(4); server_env_file.close(); return true; } catch (const std::exception& e) { std::cerr << "Failed to create server environment file: " + std::string(e.what()) << std::endl; return false; } } std::string server_env_manager::get_variable(const std::string& name) const { auto it = mVariables.find(name); if (it == mVariables.end()) { return ""; } return it->second; } sCommand server_env_manager::construct_standard_template_run_cmd(const std::string &service_name, const std::string &command, std::vector args, bool silent) const { if (command.empty()) return sCommand(); std::string remote_service_template_path = remotepath::service_template(mServerName,service_name); std::string script_path = remote_service_template_path + "/" + command + ".sh"; std::map env_vars; if (!get_all_service_env_vars(mServerName, service_name, env_vars)) { std::cerr << "Error: Failed to get all service env vars for " << service_name << std::endl; return sCommand(); } std::string argstr = ""; for (const auto& arg : args) { argstr += " " + quote(dequote(trim(arg))); } sCommand scommand(remote_service_template_path, "bash " + quote(script_path) + argstr + (silent ? " > /dev/null 2>&1" : ""), env_vars); if (scommand.empty()) std::cerr << "Error: Failed to construct command for " << service_name << " " << command << std::endl; return scommand; } bool server_env_manager::check_remote_dir_exists(const std::string &dir_path) const { sCommand scommand("test -d " + quote(dir_path)); return execute_ssh_command(get_SSH_INFO(), scommand, cMode::Silent); } bool server_env_manager::check_remote_file_exists(const std::string& file_path) const { sCommand scommand("test -f " + quote(file_path)); return execute_ssh_command(get_SSH_INFO(), scommand, cMode::Silent); } bool server_env_manager::check_remote_items_exist(const std::vector &file_paths) const { // convert file_paths to a single string, separated by spaces std::string file_paths_str; std::string file_names_str; for (const auto& file_path : file_paths) { file_paths_str += quote(file_path) + " "; file_names_str += std::filesystem::path(file_path).filename().string() + " "; } // check if all items in the vector exist on the remote server, in a single command. sCommand scommand("for item in " + file_paths_str + "; do test -f $item; done"); bool okay = execute_ssh_command(get_SSH_INFO(), scommand, cMode::Silent); if (!okay) { std::cerr << "Error: Required items not found on remote server: " << file_names_str << std::endl; return false; } return true; } bool server_env_manager::remove_remote_dir(const std::string &dir_path, bool silent) const { std::filesystem::path path(dir_path); std::filesystem::path parent_path = path.parent_path(); std::string target_dir = path.filename().string(); if (parent_path.empty()) parent_path="/"; if (target_dir.empty()) return false; if (!silent) std::cout << "Removing remote directory " << target_dir << " in " << parent_path << " on " << mServerName << std::endl; std::string remote_cmd = "docker run --rm -v " + quote(parent_path.string()) + ":/parent " + " alpine rm -rf \"/parent/" + target_dir + "\""; if (!silent) std::cout << "Running command: " << remote_cmd << std::endl; sCommand scommand(remote_cmd); cMode mode = (silent ? cMode::Silent : cMode::None); return execute_ssh_command(get_SSH_INFO(), scommand, mode); } bool server_env_manager::run_remote_template_command(const std::string &service_name, const std::string &command, std::vector args, bool silent, std::map extra_env_vars) const { sCommand scommand = construct_standard_template_run_cmd(service_name, command, args, silent); // add the extra env vars to the command for (const auto& [key, value] : extra_env_vars) scommand.add_env_var(key, value); if (scommand.get_command_to_run().empty()) return false; cMode mode = (command=="ssh") ? (cMode::Interactive | cMode::RawCommand) : cMode::Silent; return execute_ssh_command(get_SSH_INFO(), scommand, mode); } bool server_env_manager::run_remote_template_command_and_capture_output(const std::string &service_name, const std::string &command, std::vector args, std::string &output, bool silent, std::map extra_env_vars) const { sCommand scommand = construct_standard_template_run_cmd(service_name, command, args, false); if (scommand.get_command_to_run().empty()) return false; // add the extra env vars to the command for (const auto& [key, value] : extra_env_vars) scommand.add_env_var(key, value); cMode mode = cMode::CaptureOutput | cMode::RawCommand; return execute_ssh_command(get_SSH_INFO(), scommand, mode, &output); } // base64 <<< "FOO=BAR WHEE=YAY bash ./test.sh" // echo YmFzaCAtYyAnRk9PPUJBUiBXSEVFPVlBWSBiYXNoIC4vdGVzdC5zaCcK | base64 -d | bash } // namespace dropshell