From e1c631dc729e42b11093150a54a4594db3731310 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 23 May 2025 23:07:26 +1200 Subject: [PATCH] Fix list a bit --- source/src/commands/backupdata.cpp | 10 +-- source/src/commands/install.cpp | 54 +++++++------- source/src/commands/list.cpp | 59 ++++++++------- source/src/commands/restoredata.cpp | 3 +- source/src/commands/shared_commands.cpp | 95 +++++++++++++------------ source/src/commands/shared_commands.hpp | 11 +-- source/src/commands/uninstall.cpp | 11 +-- source/src/servers.cpp | 23 ++++-- source/src/servers.hpp | 2 + source/src/utils/execute.cpp | 17 ++++- source/src/utils/execute.hpp | 25 +++++-- 11 files changed, 174 insertions(+), 136 deletions(-) diff --git a/source/src/commands/backupdata.cpp b/source/src/commands/backupdata.cpp index 7309eb2..de581f7 100644 --- a/source/src/commands/backupdata.cpp +++ b/source/src/commands/backupdata.cpp @@ -53,14 +53,10 @@ namespace dropshell namespace shared_commands { - bool backupdata_service(const std::string &server, const std::string &service) + bool backupdata_service(const server_config &server_env, const std::string &service) { - server_config server_env(server); - if (!server_env.is_valid()) - { - error << "Server " << server << " is not valid" << std::endl; - return false; - } + ASSERT(server_env.is_valid(), "Invalid server environment for " + server_env.get_server_name()); + std::string server = server_env.get_server_name(); LocalServiceInfo sinfo = get_service_info(server, service); if (!SIvalid(sinfo)) diff --git a/source/src/commands/install.cpp b/source/src/commands/install.cpp index 52b1890..9dc883b 100644 --- a/source/src/commands/install.cpp +++ b/source/src/commands/install.cpp @@ -63,15 +63,13 @@ namespace dropshell // ------------------------------------------------------------------------------------------------ // install service over ssh : SHARED COMMAND // ------------------------------------------------------------------------------------------------ - bool install_service(const std::string &server, const std::string &service) + bool install_service(const server_config &server_env, const std::string &service) { - LocalServiceInfo service_info = get_service_info(server, service); + std::string server = server_env.get_server_name(); + LocalServiceInfo service_info = get_service_info(server_env.get_server_name(), service); if (!SIvalid(service_info)) return false; - server_config server_env(server); - if (!server_env.is_valid()) - return false; maketitle("Installing " + service + " (" + service_info.template_name + ") on " + server); @@ -259,42 +257,35 @@ namespace dropshell return 0; } - int install_server(const std::string &server) + int install_server(const server_config &server) { // install the dropshell agent on the given server. - maketitle("Installing dropshell agent on " + server, sColour::INFO); + maketitle("Installing dropshell agent on " + server.get_server_name(), sColour::INFO); - server_config server_env(server); - if (!server_env.is_valid()) + for (const auto &user : server.get_users()) { - error << "Invalid server environment for " << server << std::endl; - return 1; - } + info << "Installing agent for user " << user.user << " on " << server.get_server_name() << std::endl; - for (const auto &user : server_env.get_users()) - { - info << "Installing agent for user " << user.user << " on " << server << 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); + std::string agent_path = remotepath(server.get_server_name(),user.user).agent(); + ASSERT(agent_path == user.dir, "Agent path does not match user directory for "+user.user+"@" + server.get_server_name() + " : " + agent_path + " != " + user.dir); + ASSERT(!agent_path.empty(), "Agent path is empty for " + user.user + "@" + server.get_server_name()); // 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); + shared_commands::rsync_tree_to_remote(localpath::agent_remote(), agent_path, server, 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); + bool okay = execute_ssh_command(server.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; + error << "ERROR: Failed to install remote agent on " << server.get_server_name() << std::endl; return 1; } - info << "Installation on " << server << " complete." << std::endl; + info << "Installation on " << server.get_server_name() << " complete." << std::endl; } return 0; } @@ -319,7 +310,7 @@ namespace dropshell std::vector servers = get_configured_servers(); for (const auto &server : servers) { - rval = install_server(server.get_server_name()); + rval = install_server(server); if (rval != 0) return rval; } @@ -352,15 +343,24 @@ namespace dropshell } // install service(s) + if (!server_exists(server)) + { + error << "Server " << server << " does not exist." << std::endl; + info << "Create it with: dropshell create-server " << server << std::endl; + return 1; + } + + server_config server_env(server); + ASSERT(server_env.is_valid(), "Invalid server environment for " + server); if (safearg(ctx.args, 1) == "all") { // install all services on the server maketitle("Installing all services on " + server); bool okay = true; std::vector services = get_server_services_info(server); - for (const auto &service : services) + for (const auto &lsi : services) { - if (!shared_commands::install_service(server, service.service_name)) + if (!shared_commands::install_service(server_env, lsi.service_name)) okay = false; } return okay ? 0 : 1; @@ -368,7 +368,7 @@ namespace dropshell else { // install the specific service. std::string service = safearg(ctx.args, 1); - return shared_commands::install_service(server, service) ? 0 : 1; + return shared_commands::install_service(server_env, service) ? 0 : 1; } } diff --git a/source/src/commands/list.cpp b/source/src/commands/list.cpp index f26a32c..6034dea 100644 --- a/source/src/commands/list.cpp +++ b/source/src/commands/list.cpp @@ -99,7 +99,7 @@ void list_servers() { 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::map status = shared_commands::get_all_services_status(server.get_server_name(),user.user); std::set ports_used; std::string serviceticks = ""; @@ -136,28 +136,20 @@ void show_server_details(const std::string& server_name) { //--------------------- // Check if server is reachable via SSH - std::string ssh_address = env.get_SSH_HOST(); - std::string ssh_user = env.get_SSH_UNPRIVILEGED_USER(); - std::string ssh_port = env.get_SSH_PORT(); - if (!ssh_address.empty()) { - info << std::endl << "Server Status:" << std::endl; - info << std::string(40, '-') << std::endl; - - // Try to connect to the server - std::string cmd = "ssh -o ConnectTimeout=5 " + ssh_user + "@" + ssh_address + " -p " + ssh_port + " 'true' 2>/dev/null"; - int result = system(cmd.c_str()); - if (result == 0) { - info << "Status: Online" << std::endl; - - // // Get uptime if possible - // cmd = "ssh " + ssh_address + " 'uptime' 2>/dev/null"; - // int rval = system(cmd.c_str()); - // if (rval != 0) { - // std::cout << "Error: Failed to get uptime" << std::endl; - // } - } else { - warning << "Status: Offline" << std::endl; - } + ASSERT(env.get_users().size() > 0, "No users found for server " + server_name); + sSSHInfo sshinfo = env.get_SSH_INFO(env.get_users()[0].user); + ASSERT(sshinfo.valid(), "Invalid SSH info for server " + server_name); + + info << std::endl << "Server Status:" << std::endl; + info << std::string(40, '-') << std::endl; + + // Try to connect to the server + std::string cmd = "ssh -o ConnectTimeout=5 " + sshinfo.get_user() + "@" + sshinfo.get_host() + " -p " + sshinfo.get_port() + " 'true' 2>/dev/null"; + int result = system(cmd.c_str()); + if (result == 0) { + info << "Status: Online" << std::endl; + } else { + warning << "Status: Offline" << std::endl; } info << std::endl; @@ -165,9 +157,21 @@ void show_server_details(const std::string& server_name) { { std::cout << std::endl; tableprint tp("Server Configuration: " + server_name, true); + tp.add_row({"Key", "Value"}); for (const auto& [key, value] : env.get_variables()) { - tp.add_row({key, value}); + if (key == "SSH_USERS") + { + int i=1; + for (const auto& user : env.get_users()) + { + tp.add_row({"USERS -> USER[" + std::to_string(i) + "]", user.user}); + tp.add_row({"USERS -> DIR[" + std::to_string(i) + "]", user.dir}); + i++; + } + } + else + tp.add_row({key, value}); } tp.print(); } @@ -177,7 +181,6 @@ void show_server_details(const std::string& server_name) { { tableprint tp("Services: " + server_name, false); tp.add_row({"Status", "Service", "Template","Ports"}); - std::map status = shared_commands::get_all_services_status(server_name); @@ -189,8 +192,12 @@ void show_server_details(const std::string& server_name) { std::string ports_str = ""; for (const auto& port : service_status.ports) ports_str += std::to_string(port) + " "; + + std::string template_name = get_service_info(server_name,service_name).template_name; + if (template_name.empty()) + template_name = "Unknown"; - tp.add_row({healthy, service_name, get_service_info(server_name,service_name).template_name, ports_str}); + tp.add_row({healthy, service_name, template_name, ports_str}); } // end of for (const auto& service : services) tp.print(); } // end of list services diff --git a/source/src/commands/restoredata.cpp b/source/src/commands/restoredata.cpp index 220936f..0f3ff52 100644 --- a/source/src/commands/restoredata.cpp +++ b/source/src/commands/restoredata.cpp @@ -189,7 +189,8 @@ namespace dropshell { // installing fresh service info << "4) Install of fresh service..." << std::endl; - if (!shared_commands::install_service(server, service)) + server_config server_env(server); + if (!shared_commands::install_service(server_env, service)) return 1; } diff --git a/source/src/commands/shared_commands.cpp b/source/src/commands/shared_commands.cpp index b10766d..1aff7bd 100644 --- a/source/src/commands/shared_commands.cpp +++ b/source/src/commands/shared_commands.cpp @@ -54,7 +54,7 @@ namespace dropshell bool rsync_tree_to_remote( const std::string &local_path, const std::string &remote_path, - server_config &server_env, + const server_config &server_env, bool silent) { ASSERT(!local_path.empty() && !remote_path.empty(), "Local or remote path not specified. Can't rsync."); @@ -109,60 +109,61 @@ namespace dropshell // ------------------------------------------------------------------------------------------------ // get_all_services_status : SHARED COMMAND // ------------------------------------------------------------------------------------------------ - std::map get_all_services_status(std::string server_name) + std::map get_all_services_status(const server_config & server_env) { std::map status; - - server_config env(server_name); - if (!env.is_valid()) - { - error << "Invalid server environment" << std::endl; - return status; + for (const auto& user : server_env.get_users()) { + status.merge(get_all_services_status(server_env, user.user)); } + return status; + } - for (const auto& user : env.get_users()) { - std::string output; - std::string agentpath = remotepath(server_name,user.user).agent(); - if (!execute_ssh_command(env.get_SSH_INFO(user.user), - sCommand(agentpath, "./_allservicesstatus.sh", {{"HOST_NAME", server_name}, {"SERVER", server_name}, {"AGENT_PATH", agentpath}}), - cMode::Silent, - &output)) - return status; + std::map get_all_services_status(const server_config & server_env, std::string user) + { + std::map status; + std::string server_name = server_env.get_server_name(); - std::stringstream ss(output); - std::string line; - while (std::getline(ss, line)) + std::string output; + std::string agentpath = remotepath(server_name,user).agent(); + if (!execute_ssh_command(server_env.get_SSH_INFO(user), + sCommand(agentpath, "./_allservicesstatus.sh", {{"HOST_NAME", server_name}, {"SERVER", server_name}, {"AGENT_PATH", agentpath}}), + cMode::Silent, + &output)) + return status; + + std::stringstream ss(output); + std::string line; + while (std::getline(ss, line)) + { + std::string key, value; + std::size_t pos = line.find("="); + if (pos != std::string::npos) { - std::string key, value; - std::size_t pos = line.find("="); - if (pos != std::string::npos) - { - key = dequote(trim(line.substr(0, pos))); - value = dequote(trim(line.substr(pos + 1))); + key = dequote(trim(line.substr(0, pos))); + value = dequote(trim(line.substr(pos + 1))); - // decode key, it's of format SERVICENAME_[HEALTH|PORTS] - std::string service_name = key.substr(0, key.find_last_of("_")); - std::string status_type = key.substr(key.find_last_of("_") + 1); + // decode key, it's of format SERVICENAME_[HEALTH|PORTS] + std::string service_name = key.substr(0, key.find_last_of("_")); + std::string status_type = key.substr(key.find_last_of("_") + 1); - if (status_type == "HEALTH") - { // healthy|unhealthy|unknown - if (value == "healthy") - status[service_name].health = HealthStatus::HEALTHY; - else if (value == "unhealthy") - status[service_name].health = HealthStatus::UNHEALTHY; - else if (value == "unknown") - status[service_name].health = HealthStatus::UNKNOWN; - else - status[service_name].health = HealthStatus::ERROR; - } - else if (status_type == "PORTS") - { // port1,port2,port3 - std::vector ports = string2multi(value); - for (const auto &port : ports) - { - if (port != "unknown") - status[service_name].ports.push_back(str2int(port)); - } + if (status_type == "HEALTH") + { // healthy|unhealthy|unknown + if (value == "healthy") + status[service_name].health = HealthStatus::HEALTHY; + else if (value == "unhealthy") + status[service_name].health = HealthStatus::UNHEALTHY; + else if (value == "unknown") + status[service_name].health = HealthStatus::UNKNOWN; + else + status[service_name].health = HealthStatus::ERROR; + } + else if (status_type == "PORTS") + { // port1,port2,port3 + std::vector ports = string2multi(value); + for (const auto &port : ports) + { + if (port != "unknown") + status[service_name].ports.push_back(str2int(port)); } } } diff --git a/source/src/commands/shared_commands.hpp b/source/src/commands/shared_commands.hpp index cf03547..0a39cc8 100644 --- a/source/src/commands/shared_commands.hpp +++ b/source/src/commands/shared_commands.hpp @@ -43,12 +43,13 @@ namespace dropshell bool rsync_tree_to_remote( const std::string &local_path, const std::string &remote_path, - server_config &server_env, + const server_config &server_env, bool silent); std::string get_arch(); - std::map get_all_services_status(std::string server_name); + std::map get_all_services_status(const server_config & server_env); + std::map get_all_services_status(const server_config & server_env, std::string user); std::string healthtick(const std::string &server, const std::string &service); std::string HealthStatus2String(HealthStatus status); @@ -83,16 +84,16 @@ namespace dropshell 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); + bool backupdata_service(const server_config &server_env, const std::string& service); // defined in uninstall.cpp - bool uninstall_service(const std::string &server, const std::string &service); + bool uninstall_service(const server_config &server_env, const std::string &service); // defined in nuke.cpp bool nuke_service(const std::string &server, const std::string &service); // defined in install.cpp - bool install_service(const std::string &server, const std::string &service); + bool install_service(const server_config &server_env, const std::string &service); // defined in create-service.cpp bool create_service(const std::string &server_name, const std::string &template_name, const std::string &service_name); diff --git a/source/src/commands/uninstall.cpp b/source/src/commands/uninstall.cpp index 4138150..b2add40 100644 --- a/source/src/commands/uninstall.cpp +++ b/source/src/commands/uninstall.cpp @@ -42,17 +42,12 @@ namespace dropshell } uninstall_command_register; namespace shared_commands { - bool uninstall_service(const std::string &server, const std::string &service) + bool uninstall_service(const server_config & server_env, const std::string &service) { + ASSERT(server_env.is_valid(), "Invalid server environment for " + server_env.get_server_name()); + std::string server = server_env.get_server_name(); maketitle("Uninstalling " + service + " on " + server); - server_config server_env(server); - if (!server_env.is_valid()) - { - error << "Invalid server: " << server << std::endl; - return false; // should never hit this. - } - std::string user = server_env.get_user_for_service(server, service); // 2. Check if service directory exists on server diff --git a/source/src/servers.cpp b/source/src/servers.cpp index 70c4c79..116c2fa 100644 --- a/source/src/servers.cpp +++ b/source/src/servers.cpp @@ -65,7 +65,7 @@ namespace dropshell } // Verify required variables exist - for (const auto &var : {"SSH_HOST", "SSH_PORT", "USERS"}) + for (const auto &var : {"SSH_HOST", "SSH_PORT", "SSH_USERS"}) { if (mVariables.find(var) == mVariables.end()) { @@ -80,13 +80,13 @@ namespace dropshell } // Parse users array - if (!server_env_json.contains("USERS") || !server_env_json["USERS"].is_array()) + if (!server_env_json.contains("SSH_USERS") || !server_env_json["SSH_USERS"].is_array()) { - error << "USERS array not found or invalid in server configuration" << std::endl; + error << "SSH_USERS array not found or invalid in server configuration" << std::endl; return; } - for (const auto &user_json : server_env_json["USERS"]) + for (const auto &user_json : server_env_json["SSH_USERS"]) { UserConfig user; user.user = user_json["USER"].get(); @@ -96,7 +96,7 @@ namespace dropshell if (mUsers.empty()) { - error << "No users defined in server configuration" << std::endl; + error << "No users defined in server configuration " << server_env_path << std::endl; return; } @@ -181,7 +181,7 @@ namespace dropshell [&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()}; + return sSSHInfo(get_SSH_HOST(), it->user, get_SSH_PORT(), get_server_name(), it->dir); } bool server_config::check_remote_dir_exists(const std::string &dir_path, std::string user) const @@ -449,4 +449,15 @@ namespace dropshell } } + bool server_exists(const std::string &server_name) + { + std::string server_existing_dir = localpath::server(server_name); + if (server_existing_dir.empty()) + return false; + + if (std::filesystem::exists(server_existing_dir)); + return true; + return false; + } + } // namespace dropshell \ No newline at end of file diff --git a/source/src/servers.hpp b/source/src/servers.hpp index ad68e72..3952380 100644 --- a/source/src/servers.hpp +++ b/source/src/servers.hpp @@ -90,6 +90,8 @@ namespace dropshell void get_all_used_commands(std::set &commands); + bool server_exists(const std::string &server_name); + } // namespace dropshell #endif // __SERVER_ENV_HPP \ No newline at end of file diff --git a/source/src/utils/execute.cpp b/source/src/utils/execute.cpp index fd1ac97..6a5e0b4 100644 --- a/source/src/utils/execute.cpp +++ b/source/src/utils/execute.cpp @@ -165,13 +165,13 @@ namespace dropshell return false; std::stringstream ssh_cmd; - ssh_cmd << "ssh -p " << ssh_info.port << " " << (hasFlag(mode, cMode::Interactive) ? "-tt " : "") - << ssh_info.user << "@" << ssh_info.host; + ssh_cmd << "ssh -p " << ssh_info.get_port() << " " << (hasFlag(mode, cMode::Interactive) ? "-tt " : "") + << ssh_info.get_user() << "@" << ssh_info.get_host(); std::string remote_bb64_path; if (!hasFlag(mode, cMode::NoBB64)) - remote_bb64_path = remotepath(ssh_info.server_ID, ssh_info.user).agent() + "/bb64"; + remote_bb64_path = remotepath(ssh_info.get_server_ID(), ssh_info.get_user()).agent() + "/bb64"; bool rval = execute_local_command( "", // local directory to run in @@ -237,4 +237,15 @@ namespace dropshell return cmdstr; } + bool sSSHInfo::valid() const + { + if (host.empty() || user.empty() || port.empty() || server_ID.empty() || user_dir.empty()) + return false; + + if (atoi(port.c_str()) == 0) + return false; + + return true; + } + } // namespace dropshell \ No newline at end of file diff --git a/source/src/utils/execute.hpp b/source/src/utils/execute.hpp index 619f867..f72b9fc 100644 --- a/source/src/utils/execute.hpp +++ b/source/src/utils/execute.hpp @@ -24,12 +24,25 @@ inline cMode operator|=(cMode & lhs, cMode rhs) {return lhs = lhs | rhs;} inline bool hasFlag(cMode mode, cMode flag) {return (mode & flag) == flag;} -typedef struct sSSHInfo { - std::string host; - std::string user; - std::string port; - std::string server_ID; // dropshell name for server. -} sSSHInfo; +class sSSHInfo { + public: + sSSHInfo(std::string host, std::string user, std::string port, std::string server_ID, std::string user_dir) : + host(host), user(user), port(port), server_ID(server_ID), user_dir(user_dir) {} + + std::string get_host() const { return host; } + std::string get_user() const { return user; } + std::string get_port() const { return port; } + std::string get_server_ID() const { return server_ID; } + std::string get_user_dir() const { return user_dir; } + + bool valid() const; + private: + std::string host; + std::string user; + std::string port; + std::string server_ID; // dropshell name for server. + std::string user_dir; // dropshell directory for the user. +}; bool execute_local_command(std::string directory_to_run_in, std::string command_to_run, const std::map & env_vars, std::string * output = nullptr, cMode mode = cMode::Defaults); bool execute_ssh_command(const sSSHInfo & ssh_info, const sCommand & remote_command, cMode mode = cMode::Defaults, std::string * output = nullptr);