Fix list a bit
Some checks failed
Dropshell Test / Build_and_Test (push) Has been cancelled

This commit is contained in:
Your Name 2025-05-23 23:07:26 +12:00
parent b07704612b
commit e1c631dc72
11 changed files with 174 additions and 136 deletions

View File

@ -53,14 +53,10 @@ namespace dropshell
namespace shared_commands 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); ASSERT(server_env.is_valid(), "Invalid server environment for " + server_env.get_server_name());
if (!server_env.is_valid()) std::string server = server_env.get_server_name();
{
error << "Server " << server << " is not valid" << std::endl;
return false;
}
LocalServiceInfo sinfo = get_service_info(server, service); LocalServiceInfo sinfo = get_service_info(server, service);
if (!SIvalid(sinfo)) if (!SIvalid(sinfo))

View File

@ -63,15 +63,13 @@ namespace dropshell
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// install service over ssh : SHARED COMMAND // 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)) if (!SIvalid(service_info))
return false; return false;
server_config server_env(server);
if (!server_env.is_valid())
return false;
maketitle("Installing " + service + " (" + service_info.template_name + ") on " + server); maketitle("Installing " + service + " (" + service_info.template_name + ") on " + server);
@ -259,42 +257,35 @@ namespace dropshell
return 0; return 0;
} }
int install_server(const std::string &server) int install_server(const server_config &server)
{ {
// install the dropshell agent on the given 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); for (const auto &user : server.get_users())
if (!server_env.is_valid())
{ {
error << "Invalid server environment for " << server << std::endl; info << "Installing agent for user " << user.user << " on " << server.get_server_name() << std::endl;
return 1;
}
for (const auto &user : server_env.get_users()) 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);
info << "Installing agent for user " << user.user << " on " << server << std::endl; ASSERT(!agent_path.empty(), "Agent path is empty for " + user.user + "@" + server.get_server_name());
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. // now create the agent.
// copy across from the local agent files. // copy across from the local agent files.
info << "Copying local agent files to remote server... " << std::flush; 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; info << "done." << std::endl;
// run the agent installer. Can't use BB64 yet, as we're installing it on the remote server. // 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) 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; return 1;
} }
info << "Installation on " << server << " complete." << std::endl; info << "Installation on " << server.get_server_name() << " complete." << std::endl;
} }
return 0; return 0;
} }
@ -319,7 +310,7 @@ namespace dropshell
std::vector<server_config> servers = get_configured_servers(); std::vector<server_config> servers = get_configured_servers();
for (const auto &server : servers) for (const auto &server : servers)
{ {
rval = install_server(server.get_server_name()); rval = install_server(server);
if (rval != 0) if (rval != 0)
return rval; return rval;
} }
@ -352,15 +343,24 @@ namespace dropshell
} }
// install service(s) // 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") if (safearg(ctx.args, 1) == "all")
{ {
// install all services on the server // install all services on the server
maketitle("Installing all services on " + server); maketitle("Installing all services on " + server);
bool okay = true; bool okay = true;
std::vector<LocalServiceInfo> services = get_server_services_info(server); std::vector<LocalServiceInfo> 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; okay = false;
} }
return okay ? 0 : 1; return okay ? 0 : 1;
@ -368,7 +368,7 @@ namespace dropshell
else else
{ // install the specific service. { // install the specific service.
std::string service = safearg(ctx.args, 1); 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;
} }
} }

View File

@ -99,7 +99,7 @@ void list_servers() {
int first=true; int first=true;
for (const auto &user : server_env.get_users()) for (const auto &user : server_env.get_users())
{ {
std::map<std::string, shared_commands::ServiceStatus> status = shared_commands::get_all_services_status(server.get_server_name()); std::map<std::string, shared_commands::ServiceStatus> status = shared_commands::get_all_services_status(server.get_server_name(),user.user);
std::set<int> ports_used; std::set<int> ports_used;
std::string serviceticks = ""; std::string serviceticks = "";
@ -136,28 +136,20 @@ void show_server_details(const std::string& server_name) {
//--------------------- //---------------------
// Check if server is reachable via SSH // Check if server is reachable via SSH
std::string ssh_address = env.get_SSH_HOST(); ASSERT(env.get_users().size() > 0, "No users found for server " + server_name);
std::string ssh_user = env.get_SSH_UNPRIVILEGED_USER(); sSSHInfo sshinfo = env.get_SSH_INFO(env.get_users()[0].user);
std::string ssh_port = env.get_SSH_PORT(); ASSERT(sshinfo.valid(), "Invalid SSH info for server " + server_name);
if (!ssh_address.empty()) {
info << std::endl << "Server Status:" << std::endl; info << std::endl << "Server Status:" << std::endl;
info << std::string(40, '-') << std::endl; info << std::string(40, '-') << std::endl;
// Try to connect to the server // Try to connect to the server
std::string cmd = "ssh -o ConnectTimeout=5 " + ssh_user + "@" + ssh_address + " -p " + ssh_port + " 'true' 2>/dev/null"; 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()); int result = system(cmd.c_str());
if (result == 0) { if (result == 0) {
info << "Status: Online" << std::endl; info << "Status: Online" << std::endl;
} else {
// // Get uptime if possible warning << "Status: Offline" << std::endl;
// 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;
}
} }
info << std::endl; info << std::endl;
@ -165,9 +157,21 @@ void show_server_details(const std::string& server_name) {
{ {
std::cout << std::endl; std::cout << std::endl;
tableprint tp("Server Configuration: " + server_name, true); tableprint tp("Server Configuration: " + server_name, true);
tp.add_row({"Key", "Value"}); tp.add_row({"Key", "Value"});
for (const auto& [key, value] : env.get_variables()) { 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(); tp.print();
} }
@ -177,7 +181,6 @@ void show_server_details(const std::string& server_name) {
{ {
tableprint tp("Services: " + server_name, false); tableprint tp("Services: " + server_name, false);
tp.add_row({"Status", "Service", "Template","Ports"}); tp.add_row({"Status", "Service", "Template","Ports"});
std::map<std::string, shared_commands::ServiceStatus> status = shared_commands::get_all_services_status(server_name); std::map<std::string, shared_commands::ServiceStatus> 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 = ""; std::string ports_str = "";
for (const auto& port : service_status.ports) for (const auto& port : service_status.ports)
ports_str += std::to_string(port) + " "; 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) } // end of for (const auto& service : services)
tp.print(); tp.print();
} // end of list services } // end of list services

View File

@ -189,7 +189,8 @@ namespace dropshell
{ // installing fresh service { // installing fresh service
info << "4) Install of fresh service..." << std::endl; 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; return 1;
} }

View File

@ -54,7 +54,7 @@ namespace dropshell
bool rsync_tree_to_remote( bool rsync_tree_to_remote(
const std::string &local_path, const std::string &local_path,
const std::string &remote_path, const std::string &remote_path,
server_config &server_env, const server_config &server_env,
bool silent) bool silent)
{ {
ASSERT(!local_path.empty() && !remote_path.empty(), "Local or remote path not specified. Can't rsync."); 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 // get_all_services_status : SHARED COMMAND
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
std::map<std::string, ServiceStatus> get_all_services_status(std::string server_name) std::map<std::string, ServiceStatus> get_all_services_status(const server_config & server_env)
{ {
std::map<std::string, ServiceStatus> status; std::map<std::string, ServiceStatus> status;
for (const auto& user : server_env.get_users()) {
server_config env(server_name); status.merge(get_all_services_status(server_env, user.user));
if (!env.is_valid())
{
error << "Invalid server environment" << std::endl;
return status;
} }
return status;
}
for (const auto& user : env.get_users()) { std::map<std::string, ServiceStatus> get_all_services_status(const server_config & server_env, std::string user)
std::string output; {
std::string agentpath = remotepath(server_name,user.user).agent(); std::map<std::string, ServiceStatus> status;
if (!execute_ssh_command(env.get_SSH_INFO(user.user), std::string server_name = server_env.get_server_name();
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 output;
std::string line; std::string agentpath = remotepath(server_name,user).agent();
while (std::getline(ss, line)) 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; key = dequote(trim(line.substr(0, pos)));
std::size_t pos = line.find("="); value = dequote(trim(line.substr(pos + 1)));
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] // decode key, it's of format SERVICENAME_[HEALTH|PORTS]
std::string service_name = key.substr(0, key.find_last_of("_")); std::string service_name = key.substr(0, key.find_last_of("_"));
std::string status_type = key.substr(key.find_last_of("_") + 1); std::string status_type = key.substr(key.find_last_of("_") + 1);
if (status_type == "HEALTH") if (status_type == "HEALTH")
{ // healthy|unhealthy|unknown { // healthy|unhealthy|unknown
if (value == "healthy") if (value == "healthy")
status[service_name].health = HealthStatus::HEALTHY; status[service_name].health = HealthStatus::HEALTHY;
else if (value == "unhealthy") else if (value == "unhealthy")
status[service_name].health = HealthStatus::UNHEALTHY; status[service_name].health = HealthStatus::UNHEALTHY;
else if (value == "unknown") else if (value == "unknown")
status[service_name].health = HealthStatus::UNKNOWN; status[service_name].health = HealthStatus::UNKNOWN;
else else
status[service_name].health = HealthStatus::ERROR; status[service_name].health = HealthStatus::ERROR;
} }
else if (status_type == "PORTS") else if (status_type == "PORTS")
{ // port1,port2,port3 { // port1,port2,port3
std::vector<std::string> ports = string2multi(value); std::vector<std::string> ports = string2multi(value);
for (const auto &port : ports) for (const auto &port : ports)
{ {
if (port != "unknown") if (port != "unknown")
status[service_name].ports.push_back(str2int(port)); status[service_name].ports.push_back(str2int(port));
}
} }
} }
} }

View File

@ -43,12 +43,13 @@ namespace dropshell
bool rsync_tree_to_remote( bool rsync_tree_to_remote(
const std::string &local_path, const std::string &local_path,
const std::string &remote_path, const std::string &remote_path,
server_config &server_env, const server_config &server_env,
bool silent); bool silent);
std::string get_arch(); std::string get_arch();
std::map<std::string, ServiceStatus> get_all_services_status(std::string server_name); std::map<std::string, ServiceStatus> get_all_services_status(const server_config & server_env);
std::map<std::string, ServiceStatus> 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 healthtick(const std::string &server, const std::string &service);
std::string HealthStatus2String(HealthStatus status); 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); 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. // 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 // 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 // defined in nuke.cpp
bool nuke_service(const std::string &server, const std::string &service); bool nuke_service(const std::string &server, const std::string &service);
// defined in install.cpp // 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 // defined in create-service.cpp
bool create_service(const std::string &server_name, const std::string &template_name, const std::string &service_name); bool create_service(const std::string &server_name, const std::string &template_name, const std::string &service_name);

View File

@ -42,17 +42,12 @@ namespace dropshell
} uninstall_command_register; } uninstall_command_register;
namespace shared_commands { 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); 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); std::string user = server_env.get_user_for_service(server, service);
// 2. Check if service directory exists on server // 2. Check if service directory exists on server

View File

@ -65,7 +65,7 @@ namespace dropshell
} }
// Verify required variables exist // 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()) if (mVariables.find(var) == mVariables.end())
{ {
@ -80,13 +80,13 @@ namespace dropshell
} }
// Parse users array // 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; return;
} }
for (const auto &user_json : server_env_json["USERS"]) for (const auto &user_json : server_env_json["SSH_USERS"])
{ {
UserConfig user; UserConfig user;
user.user = user_json["USER"].get<std::string>(); user.user = user_json["USER"].get<std::string>();
@ -96,7 +96,7 @@ namespace dropshell
if (mUsers.empty()) 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; return;
} }
@ -181,7 +181,7 @@ namespace dropshell
[&user](const UserConfig &u) [&user](const UserConfig &u)
{ return u.user == user; }); { return u.user == user; });
ASSERT(it != mUsers.end(), ("User " + user + " not found in server environment.")); 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 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 } // namespace dropshell

View File

@ -90,6 +90,8 @@ namespace dropshell
void get_all_used_commands(std::set<std::string> &commands); void get_all_used_commands(std::set<std::string> &commands);
bool server_exists(const std::string &server_name);
} // namespace dropshell } // namespace dropshell
#endif // __SERVER_ENV_HPP #endif // __SERVER_ENV_HPP

View File

@ -165,13 +165,13 @@ namespace dropshell
return false; return false;
std::stringstream ssh_cmd; std::stringstream ssh_cmd;
ssh_cmd << "ssh -p " << ssh_info.port << " " << (hasFlag(mode, cMode::Interactive) ? "-tt " : "") ssh_cmd << "ssh -p " << ssh_info.get_port() << " " << (hasFlag(mode, cMode::Interactive) ? "-tt " : "")
<< ssh_info.user << "@" << ssh_info.host; << ssh_info.get_user() << "@" << ssh_info.get_host();
std::string remote_bb64_path; std::string remote_bb64_path;
if (!hasFlag(mode, cMode::NoBB64)) 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( bool rval = execute_local_command(
"", // local directory to run in "", // local directory to run in
@ -237,4 +237,15 @@ namespace dropshell
return cmdstr; 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 } // namespace dropshell

View File

@ -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;} inline bool hasFlag(cMode mode, cMode flag) {return (mode & flag) == flag;}
typedef struct sSSHInfo { class sSSHInfo {
std::string host; public:
std::string user; sSSHInfo(std::string host, std::string user, std::string port, std::string server_ID, std::string user_dir) :
std::string port; host(host), user(user), port(port), server_ID(server_ID), user_dir(user_dir) {}
std::string server_ID; // dropshell name for server.
} sSSHInfo; 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<std::string, std::string> & env_vars, std::string * output = nullptr, cMode mode = cMode::Defaults); bool execute_local_command(std::string directory_to_run_in, std::string command_to_run, const std::map<std::string, std::string> & 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); bool execute_ssh_command(const sSSHInfo & ssh_info, const sCommand & remote_command, cMode mode = cMode::Defaults, std::string * output = nullptr);