diff --git a/source/src/autocomplete.cpp b/source/src/autocomplete.cpp index 90a77ac..592c409 100644 --- a/source/src/autocomplete.cpp +++ b/source/src/autocomplete.cpp @@ -59,7 +59,7 @@ bool autocomplete(const std::vector &args) { auto servers = dropshell::get_configured_servers(); for (const auto& server : servers) - std::cout << server.name << std::endl; + std::cout << server.get_server_name() << std::endl; return true; } diff --git a/source/src/commands/backupdata.cpp b/source/src/commands/backupdata.cpp index 439617c..7309eb2 100644 --- a/source/src/commands/backupdata.cpp +++ b/source/src/commands/backupdata.cpp @@ -11,7 +11,7 @@ #include "config.hpp" #include "services.hpp" #include "servers.hpp" -#include "server_env_manager.hpp" +#include "servers.hpp" #include "templates.hpp" #include "utils/directories.hpp" #include "shared_commands.hpp" @@ -55,7 +55,7 @@ namespace dropshell bool backupdata_service(const std::string &server, const std::string &service) { - server_env_manager server_env(server); + server_config server_env(server); if (!server_env.is_valid()) { error << "Server " << server << " is not valid" << std::endl; diff --git a/source/src/commands/destroy.cpp b/source/src/commands/destroy.cpp index dbb9fa4..2548be4 100644 --- a/source/src/commands/destroy.cpp +++ b/source/src/commands/destroy.cpp @@ -2,7 +2,7 @@ #include "shared_commands.hpp" #include "config.hpp" #include "services.hpp" -#include "server_env_manager.hpp" +#include "servers.hpp" #include "utils/directories.hpp" #include "servers.hpp" #include "templates.hpp" @@ -52,7 +52,7 @@ namespace dropshell bool nuke_service(const std::string &server, const std::string &service) { - server_env_manager server_env(server); + server_config server_env(server); // step 1 - nuke on remote server. if (server_env.is_valid()) diff --git a/source/src/commands/health.cpp b/source/src/commands/health.cpp index ac4f850..f99ccc2 100644 --- a/source/src/commands/health.cpp +++ b/source/src/commands/health.cpp @@ -3,7 +3,7 @@ #include "utils/utils.hpp" #include "utils/directories.hpp" #include "shared_commands.hpp" -#include "server_env_manager.hpp" +#include "servers.hpp" #include "services.hpp" #include "servers.hpp" #include "transwarp.hpp" diff --git a/source/src/commands/install.cpp b/source/src/commands/install.cpp index 480d9fa..52b1890 100644 --- a/source/src/commands/install.cpp +++ b/source/src/commands/install.cpp @@ -69,7 +69,7 @@ namespace dropshell if (!SIvalid(service_info)) return false; - server_env_manager server_env(server); + server_config server_env(server); if (!server_env.is_valid()) return false; @@ -90,9 +90,10 @@ namespace dropshell } // Create service directory - std::string remote_service_path = remotepath::service(server, service); + std::string user = server_env.get_user_for_service(server, service); + std::string remote_service_path = remotepath(server,user).service(service); std::string mkdir_cmd = "mkdir -p " + quote(remote_service_path); - if (!execute_ssh_command(server_env.get_SSH_INFO(), sCommand("", mkdir_cmd, {}), cMode::Silent)) + if (!execute_ssh_command(server_env.get_SSH_INFO(user), sCommand("", mkdir_cmd, {}), cMode::Silent)) { std::cerr << "Failed to create service directory " << remote_service_path << std::endl; return false; @@ -100,7 +101,7 @@ namespace dropshell // Check if rsync is installed on remote host std::string check_rsync_cmd = "which rsync"; - if (!execute_ssh_command(server_env.get_SSH_INFO(), sCommand("", check_rsync_cmd, {}), cMode::Silent)) + if (!execute_ssh_command(server_env.get_SSH_INFO(user), sCommand("", check_rsync_cmd, {}), cMode::Silent)) { std::cerr << "rsync is not installed on the remote host" << std::endl; return false; @@ -108,8 +109,8 @@ namespace dropshell // Copy template files debug << "Copying: [LOCAL] " << tinfo.local_template_path() << std::endl - << std::string(8, ' ') << "[REMOTE] " << remotepath::service_template(server, service) << "/" << std::endl; - if (!shared_commands::rsync_tree_to_remote(tinfo.local_template_path().string(), remotepath::service_template(server, service), + << std::string(8, ' ') << "[REMOTE] " << remotepath(server,user).service_template(service) << "/" << std::endl; + if (!shared_commands::rsync_tree_to_remote(tinfo.local_template_path().string(), remotepath(server,user).service_template(service), server_env, false)) { std::cerr << "Failed to copy template files using rsync" << std::endl; @@ -118,8 +119,8 @@ namespace dropshell // Copy service files debug << "Copying: [LOCAL] " << localpath::service(server, service) << std::endl - << std::string(8, ' ') << "[REMOTE] " << remotepath::service_config(server, service) << std::endl; - if (!shared_commands::rsync_tree_to_remote(localpath::service(server, service), remotepath::service_config(server, service), + << std::string(8, ' ') << "[REMOTE] " << remotepath(server,user).service_config(service) << std::endl; + if (!shared_commands::rsync_tree_to_remote(localpath::service(server, service), remotepath(server,user).service_config(service), server_env, false)) { std::cerr << "Failed to copy service files using rsync" << std::endl; @@ -263,36 +264,38 @@ namespace dropshell // install the dropshell agent on the given server. maketitle("Installing dropshell agent on " + server, sColour::INFO); - std::string agent_path = remotepath::agent(server); - if (agent_path.empty()) - { - error << "Failed to get agent path for " << server << std::endl; - return 1; - } - - server_env_manager server_env(server); + server_config server_env(server); if (!server_env.is_valid()) { error << "Invalid server environment for " << server << std::endl; return 1; } - // now create the agent. - // copy across from the local agent files. - info << "Copying local agent files to remote server... " << std::flush; - shared_commands::rsync_tree_to_remote(localpath::agent_remote(), agent_path, server_env, false); - info << "done." << std::endl; - - // run the agent installer. Can't use BB64 yet, as we're installing it on the remote server. - - bool okay = execute_ssh_command(server_env.get_SSH_INFO(), sCommand(agent_path, "agent-install.sh",{}), cMode::Defaults | cMode::NoBB64, nullptr); - if (!okay) + for (const auto &user : server_env.get_users()) { - error << "ERROR: Failed to install remote agent on " << server << std::endl; - return 1; - } + info << "Installing agent for user " << user.user << " on " << server << std::endl; - info << "Installation on " << server << " complete." << std::endl; + std::string agent_path = remotepath(server,user.user).agent(); + ASSERT(agent_path == user.dir, "Agent path does not match user directory for "+user.user+"@" + server + " : " + agent_path + " != " + user.dir); + ASSERT(!agent_path.empty(), "Agent path is empty for " + user.user + "@" + server); + + // now create the agent. + // copy across from the local agent files. + info << "Copying local agent files to remote server... " << std::flush; + shared_commands::rsync_tree_to_remote(localpath::agent_remote(), agent_path, server_env, false); + info << "done." << std::endl; + + // run the agent installer. Can't use BB64 yet, as we're installing it on the remote server. + + bool okay = execute_ssh_command(server_env.get_SSH_INFO(user.user), sCommand(agent_path, "agent-install.sh",{}), cMode::Defaults | cMode::NoBB64, nullptr); + if (!okay) + { + error << "ERROR: Failed to install remote agent on " << server << std::endl; + return 1; + } + + info << "Installation on " << server << " complete." << std::endl; + } return 0; } @@ -313,10 +316,10 @@ namespace dropshell return rval; // install the dropshell agent on all servers. - std::vector servers = get_configured_servers(); + std::vector servers = get_configured_servers(); for (const auto &server : servers) { - rval = install_server(server.name); + rval = install_server(server.get_server_name()); if (rval != 0) return rval; } diff --git a/source/src/commands/list.cpp b/source/src/commands/list.cpp index fe484e1..f26a32c 100644 --- a/source/src/commands/list.cpp +++ b/source/src/commands/list.cpp @@ -6,7 +6,7 @@ #include "servers.hpp" #include "tableprint.hpp" #include "transwarp.hpp" -#include "server_env_manager.hpp" +#include "servers.hpp" #include "services.hpp" #include @@ -81,26 +81,40 @@ void list_servers() { } tableprint tp("All DropShell Servers"); - tp.add_row({"Name", "User", "Address", "Health", "Ports"}); + tp.add_row({"Name", "Address", "User", "Health", "Ports"}); info << "Checking "< status = shared_commands::get_all_services_status(server.name); + auto task = transwarp::for_each(exec, servers.begin(), servers.end(), [&](const server_config& server) { - std::set ports_used; - std::string serviceticks = ""; - for (const auto& [service_name, service_status] : status) { - ports_used.insert(service_status.ports.begin(), service_status.ports.end()); - serviceticks += shared_commands::HealthStatus2String(service_status.health) + " "; + server_config server_env(server.get_server_name()); + if (!server_env.is_valid()) + { + error << "Invalid server environment for " << server.get_server_name() << std::endl; + return; } - std::string ports_used_str = ""; - for (const auto& port : ports_used) - ports_used_str += std::to_string(port) + " "; - - tp.add_row({server.name, server.ssh_user, server.ssh_host, serviceticks, ports_used_str}); + + int first=true; + for (const auto &user : server_env.get_users()) + { + std::map status = shared_commands::get_all_services_status(server.get_server_name()); + + std::set ports_used; + std::string serviceticks = ""; + for (const auto& [service_name, service_status] : status) { + ports_used.insert(service_status.ports.begin(), service_status.ports.end()); + serviceticks += shared_commands::HealthStatus2String(service_status.health) + " "; + } + std::string ports_used_str = ""; + for (const auto& port : ports_used) + ports_used_str += std::to_string(port) + " "; + + tp.add_row({(first ? server.get_server_name() : ""), (first ? server.get_SSH_HOST() : ""), user.user, serviceticks, ports_used_str}); + first = false; + } + ++checked; // print out a tick character for each server checked. info << checked << " ✓ " << std::flush; @@ -114,7 +128,7 @@ void list_servers() { void show_server_details(const std::string& server_name) { - server_env_manager env(server_name); + server_config env(server_name); if (!env.is_valid()) { error << "Error: Invalid server environment file: " << server_name << std::endl; return; diff --git a/source/src/commands/restoredata.cpp b/source/src/commands/restoredata.cpp index 7064487..220936f 100644 --- a/source/src/commands/restoredata.cpp +++ b/source/src/commands/restoredata.cpp @@ -11,7 +11,7 @@ #include "config.hpp" #include "services.hpp" #include "servers.hpp" -#include "server_env_manager.hpp" +#include "servers.hpp" #include "templates.hpp" #include "utils/directories.hpp" #include "shared_commands.hpp" @@ -92,7 +92,7 @@ namespace dropshell std::string service = ctx.args[1]; std::string backup_arg = ctx.args[2]; - server_env_manager server_env(server); + server_config server_env(server); if (!server_env.is_valid()) { error << "Server " << server << " is not valid" << std::endl; diff --git a/source/src/commands/shared_commands.cpp b/source/src/commands/shared_commands.cpp index dbaf97f..b10766d 100644 --- a/source/src/commands/shared_commands.cpp +++ b/source/src/commands/shared_commands.cpp @@ -1,7 +1,7 @@ #include "shared_commands.hpp" #include "utils/assert.hpp" #include "utils/utils.hpp" -#include "server_env_manager.hpp" +#include "servers.hpp" #include "directories.hpp" #include "services.hpp" #include "servers.hpp" @@ -21,10 +21,10 @@ namespace dropshell if (ctx.args.size() == 0) { // just the command, no args yet. // list servers - std::vector servers = get_configured_servers(); + std::vector servers = get_configured_servers(); for (const auto &server : servers) { - rawout << server.name << std::endl; + rawout << server.get_server_name() << std::endl; } } else if (ctx.args.size() == 1) @@ -54,7 +54,7 @@ namespace dropshell bool rsync_tree_to_remote( const std::string &local_path, const std::string &remote_path, - server_env_manager &server_env, + server_config &server_env, bool silent) { ASSERT(!local_path.empty() && !remote_path.empty(), "Local or remote path not specified. Can't rsync."); @@ -84,7 +84,7 @@ namespace dropshell // ------------------------------------------------------------------------------------------------ // cRemoteTempFolder : SHARED CLASS // ------------------------------------------------------------------------------------------------ - cRemoteTempFolder::cRemoteTempFolder(const server_env_manager &server_env, std::string user) : + cRemoteTempFolder::cRemoteTempFolder(const server_config &server_env, std::string user) : mServerEnv(server_env), mUser(user) { std::string p = remotepath(server_env.get_server_name(),user).temp_files() + "/" + random_alphanumeric_string(10); @@ -113,7 +113,7 @@ namespace dropshell { std::map status; - server_env_manager env(server_name); + server_config env(server_name); if (!env.is_valid()) { error << "Invalid server environment" << std::endl; @@ -214,7 +214,7 @@ namespace dropshell // ------------------------------------------------------------------------------------------------ HealthStatus is_healthy(const std::string &server, const std::string &service) { - server_env_manager env(server); + server_config env(server); if (!env.is_valid()) { error << "Server service not initialized" << std::endl; @@ -307,7 +307,7 @@ namespace dropshell // ------------------------------------------------------------------------------------------------ // scp_file_to_remote : SHARED COMMAND // ------------------------------------------------------------------------------------------------ - bool scp_file_to_remote(const server_env_manager &server_env, const std::string &local_path, const std::string &remote_path, bool silent) + bool scp_file_to_remote(const server_config &server_env, const std::string &local_path, const std::string &remote_path, bool silent) { if (!server_env.is_valid()) { @@ -322,7 +322,7 @@ namespace dropshell // ------------------------------------------------------------------------------------------------ // scp_file_from_remote : SHARED COMMAND // ------------------------------------------------------------------------------------------------ - bool scp_file_from_remote(const server_env_manager &server_env, const std::string &remote_path, const std::string &local_path, bool silent) + bool scp_file_from_remote(const server_config &server_env, const std::string &remote_path, const std::string &local_path, bool silent) { if (!server_env.is_valid()) { diff --git a/source/src/commands/shared_commands.hpp b/source/src/commands/shared_commands.hpp index 7bfc800..cf03547 100644 --- a/source/src/commands/shared_commands.hpp +++ b/source/src/commands/shared_commands.hpp @@ -3,7 +3,7 @@ #include "servers.hpp" #include "command_registry.hpp" -#include "server_env_manager.hpp" +#include "servers.hpp" namespace dropshell { @@ -31,19 +31,19 @@ namespace dropshell class cRemoteTempFolder { public: - cRemoteTempFolder(const server_env_manager &server_env, std::string user); // create a temp folder on the remote server + cRemoteTempFolder(const server_config &server_env, std::string user); // create a temp folder on the remote server ~cRemoteTempFolder(); // delete the temp folder on the remote server std::string path() const; // get the path to the temp folder on the remote server private: std::string mPath; - const server_env_manager &mServerEnv; + const server_config &mServerEnv; std::string mUser; }; bool rsync_tree_to_remote( const std::string &local_path, const std::string &remote_path, - server_env_manager &server_env, + server_config &server_env, bool silent); std::string get_arch(); @@ -79,8 +79,8 @@ namespace dropshell std::string mDatetime; }; - bool scp_file_to_remote(const server_env_manager &server_env, const std::string &local_path, const std::string &remote_path, bool silent); - bool scp_file_from_remote(const server_env_manager &server_env, const std::string &remote_path, const std::string &local_path, bool silent); + bool scp_file_to_remote(const server_config &server_env, const std::string &local_path, const std::string &remote_path, bool silent); + bool scp_file_from_remote(const server_config &server_env, const std::string &remote_path, const std::string &local_path, bool silent); // defined in backupdata.cpp, used by restoredata.cpp. bool backupdata_service(const std::string& server, const std::string& service); diff --git a/source/src/commands/ssh.cpp b/source/src/commands/ssh.cpp index 84930c5..27f3a59 100644 --- a/source/src/commands/ssh.cpp +++ b/source/src/commands/ssh.cpp @@ -3,7 +3,7 @@ #include "utils/utils.hpp" #include "utils/directories.hpp" #include "shared_commands.hpp" -#include "server_env_manager.hpp" +#include "servers.hpp" #include "services.hpp" #include "servers.hpp" #include "templates.hpp" @@ -42,7 +42,7 @@ namespace dropshell bool ssh_into_server(const std::string &server, std::string user) { - server_env_manager server_env(server); + server_config server_env(server); if (!server_env.is_valid()) { error << "Server " << server << " is not valid" << std::endl; @@ -54,7 +54,7 @@ namespace dropshell bool ssh_into_service(const std::string &server, const std::string &service) { - server_env_manager server_env(server); + server_config server_env(server); if (!server_env.is_valid()) { error << "Server " << server << " is not valid" << std::endl; @@ -109,7 +109,7 @@ namespace dropshell server = arg1; // get the first user from the server.env file, and ssh in as that user. - server_env_manager server_env(server); + server_config server_env(server); if (!server_env.is_valid()) { error << "Server " << server << " is not valid" << std::endl; diff --git a/source/src/commands/start.cpp b/source/src/commands/start.cpp index 2185049..44853a6 100644 --- a/source/src/commands/start.cpp +++ b/source/src/commands/start.cpp @@ -3,7 +3,7 @@ #include "utils/utils.hpp" #include "utils/directories.hpp" #include "shared_commands.hpp" -#include "server_env_manager.hpp" +#include "servers.hpp" #include "services.hpp" #include "servers.hpp" #include "utils/output.hpp" @@ -44,7 +44,7 @@ namespace dropshell bool start_service(const std::string &server, const std::string &service) { - server_env_manager server_env(server); + server_config server_env(server); if (!server_env.is_valid()) { error << "Server " << server << " is not valid" << std::endl; diff --git a/source/src/commands/stop.cpp b/source/src/commands/stop.cpp index 60434dd..f8aee6c 100644 --- a/source/src/commands/stop.cpp +++ b/source/src/commands/stop.cpp @@ -3,7 +3,7 @@ #include "utils/utils.hpp" #include "utils/directories.hpp" #include "shared_commands.hpp" -#include "server_env_manager.hpp" +#include "servers.hpp" #include "services.hpp" #include "servers.hpp" #include "utils/output.hpp" @@ -44,7 +44,7 @@ namespace dropshell bool stop_service(const std::string &server, const std::string &service) { - server_env_manager server_env(server); + server_config server_env(server); if (!server_env.is_valid()) { error << "Server " << server << " is not valid" << std::endl; diff --git a/source/src/commands/uninstall.cpp b/source/src/commands/uninstall.cpp index b19ae25..4138150 100644 --- a/source/src/commands/uninstall.cpp +++ b/source/src/commands/uninstall.cpp @@ -46,7 +46,7 @@ namespace dropshell { maketitle("Uninstalling " + service + " on " + server); - server_env_manager server_env(server); + server_config server_env(server); if (!server_env.is_valid()) { error << "Invalid server: " << server << std::endl; diff --git a/source/src/config.cpp b/source/src/config.cpp index 73975ef..16e412f 100644 --- a/source/src/config.cpp +++ b/source/src/config.cpp @@ -3,7 +3,6 @@ #include #include "config.hpp" #include "utils/utils.hpp" -#include "utils/json.hpp" #include #include "utils/execute.hpp" #include "output.hpp" diff --git a/source/src/config.hpp b/source/src/config.hpp index 7081f22..090d706 100644 --- a/source/src/config.hpp +++ b/source/src/config.hpp @@ -2,7 +2,9 @@ #include #include -#include "utils/json.hpp" + +#define JSON_INLINE_ALL +#include "json.hpp" namespace dropshell { diff --git a/source/src/server_env_manager.cpp b/source/src/server_env_manager.cpp deleted file mode 100644 index 9d6e70d..0000000 --- a/source/src/server_env_manager.cpp +++ /dev/null @@ -1,321 +0,0 @@ -#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 "utils/assert.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.json - 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_PORT", "USERS"}) { - 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)); - } - } - - // Parse users array - if (!server_env_json.contains("USERS") || !server_env_json["USERS"].is_array()) { - std::cerr << "Error: USERS array not found or invalid in server configuration" << std::endl; - return; - } - - for (const auto& user_json : server_env_json["USERS"]) { - UserConfig user; - user.user = user_json["USER"].get(); - user.dir = user_json["DIR"].get(); - mUsers.push_back(user); - } - - if (mUsers.empty()) { - std::cerr << "Error: No users defined in server configuration" << std::endl; - return; - } - - 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_json_file(const std::string &server_env_path, const std::string &SSH_HOST, const std::string &SSH_PORT, const std::vector& users) -{ - nlohmann::json server_env_json; - server_env_json["SSH_HOST"] = SSH_HOST; - server_env_json["SSH_PORT"] = std::stoi(SSH_PORT); - - // Create users array - nlohmann::json users_array = nlohmann::json::array(); - for (const auto& user : users) { - nlohmann::json user_json; - user_json["USER"] = user.user; - user_json["DIR"] = user.dir; - users_array.push_back(user_json); - } - server_env_json["USERS"] = users_array; - - 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_user_dir(const std::string& user) const { - for (const auto& u : mUsers) { - if (u.user == user) { - return u.dir; - } - } - return ""; -} - -std::string server_env_manager::get_user_for_service(const std::string &server, const std::string &service) -{ - auto services_info = get_server_services_info(server); - if (std::find_if(services_info.begin(), services_info.end(), - [&service](const LocalServiceInfo &si) { return si.service_name == service; }) != services_info.end()) { - // found a service with matching name. - auto it = std::find_if(services_info.begin(), services_info.end(), - [&service](const LocalServiceInfo &si) { return si.service_name == service; }); - if (it != services_info.end()) { - return it->user; - } - } - return ""; -} - -sSSHInfo server_env_manager::get_SSH_INFO(std::string user) const -{ - ASSERT(!user.empty(), "User is empty, cannot get SSH info."); - // Find user in mUsers vector - auto it = std::find_if(mUsers.begin(), mUsers.end(), - [&user](const UserConfig& u) { return u.user == user; }); - ASSERT(it != mUsers.end(), ("User " + user + " not found in server environment.")); - return sSSHInfo{get_SSH_HOST(), user, get_SSH_PORT(), get_server_name()}; -} - -bool server_env_manager::check_remote_dir_exists(const std::string &dir_path, std::string user) const -{ - sCommand scommand("", "test -d " + quote(dir_path),{}); - return execute_ssh_command(get_SSH_INFO(user), scommand, cMode::Silent); -} - -bool server_env_manager::check_remote_file_exists(const std::string& file_path, std::string user) const { - sCommand scommand("", "test -f " + quote(file_path),{}); - return execute_ssh_command(get_SSH_INFO(user), scommand, cMode::Silent); -} - -bool server_env_manager::check_remote_items_exist(const std::vector &file_paths, std::string user) 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",{}); - - dropshell::sSSHInfo sshinfo = get_SSH_INFO(user); - bool okay = execute_ssh_command(sshinfo, 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, std::string user) 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::Defaults); - - dropshell::sSSHInfo sshinfo = get_SSH_INFO(user); - return execute_ssh_command(sshinfo, 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 -{ - std::string user = get_user_for_service(mServerName, service_name); - auto scommand = construct_standard_template_run_cmd(service_name, command, args, silent); - if (!scommand.has_value()) - return false; - - // 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) : (silent ? cMode::Silent : cMode::Defaults); - return execute_ssh_command(get_SSH_INFO(user), scommand.value(), 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 -{ - std::string user = get_user_for_service(mServerName, service_name); - auto scommand = construct_standard_template_run_cmd(service_name, command, args, false); - if (!scommand.has_value()) - return false; - - // add the extra env vars to the command - for (const auto& [key, value] : extra_env_vars) - scommand->add_env_var(key, value); - - return execute_ssh_command(get_SSH_INFO(user), scommand.value(), cMode::Defaults, &output); -} - -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; -} - -std::optional server_env_manager::construct_standard_template_run_cmd(const std::string &service_name, const std::string &command, const std::vector args, const bool silent) const -{ - if (command.empty()) - return std::nullopt; - - std::string user = get_user_for_service(mServerName, service_name); - - std::string remote_service_template_path = remotepath(mServerName,user).service_template(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 std::nullopt; - } - - std::string argstr = ""; - for (const auto& arg : args) { - argstr += " " + quote(dequote(trim(arg))); - } - - if(env_vars.find("RUNAS") == env_vars.end()) { - error << "Error: RUNAS is not set in .template_info.env for the service." << std::endl; - return std::nullopt; - } - std::string runas = env_vars.find("RUNAS")->second; - if(runas!="root" && runas!="user") { - error << "Error: RUNAS is not set to root or user in .template_info.env for the service." << std::endl; - return std::nullopt; - } - bool run_as_root = runas == "root"; - - if (run_as_root && !get_ALLOW_ROOT_SERVICES()) { - error << "Error: The service " << service_name << " is set to run as root, but the server environment does not allow root services." << std::endl; - return std::nullopt; - } - - sCommand sc( - remote_service_template_path, - quote(script_path) + argstr + (silent ? " > /dev/null 2>&1" : ""), - env_vars, - run_as_root - ); - - if (sc.empty()) { - std::cerr << "Error: Failed to construct command for " << service_name << " " << command << std::endl; - return std::nullopt; - } - return sc; -} - - -} // namespace dropshell \ No newline at end of file diff --git a/source/src/server_env_manager.hpp b/source/src/server_env_manager.hpp deleted file mode 100644 index 56e6f3d..0000000 --- a/source/src/server_env_manager.hpp +++ /dev/null @@ -1,92 +0,0 @@ -// server_env.hpp -// -// read the server.env file and provide a class to access the variables - -#ifndef __SERVER_ENV_HPP -#define __SERVER_ENV_HPP - -#include -#include -#include -#include -#include "utils/execute.hpp" -#include - -#define JSON_INLINE_ALL -#include - -namespace dropshell { - -struct UserConfig { - std::string user; - std::string dir; -}; - -class server_env_manager; - -// ------------------------------------------------------------------------------------------------ - -// reads path / server.env and provides a class to access the variables. -// each env file is required to have the following variables: -// SSH_HOST -// SSH_UNPRIVILEGED_USER -// SSH_PORT -// the following replacements are made in the values: -// ${USER} -> the username of the user running dropshell -class server_env_manager { - public: - server_env_manager(const std::string& server_name); - - static bool create_server_json_file( - const std::string& server_env_path, - const std::string& SSH_HOST, - const std::string& SSH_PORT, - const std::vector& users); - - std::string get_variable(const std::string& name) const; - - // trivial getters. - const std::map& get_variables() const { return mVariables; } - std::string get_SSH_HOST() const { return get_variable("SSH_HOST"); } - std::string get_SSH_PORT() const { return get_variable("SSH_PORT"); } - std::string get_SSH_UNPRIVILEGED_USER() const { return get_variable("SSH_UNPRIVILEGED_USER"); } - bool get_ALLOW_ROOT_SERVICES() const { return get_variable("ALLOW_ROOT_SERVICES")=="true"; } - std::vector get_users() const { return mUsers; } - std::string get_user_dir(const std::string& user) const; - bool is_valid() const { return mValid; } - std::string get_server_name() const { return mServerName; } - - static std::string get_user_for_service(const std::string& server, const std::string& service); - - sSSHInfo get_SSH_INFO(std::string user) const; - -// helper functions - public: - bool check_remote_dir_exists(const std::string &dir_path, std::string user) const; - bool check_remote_file_exists(const std::string& file_path, std::string user) const; - bool check_remote_items_exist(const std::vector& file_paths, std::string user) const; - - bool remove_remote_dir(const std::string &dir_path, bool silent, std::string user) const; - - bool run_remote_template_command(const std::string& service_name, const std::string& command, - std::vector args, bool silent, std::map extra_env_vars) const; - - bool 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; - - private: - std::optional construct_standard_template_run_cmd(const std::string& service_name, const std::string& command, const std::vector args, const bool silent) const; - - private: - std::string mServerName; - std::map mVariables; - std::vector mUsers; - bool mValid; -}; - - - -} // namespace dropshell - - -#endif // __SERVER_ENV_HPP \ No newline at end of file diff --git a/source/src/servers.cpp b/source/src/servers.cpp index fc5f28f..50535b9 100644 --- a/source/src/servers.cpp +++ b/source/src/servers.cpp @@ -1,126 +1,444 @@ -#include "servers.hpp" -#include "server_env_manager.hpp" -#include "utils/tableprint.hpp" -#include "utils/envmanager.hpp" #include "utils/directories.hpp" +#include "utils/utils.hpp" +#include "servers.hpp" #include "services.hpp" -#include "config.hpp" #include "templates.hpp" -#include "contrib/transwarp.hpp" -#include "utils/output.hpp" +#include "utils/utils.hpp" +#include "utils/execute.hpp" +#include "output.hpp" +#include "utils/assert.hpp" +#include "config.hpp" #include -#include -#include +#include #include +#include +#include +#include +#include +#include +#include +#include // For potential shell-like expansion if needed -namespace dropshell { +namespace dropshell +{ -std::vector get_configured_servers() { - std::vector servers; + server_config::server_config(const std::string &server_name) : mValid(false), mServerName(server_name) + { + if (server_name.empty()) + return; - std::vector lsdp = gConfig().get_local_server_definition_paths(); - if (lsdp.empty()) - return servers; + // Construct the full path to server.json + std::string server_env_path = localfile::server_json(server_name); - for (auto servers_dir : lsdp) { - if (!servers_dir.empty() && std::filesystem::exists(servers_dir)) { - for (const auto& entry : std::filesystem::directory_iterator(servers_dir)) { - if (std::filesystem::is_directory(entry)) { - std::string server_name = entry.path().filename().string(); + // 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; + } - if (server_name.empty() || server_name[0]=='.' || server_name[0]=='_') - continue; + 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; + } - server_env_manager env(server_name); - if (!env.is_valid()) { - std::cerr << "Error: Invalid server environment file: " << entry.path().string() << std::endl; - continue; + // 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_PORT", "USERS"}) + { + 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)); + } + } + + // Parse users array + if (!server_env_json.contains("USERS") || !server_env_json["USERS"].is_array()) + { + std::cerr << "Error: USERS array not found or invalid in server configuration" << std::endl; + return; + } + + for (const auto &user_json : server_env_json["USERS"]) + { + UserConfig user; + user.user = user_json["USER"].get(); + user.dir = user_json["DIR"].get(); + mUsers.push_back(user); + } + + if (mUsers.empty()) + { + std::cerr << "Error: No users defined in server configuration" << std::endl; + return; + } + + mValid = true; + } + catch (const std::exception &e) + { + std::cerr << "Failed to parse server environment file: " + std::string(e.what()) << std::endl; + } + } + + bool server_config::create_server_json_file(const std::string &server_env_path, const std::string &SSH_HOST, const std::string &SSH_PORT, const std::vector &users) + { + nlohmann::json server_env_json; + server_env_json["SSH_HOST"] = SSH_HOST; + server_env_json["SSH_PORT"] = std::stoi(SSH_PORT); + + // Create users array + nlohmann::json users_array = nlohmann::json::array(); + for (const auto &user : users) + { + nlohmann::json user_json; + user_json["USER"] = user.user; + user_json["DIR"] = user.dir; + users_array.push_back(user_json); + } + server_env_json["USERS"] = users_array; + + 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_config::get_user_dir(const std::string &user) const + { + for (const auto &u : mUsers) + { + if (u.user == user) + { + return u.dir; + } + } + return ""; + } + + std::string server_config::get_user_for_service(const std::string &server, const std::string &service) + { + auto services_info = get_server_services_info(server); + if (std::find_if(services_info.begin(), services_info.end(), + [&service](const LocalServiceInfo &si) + { return si.service_name == service; }) != services_info.end()) + { + // found a service with matching name. + auto it = std::find_if(services_info.begin(), services_info.end(), + [&service](const LocalServiceInfo &si) + { return si.service_name == service; }); + if (it != services_info.end()) + { + return it->user; + } + } + return ""; + } + + sSSHInfo server_config::get_SSH_INFO(std::string user) const + { + ASSERT(!user.empty(), "User is empty, cannot get SSH info."); + // Find user in mUsers vector + auto it = std::find_if(mUsers.begin(), mUsers.end(), + [&user](const UserConfig &u) + { return u.user == user; }); + ASSERT(it != mUsers.end(), ("User " + user + " not found in server environment.")); + return sSSHInfo{get_SSH_HOST(), user, get_SSH_PORT(), get_server_name()}; + } + + bool server_config::check_remote_dir_exists(const std::string &dir_path, std::string user) const + { + sCommand scommand("", "test -d " + quote(dir_path), {}); + return execute_ssh_command(get_SSH_INFO(user), scommand, cMode::Silent); + } + + bool server_config::check_remote_file_exists(const std::string &file_path, std::string user) const + { + sCommand scommand("", "test -f " + quote(file_path), {}); + return execute_ssh_command(get_SSH_INFO(user), scommand, cMode::Silent); + } + + bool server_config::check_remote_items_exist(const std::vector &file_paths, std::string user) 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", {}); + + sSSHInfo sshinfo = get_SSH_INFO(user); + bool okay = execute_ssh_command(sshinfo, 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_config::remove_remote_dir( + const std::string &dir_path, bool silent, std::string user) 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::Defaults); + + sSSHInfo sshinfo = get_SSH_INFO(user); + return execute_ssh_command(sshinfo, scommand, mode); + } + + bool server_config::run_remote_template_command( + const std::string &service_name, + const std::string &command, + std::vector args, + bool silent, + std::map extra_env_vars) const + { + std::string user = get_user_for_service(mServerName, service_name); + auto scommand = construct_standard_template_run_cmd(service_name, command, args, silent); + if (!scommand.has_value()) + return false; + + // 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) : (silent ? cMode::Silent : cMode::Defaults); + return execute_ssh_command(get_SSH_INFO(user), scommand.value(), mode); + } + + bool server_config::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 + { + std::string user = get_user_for_service(mServerName, service_name); + auto scommand = construct_standard_template_run_cmd(service_name, command, args, false); + if (!scommand.has_value()) + return false; + + // add the extra env vars to the command + for (const auto &[key, value] : extra_env_vars) + scommand->add_env_var(key, value); + + return execute_ssh_command(get_SSH_INFO(user), scommand.value(), cMode::Defaults, &output); + } + + std::string server_config::get_variable(const std::string &name) const + { + auto it = mVariables.find(name); + if (it == mVariables.end()) + { + return ""; + } + return it->second; + } + + std::optional server_config::construct_standard_template_run_cmd(const std::string &service_name, const std::string &command, const std::vector args, const bool silent) const + { + if (command.empty()) + return std::nullopt; + + std::string user = get_user_for_service(mServerName, service_name); + + std::string remote_service_template_path = remotepath(mServerName, user).service_template(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 std::nullopt; + } + + std::string argstr = ""; + for (const auto &arg : args) + { + argstr += " " + quote(dequote(trim(arg))); + } + + if (env_vars.find("RUNAS") == env_vars.end()) + { + error << "Error: RUNAS is not set in .template_info.env for the service." << std::endl; + return std::nullopt; + } + std::string runas = env_vars.find("RUNAS")->second; + if (runas != "root" && runas != "user") + { + error << "Error: RUNAS is not set to root or user in .template_info.env for the service." << std::endl; + return std::nullopt; + } + bool run_as_root = runas == "root"; + + if (run_as_root && !get_ALLOW_ROOT_SERVICES()) + { + error << "Error: The service " << service_name << " is set to run as root, but the server environment does not allow root services." << std::endl; + return std::nullopt; + } + + sCommand sc( + remote_service_template_path, + quote(script_path) + argstr + (silent ? " > /dev/null 2>&1" : ""), + env_vars, + run_as_root); + + if (sc.empty()) + { + std::cerr << "Error: Failed to construct command for " << service_name << " " << command << std::endl; + return std::nullopt; + } + return sc; + } + + std::vector get_configured_servers() + { + std::vector servers; + + std::vector lsdp = gConfig().get_local_server_definition_paths(); + if (lsdp.empty()) + return servers; + + for (auto servers_dir : lsdp) + { + if (!servers_dir.empty() && std::filesystem::exists(servers_dir)) + { + for (const auto &entry : std::filesystem::directory_iterator(servers_dir)) + { + if (std::filesystem::is_directory(entry)) + { + std::string server_name = entry.path().filename().string(); + + if (server_name.empty() || server_name[0] == '.' || server_name[0] == '_') + continue; + + server_config env(server_name); + if (!env.is_valid()) + { + std::cerr << "Error: Invalid server environment file: " << entry.path().string() << std::endl; + continue; + } + servers.push_back(env); } - servers.push_back({ - server_name, - env.get_SSH_HOST(), - env.get_SSH_UNPRIVILEGED_USER(), - env.get_SSH_PORT() - }); } } } + + return servers; } - return servers; -} + bool create_server(const std::string &server_name) + { + // 1. check if server name already exists + std::string server_existing_dir = localpath::server(server_name); + if (!server_existing_dir.empty()) + { + error << "Error: Server name already exists: " << server_name << std::endl; + info << "Current server path: " << server_existing_dir << std::endl; + return false; + } -ServerInfo get_server_info(const std::string &server_name) -{ - std::vector lsdp = gConfig().get_local_server_definition_paths(); - if (lsdp.empty()) - return ServerInfo(); + // 2. create a new directory in the user config directory + auto lsdp = gConfig().get_local_server_definition_paths(); + if (lsdp.empty() || lsdp[0].empty()) + { + error << "Error: Local server definition path not found" << std::endl; + info << "Run 'dropshell edit' to configure DropShell" << std::endl; + return false; + } + std::string server_dir = lsdp[0] + "/" + server_name; + std::filesystem::create_directory(server_dir); - for (auto &config_dir : lsdp) { - std::string server_dir = config_dir + "/" + server_name; - if (std::filesystem::exists(server_dir)) { - server_env_manager env(server_name); - if (!env.is_valid()) { - std::cerr << "Error: Invalid server environment file: " << server_dir << std::endl; - continue; - } - return ServerInfo({server_name, env.get_SSH_HOST(), env.get_SSH_UNPRIVILEGED_USER(), env.get_SSH_PORT()}); + // 3. create a template server.env file in the server directory + std::string user = getenv("USER"); + std::string server_env_path = server_dir + "/server.json"; + std::ofstream server_env_file(server_env_path); + server_env_file << "{" << std::endl; + server_env_file << " \"SSH_HOST\": \"" << server_name << "\"," << std::endl; + server_env_file << " \"SSH_UNPRIVILEGED_USER\": \"" << user << "\"," << std::endl; + server_env_file << " \"SSH_PORT\": " << 22 << "," << std::endl; + server_env_file << " \"DROPSHELL_DIR\": \"" << "/home/" + user + "/.dropshell\"" << std::endl; + server_env_file << "}" << std::endl; + server_env_file.close(); + + std::cout << "Server created successfully: " << server_name << std::endl; + std::cout << "Please complete the installation:" << std::endl; + std::cout << "1) edit the server configuration: dropshell edit " << server_name << std::endl; + std::cout << "2) install the server: dropshell install " << server_name << std::endl; + std::cout << std::endl; + return true; + } + + void get_all_used_commands(std::set &commands) + { + std::vector servers = get_configured_servers(); + for (const auto &server : servers) + { + auto services = get_server_services_info(server.get_server_name()); + for (const auto &service : services) + commands.merge(get_used_commands(server.get_server_name(), service.service_name)); } } - return ServerInfo(); -} - - -bool create_server(const std::string &server_name) -{ - // 1. check if server name already exists - std::string server_existing_dir = localpath::server(server_name); - if (!server_existing_dir.empty()) { - error << "Error: Server name already exists: " << server_name << std::endl; - info << "Current server path: " << server_existing_dir << std::endl; - return false; - } - - // 2. create a new directory in the user config directory - auto lsdp = gConfig().get_local_server_definition_paths(); - if (lsdp.empty() || lsdp[0].empty()) { - error << "Error: Local server definition path not found" << std::endl; - info << "Run 'dropshell edit' to configure DropShell" << std::endl; - return false; - } - std::string server_dir = lsdp[0] + "/" + server_name; - std::filesystem::create_directory(server_dir); - - // 3. create a template server.env file in the server directory - std::string user = getenv("USER"); - std::string server_env_path = server_dir + "/server.json"; - std::ofstream server_env_file(server_env_path); - server_env_file << "{" << std::endl; - server_env_file << " \"SSH_HOST\": \"" << server_name << "\"," << std::endl; - server_env_file << " \"SSH_UNPRIVILEGED_USER\": \"" << user << "\"," << std::endl; - server_env_file << " \"SSH_PORT\": " << 22 << "," << std::endl; - server_env_file << " \"DROPSHELL_DIR\": \"" << "/home/"+user+"/.dropshell\"" << std::endl; - server_env_file << "}" << std::endl; - server_env_file.close(); - - std::cout << "Server created successfully: " << server_name << std::endl; - std::cout << "Please complete the installation:" < &commands) -{ - std::vector servers = get_configured_servers(); - for (const auto& server : servers) - { - auto services = dropshell::get_server_services_info(server.name); - for (const auto& service : services) - commands.merge(dropshell::get_used_commands(server.name, service.service_name)); - } -} } // namespace dropshell \ No newline at end of file diff --git a/source/src/servers.hpp b/source/src/servers.hpp index e61c4b1..ad68e72 100644 --- a/source/src/servers.hpp +++ b/source/src/servers.hpp @@ -1,27 +1,95 @@ -#ifndef SERVERS_HPP -#define SERVERS_HPP +// server_env.hpp +// +// read the server.env file and provide a class to access the variables + +#ifndef __SERVER_ENV_HPP +#define __SERVER_ENV_HPP #include -#include +#include +#include #include +#include +#include -namespace dropshell { +#include "utils/execute.hpp" - // Server information structure - struct ServerInfo { - std::string name; - std::string ssh_host; - std::string ssh_port; +namespace dropshell +{ + + struct UserConfig + { + std::string user; + std::string dir; }; - std::vector get_configured_servers(); + // ------------------------------------------------------------------------------------------------ - ServerInfo get_server_info(const std::string& server_name); + // reads path / server.env and provides a class to access the variables. + // each env file is required to have the following variables: + // SSH_HOST + // SSH_UNPRIVILEGED_USER + // SSH_PORT + // the following replacements are made in the values: + // ${USER} -> the username of the user running dropshell + class server_config + { + public: + server_config(const std::string &server_name); + + static bool create_server_json_file( + const std::string &server_env_path, + const std::string &SSH_HOST, + const std::string &SSH_PORT, + const std::vector &users); + + std::string get_variable(const std::string &name) const; + + // trivial getters. + const std::map &get_variables() const { return mVariables; } + std::string get_SSH_HOST() const { return get_variable("SSH_HOST"); } + std::string get_SSH_PORT() const { return get_variable("SSH_PORT"); } + std::string get_SSH_UNPRIVILEGED_USER() const { return get_variable("SSH_UNPRIVILEGED_USER"); } + bool get_ALLOW_ROOT_SERVICES() const { return get_variable("ALLOW_ROOT_SERVICES") == "true"; } + std::vector get_users() const { return mUsers; } + std::string get_user_dir(const std::string &user) const; + bool is_valid() const { return mValid; } + std::string get_server_name() const { return mServerName; } + + static std::string get_user_for_service(const std::string &server, const std::string &service); + + sSSHInfo get_SSH_INFO(std::string user) const; + + // helper functions + public: + bool check_remote_dir_exists(const std::string &dir_path, std::string user) const; + bool check_remote_file_exists(const std::string &file_path, std::string user) const; + bool check_remote_items_exist(const std::vector &file_paths, std::string user) const; + + bool remove_remote_dir(const std::string &dir_path, bool silent, std::string user) const; + + bool run_remote_template_command(const std::string &service_name, const std::string &command, + std::vector args, bool silent, std::map extra_env_vars) const; + + bool 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; + + private: + std::optional construct_standard_template_run_cmd(const std::string &service_name, const std::string &command, const std::vector args, const bool silent) const; + + private: + std::string mServerName; + std::map mVariables; + std::vector mUsers; + bool mValid; + }; // class server_config + + std::vector get_configured_servers(); + + bool create_server(const std::string &server_name); - bool create_server(const std::string& server_name); - void get_all_used_commands(std::set &commands); } // namespace dropshell -#endif // SERVERS_HPP +#endif // __SERVER_ENV_HPP \ No newline at end of file diff --git a/source/src/service_runner.cpp b/source/src/service_runner.cpp index 892544b..a919bab 100644 --- a/source/src/service_runner.cpp +++ b/source/src/service_runner.cpp @@ -10,7 +10,7 @@ // #include "utils/assert.hpp" // #include "config.hpp" -// #include "server_env_manager.hpp" +// #include "servers.hpp" // #include "templates.hpp" // #include "services.hpp" // #include "utils/directories.hpp" @@ -73,7 +73,7 @@ // private: // std::string mServer; -// server_env_manager mServerEnv; +// server_config mServerEnv; // LocalServiceInfo mServiceInfo; // std::string mService; // bool mValid; @@ -240,7 +240,7 @@ // return false; // } -// server_env_manager env(server_name); +// server_config env(server_name); // if (!env.is_valid()) { // std::cerr << "Error: Invalid server environment file: " << server_name << std::endl; // return false; diff --git a/source/src/services.cpp b/source/src/services.cpp index 63a4864..85547ed 100644 --- a/source/src/services.cpp +++ b/source/src/services.cpp @@ -4,7 +4,7 @@ #include "templates.hpp" #include "config.hpp" #include "utils/utils.hpp" -#include "server_env_manager.hpp" +#include "servers.hpp" #include "servers.hpp" #include "assert.hpp" @@ -166,11 +166,11 @@ bool get_all_service_env_vars(const std::string &server_name, const std::string return false; } - ServerInfo server_info = get_server_info(server_name); - if (server_info.ssh_host.empty()) + server_config server_info(server_name); + if (server_info.get_SSH_HOST().empty()) std::cerr << "Error: Server " << server_name << " not found - ssh_host empty, so HOST_NAME not set" << std::endl; - std::string user = server_env_manager::get_user_for_service(server_name, service_name); + std::string user = server_config::get_user_for_service(server_name, service_name); // add in some handy variables. // if we change these, we also need to update agent/_allservicesstatus.sh @@ -178,7 +178,7 @@ bool get_all_service_env_vars(const std::string &server_name, const std::string all_env_vars["SERVER"] = server_name; all_env_vars["SERVICE"] = service_name; all_env_vars["AGENT_PATH"] = remotepath(server_name,user).agent(); - all_env_vars["HOST_NAME"] = server_info.ssh_host; + all_env_vars["HOST_NAME"] = server_info.get_SSH_HOST(); all_env_vars["DOCKER_CLI_HINTS"] = "false"; // turn off docker junk. all_env_vars["SSH_USER"] = user; diff --git a/source/src/templates.hpp b/source/src/templates.hpp index 2397c65..7b2bbb4 100644 --- a/source/src/templates.hpp +++ b/source/src/templates.hpp @@ -4,7 +4,9 @@ #include #include -#include "utils/json.hpp" +#define JSON_INLINE_ALL +#include "json.hpp" + namespace dropshell { diff --git a/source/src/utils/assert.hpp b/source/src/utils/assert.hpp index c32a292..4a08ed5 100644 --- a/source/src/utils/assert.hpp +++ b/source/src/utils/assert.hpp @@ -9,4 +9,5 @@ std::exit(1); \ } + #endif // ASSERT_HPP diff --git a/source/src/utils/directories.cpp b/source/src/utils/directories.cpp index cfc3a19..fc65fab 100644 --- a/source/src/utils/directories.cpp +++ b/source/src/utils/directories.cpp @@ -1,6 +1,6 @@ #include "directories.hpp" #include "config.hpp" -#include "server_env_manager.hpp" +#include "servers.hpp" #include "output.hpp" #include @@ -174,7 +174,7 @@ namespace localpath { std::string remotepath::DROPSHELL_DIR() const { - return server_env_manager(mServer_name).get_user_dir(mUser); + return server_config(mServer_name).get_user_dir(mUser); } std::string remotepath::services() const