#include "shared_commands.hpp" #include "utils/assert.hpp" #include "utils/utils.hpp" #include "server_env_manager.hpp" #include "directories.hpp" #include "services.hpp" #include "servers.hpp" namespace dropshell { namespace shared_commands { // ------------------------------------------------------------------------------------------------ // std_autocomplete : SHARED COMMAND // ------------------------------------------------------------------------------------------------ void std_autocomplete(const CommandContext &ctx) { if (ctx.args.size() == 0) { // just the command, no args yet. // list servers std::vector<ServerInfo> servers = get_configured_servers(); for (const auto &server : servers) { std::cout << server.name << std::endl; } } else if (ctx.args.size() == 1) { // list services std::vector<LocalServiceInfo> services = get_server_services_info(ctx.args[0]); for (const auto &service : services) { std::cout << service.service_name << std::endl; } } } // ------------------------------------------------------------------------------------------------ // std_autocomplete_allowall : SHARED COMMAND // ------------------------------------------------------------------------------------------------ void std_autocomplete_allowall(const CommandContext &ctx) { std_autocomplete(ctx); if (ctx.args.size() == 1) std::cout << "all" << std::endl; } // ------------------------------------------------------------------------------------------------ // rsync_tree_to_remote : SHARED COMMAND // ------------------------------------------------------------------------------------------------ bool rsync_tree_to_remote( const std::string &local_path, const std::string &remote_path, server_env_manager &server_env, bool silent) { ASSERT(!local_path.empty() && !remote_path.empty(), "Local or remote path not specified. Can't rsync."); std::string rsync_cmd = "rsync --delete --mkpath -zrpc -e 'ssh -p " + server_env.get_SSH_PORT() + "' " + quote(local_path + "/") + " " + quote(server_env.get_SSH_USER() + "@" + server_env.get_SSH_HOST() + ":" + remote_path + "/"); return execute_local_command(rsync_cmd, nullptr, (silent ? cMode::Silent : cMode::Defaults)); } // ------------------------------------------------------------------------------------------------ // get_arch : SHARED COMMAND // ------------------------------------------------------------------------------------------------ std::string get_arch() { // determine the architecture of the system std::string arch; #ifdef __aarch64__ arch = "arm64"; #elif __x86_64__ arch = "amd64"; #endif return arch; } // ------------------------------------------------------------------------------------------------ // cRemoteTempFolder : SHARED CLASS // ------------------------------------------------------------------------------------------------ cRemoteTempFolder::cRemoteTempFolder(const server_env_manager &server_env) : mServerEnv(server_env) { std::string p = remotepath::temp_files(server_env.get_server_name()) + "/" + random_alphanumeric_string(10); std::string mkdir_cmd = "mkdir -p " + quote(p); if (!execute_ssh_command(server_env.get_SSH_INFO(), sCommand("", mkdir_cmd, {}), cMode::Silent)) std::cerr << "Failed to create temp directory on server" << std::endl; else mPath = p; } cRemoteTempFolder::~cRemoteTempFolder() { std::string rm_cmd = "rm -rf " + quote(mPath); execute_ssh_command(mServerEnv.get_SSH_INFO(), sCommand("", rm_cmd, {}), cMode::Silent); } std::string cRemoteTempFolder::path() const { return mPath; } // ------------------------------------------------------------------------------------------------ // get_all_services_status : SHARED COMMAND // ------------------------------------------------------------------------------------------------ std::map<std::string, ServiceStatus> get_all_services_status(std::string server_name) { std::map<std::string, ServiceStatus> status; server_env_manager env(server_name); if (!env.is_valid()) { std::cerr << "Error: Invalid server environment" << std::endl; return status; } std::string output; if (!execute_ssh_command(env.get_SSH_INFO(), sCommand(remotepath::agent(server_name), "./_allservicesstatus.sh", {{"HOST_NAME", server_name}, {"SERVER", server_name}, {"AGENT_PATH", remotepath::agent(server_name)}}), cMode::CaptureOutput, &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) { 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); 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<std::string> ports = string2multi(value); for (const auto &port : ports) { if (port != "unknown") status[service_name].ports.push_back(str2int(port)); } } } } return status; } // ------------------------------------------------------------------------------------------------ // healthtick : SHARED COMMAND // ------------------------------------------------------------------------------------------------ std::string healthtick(const std::string &server, const std::string &service) { std::string green_tick = "\033[32m✓\033[0m"; std::string red_cross = "\033[31m✗\033[0m"; std::string yellow_exclamation = "\033[33m!\033[0m"; std::string unknown = "\033[37m✓\033[0m"; HealthStatus status = is_healthy(server, service); if (status == HealthStatus::HEALTHY) return green_tick; else if (status == HealthStatus::UNHEALTHY) return red_cross; else if (status == HealthStatus::UNKNOWN) return unknown; else return yellow_exclamation; } // ------------------------------------------------------------------------------------------------ // HealthStatus2String : SHARED COMMAND // ------------------------------------------------------------------------------------------------ std::string HealthStatus2String(HealthStatus status) { if (status == HealthStatus::HEALTHY) return ":tick:"; else if (status == HealthStatus::UNHEALTHY) return ":cross:"; else if (status == HealthStatus::UNKNOWN) return ":greytick:"; else if (status == HealthStatus::NOTINSTALLED) return ":warning:"; else return ":error:"; } // ------------------------------------------------------------------------------------------------ // is_healthy : SHARED COMMAND // ------------------------------------------------------------------------------------------------ HealthStatus is_healthy(const std::string &server, const std::string &service) { server_env_manager env(server); if (!env.is_valid()) { std::cerr << "Error: Server service not initialized" << std::endl; return HealthStatus::ERROR; } if (!env.check_remote_dir_exists(remotepath::service(server, service))) { return HealthStatus::NOTINSTALLED; } std::string script_path = remotepath::service_template(server, service) + "/status.sh"; if (!env.check_remote_file_exists(script_path)) { return HealthStatus::UNKNOWN; } // Run status script, does not display output. if (!env.run_remote_template_command(service, "status", {}, true, {})) return HealthStatus::UNHEALTHY; return HealthStatus::HEALTHY; } // ------------------------------------------------------------------------------------------------ // healthmark : SHARED COMMAND // ------------------------------------------------------------------------------------------------ std::string healthmark(const std::string &server, const std::string &service) { HealthStatus status = is_healthy(server, service); return HealthStatus2String(status); } } // namespace shared_commands } // namespace dropshell