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
{
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))

View File

@ -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<server_config> 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<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;
}
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;
}
}

View File

@ -99,7 +99,7 @@ void list_servers() {
int first=true;
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::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;
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);
// 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;
info << std::endl << "Server Status:" << std::endl;
info << std::string(40, '-') << 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;
}
// 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();
}
@ -178,7 +182,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<std::string, shared_commands::ServiceStatus> status = shared_commands::get_all_services_status(server_name);
std::set<int> ports_used;
@ -190,7 +193,11 @@ void show_server_details(const std::string& server_name) {
for (const auto& port : service_status.ports)
ports_str += std::to_string(port) + " ";
tp.add_row({healthy, service_name, get_service_info(server_name,service_name).template_name, ports_str});
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, template_name, ports_str});
} // end of for (const auto& service : services)
tp.print();
} // end of list services

View File

@ -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;
}

View File

@ -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<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;
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<std::string, ServiceStatus> get_all_services_status(const server_config & server_env, std::string user)
{
std::map<std::string, ServiceStatus> 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<std::string> 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<std::string> ports = string2multi(value);
for (const auto &port : ports)
{
if (port != "unknown")
status[service_name].ports.push_back(str2int(port));
}
}
}

View File

@ -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<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 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);

View File

@ -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

View File

@ -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<std::string>();
@ -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

View File

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

View File

@ -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

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;}
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<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);