Nuke and uninstall.
This commit is contained in:
parent
a2340dcb80
commit
a27dd7638f
@ -17,6 +17,8 @@
|
||||
namespace dropshell {
|
||||
|
||||
int list_handler(const CommandContext& ctx);
|
||||
void show_server_details(const std::string& server_name);
|
||||
void list_servers();
|
||||
|
||||
static std::vector<std::string> list_name_list={"list","ls","info","-l"};
|
||||
|
||||
@ -53,6 +55,11 @@ int list_handler(const CommandContext& ctx) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ctx.args.size() == 1) {
|
||||
show_server_details(ctx.args[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::cout << "List handler called with " << ctx.args.size() << " args\n";
|
||||
return 0;
|
||||
}
|
||||
@ -101,4 +108,76 @@ void list_servers() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void show_server_details(const std::string& server_name) {
|
||||
server_env_manager env(server_name);
|
||||
if (!env.is_valid()) {
|
||||
std::cerr << "Error: Invalid server environment file: " << server_name << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
//---------------------
|
||||
// Check if server is reachable via SSH
|
||||
std::string ssh_address = env.get_SSH_HOST();
|
||||
std::string ssh_user = env.get_SSH_USER();
|
||||
std::string ssh_port = env.get_SSH_PORT();
|
||||
if (!ssh_address.empty()) {
|
||||
std::cout << std::endl << "Server Status:" << std::endl;
|
||||
std::cout << 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 + " 'echo connected' 2>/dev/null";
|
||||
int result = system(cmd.c_str());
|
||||
if (result == 0) {
|
||||
std::cout << "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 {
|
||||
std::cout << "Status: Offline" << std::endl;
|
||||
}
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
//---------------------
|
||||
{
|
||||
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});
|
||||
}
|
||||
tp.print();
|
||||
}
|
||||
|
||||
//---------------------
|
||||
// list services, and run healthcheck on each
|
||||
{
|
||||
tableprint tp("Services: " + server_name, false);
|
||||
tp.add_row({"Status", "Service", "Ports"});
|
||||
|
||||
|
||||
std::map<std::string, ServiceStatus> status = service_runner::get_all_services_status(server_name);
|
||||
|
||||
std::set<int> ports_used;
|
||||
std::string serviceticks = "";
|
||||
for (const auto& [service_name, service_status] : status) {
|
||||
std::string healthy = service_runner::HealthStatus2String(service_status.health);
|
||||
|
||||
std::string ports_str = "";
|
||||
for (const auto& port : service_status.ports)
|
||||
ports_str += std::to_string(port) + " ";
|
||||
|
||||
tp.add_row({healthy, service_name, ports_str});
|
||||
} // end of for (const auto& service : services)
|
||||
tp.print();
|
||||
} // end of list services
|
||||
} // end of show_server_details
|
||||
|
||||
|
||||
} // namespace dropshell
|
96
src/commands/nuke.cpp
Normal file
96
src/commands/nuke.cpp
Normal file
@ -0,0 +1,96 @@
|
||||
#include "command_registry.hpp"
|
||||
#include "shared_commands.hpp"
|
||||
#include "config.hpp"
|
||||
#include "services.hpp"
|
||||
#include "server_env_manager.hpp"
|
||||
#include "utils/directories.hpp"
|
||||
#include "servers.hpp"
|
||||
#include "templates.hpp"
|
||||
|
||||
#include <libassert/assert.hpp>
|
||||
|
||||
namespace dropshell {
|
||||
|
||||
int nuke_handler(const CommandContext& ctx);
|
||||
static std::vector<std::string> nuke_name_list={"nuke"};
|
||||
|
||||
// Static registration
|
||||
struct NukeCommandRegister {
|
||||
NukeCommandRegister() {
|
||||
CommandRegistry::instance().register_command({
|
||||
nuke_name_list,
|
||||
nuke_handler,
|
||||
std_autocomplete,
|
||||
false, // hidden
|
||||
true, // requires_config
|
||||
2, // min_args (after command)
|
||||
2, // max_args (after command)
|
||||
"nuke SERVER SERVICE",
|
||||
"Nuke a service on a server. Destroys everything, both local and remote!",
|
||||
// heredoc
|
||||
R"(
|
||||
Nuke a service on a server. Destroys everything, both local and remote!
|
||||
nuke <server> <service> nuke the given service on the given server.
|
||||
)"
|
||||
});
|
||||
}
|
||||
} nuke_command_register;
|
||||
|
||||
int nuke_handler(const CommandContext &ctx)
|
||||
{
|
||||
ASSERT(ctx.args.size() > 1, "Usage: nuke <server> <service>");
|
||||
ASSERT(gConfig().is_config_set(), "No configuration found. Please run 'dropshell config' to set up your configuration.");
|
||||
|
||||
std::string server = safearg(ctx.args, 0);
|
||||
std::string service = safearg(ctx.args, 1);
|
||||
|
||||
maketitle("Nuking " + service + " on " + server);
|
||||
|
||||
server_env_manager server_env(server);
|
||||
LocalServiceInfo service_info;
|
||||
|
||||
if (server_env.is_valid())
|
||||
{
|
||||
service_info = get_service_info(server, service);
|
||||
if (!SIvalid(service_info))
|
||||
std::cerr << "Warning: Invalid service: " << service << std::endl;
|
||||
else
|
||||
if (!uninstall_service(server, service, false))
|
||||
std::cerr << "Warning: Failed to uninstall service: " << service << std::endl;
|
||||
|
||||
// run the nuke script on the remote server if it exists.
|
||||
if (gTemplateManager().template_command_exists(service_info.template_name, "nuke"))
|
||||
{
|
||||
if (!server_env.run_remote_template_command(service, "nuke", {}, false, {}))
|
||||
std::cerr << "Warning: Failed to run nuke script: " << service << std::endl;
|
||||
}
|
||||
|
||||
// remove the remote service directory, running in a docker container as root.
|
||||
std::string rm_cmd = "docker run --rm -v " + quote(remotepath::service(server, service)) + ":/service alpine rm -rf /service";
|
||||
if (!execute_ssh_command(server_env.get_SSH_INFO(), sCommand(rm_cmd), cMode::Silent))
|
||||
{
|
||||
std::cerr << "Warning: Failed to remove remote service directory" << std::endl;
|
||||
std::cerr << "You may need to log in and delete it manually." << std::endl;
|
||||
std::cerr << " ssh " << server << std::endl;
|
||||
std::cerr << " rm -rf " << remotepath::service(server, service) << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
std::cerr << "Warning: Invalid server: " << server << std::endl;
|
||||
|
||||
// remove the local service directory
|
||||
std::string local_service_path = service_info.local_service_path;
|
||||
if (local_service_path.empty() || !std::filesystem::exists(local_service_path))
|
||||
{
|
||||
std::cerr << "Warning: Local service directory not found: " << local_service_path << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto ret = std::filesystem::remove_all(local_service_path);
|
||||
if (ret != 0)
|
||||
std::cerr << "Warning: Failed to remove local service directory" << std::endl;
|
||||
|
||||
return ret == 0 ? 0 : 1;
|
||||
}
|
||||
|
||||
} // namespace dropshell
|
@ -15,6 +15,7 @@ namespace dropshell {
|
||||
|
||||
// defined in install.cpp
|
||||
bool install_service(const std::string& server, const std::string& service, bool silent);
|
||||
bool uninstall_service(const std::string& server, const std::string& service, bool silent);
|
||||
|
||||
// defined in health.cpp
|
||||
std::string healthtick(const std::string& server, const std::string& service);
|
||||
|
109
src/commands/uninstall.cpp
Normal file
109
src/commands/uninstall.cpp
Normal file
@ -0,0 +1,109 @@
|
||||
#include "command_registry.hpp"
|
||||
#include "directories.hpp"
|
||||
#include "shared_commands.hpp"
|
||||
|
||||
namespace dropshell
|
||||
{
|
||||
|
||||
int uninstall_handler(const CommandContext &ctx);
|
||||
|
||||
static std::vector<std::string> uninstall_name_list = {"uninstall", "remove"};
|
||||
|
||||
// Static registration
|
||||
struct UninstallCommandRegister
|
||||
{
|
||||
UninstallCommandRegister()
|
||||
{
|
||||
CommandRegistry::instance().register_command({uninstall_name_list,
|
||||
uninstall_handler,
|
||||
std_autocomplete,
|
||||
false, // hidden
|
||||
false, // requires_config
|
||||
1, // min_args (after command)
|
||||
2, // max_args (after command)
|
||||
"uninstall SERVER [SERVICE]",
|
||||
"Uninstall a service on a server. Does not remove configuration or user data.",
|
||||
// heredoc
|
||||
R"(
|
||||
Uninstall a service on a server. Does not remove configuration or user data.
|
||||
uninstall <server> <service> uninstall the given service on the given server.
|
||||
uninstall <server> uninstall all services on the given server.
|
||||
)"});
|
||||
}
|
||||
} uninstall_command_register;
|
||||
|
||||
int uninstall_handler(const CommandContext &ctx)
|
||||
{
|
||||
if (ctx.args.size() < 1)
|
||||
{
|
||||
std::cerr << "Error: uninstall requires a server and (optionally) a service" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string server = safearg(ctx.args, 0);
|
||||
|
||||
if (ctx.args.size() == 1)
|
||||
{
|
||||
// uninstall all services on the server
|
||||
bool okay = true;
|
||||
std::vector<LocalServiceInfo> services = get_server_services_info(server);
|
||||
for (const auto &service : services)
|
||||
{
|
||||
if (!uninstall_service(server, service.service_name, false))
|
||||
okay = false;
|
||||
}
|
||||
return okay ? 0 : 1;
|
||||
}
|
||||
|
||||
std::string service = safearg(ctx.args, 1);
|
||||
return uninstall_service(server, service, false) ? 0 : 1;
|
||||
}
|
||||
|
||||
bool uninstall_service(const std::string &server, const std::string &service, bool silent)
|
||||
{
|
||||
if (!silent)
|
||||
maketitle("Uninstalling " + service + " on " + server);
|
||||
|
||||
server_env_manager server_env(server);
|
||||
if (!server_env.is_valid())
|
||||
{
|
||||
std::cerr << "Invalid server: " << server << std::endl;
|
||||
return false; // should never hit this.
|
||||
}
|
||||
|
||||
// 2. Check if service directory exists on server
|
||||
if (!server_env.check_remote_dir_exists(remotepath::service(server, service)))
|
||||
{
|
||||
std::cerr << "Service is not installed: " << service << std::endl;
|
||||
return true; // Nothing to uninstall
|
||||
}
|
||||
|
||||
// 3. Run uninstall script if it exists
|
||||
std::string uninstall_script = remotepath::service_template(server, service) + "/uninstall.sh";
|
||||
bool script_exists = server_env.check_remote_file_exists(uninstall_script);
|
||||
|
||||
if (script_exists)
|
||||
{
|
||||
if (!server_env.run_remote_template_command(service, "uninstall", {}, silent, {}))
|
||||
if (!silent)
|
||||
std::cerr << "Warning: Uninstall script failed, but continuing with directory removal" << std::endl;
|
||||
}
|
||||
else
|
||||
if (!silent)
|
||||
std::cerr << "Warning: No uninstall script found, continuing with direcotry removal." << std::endl;
|
||||
|
||||
// 4. Remove the service directory from the server
|
||||
std::string rm_cmd = "rm -rf " + quote(remotepath::service(server, service));
|
||||
if (!execute_ssh_command(server_env.get_SSH_INFO(), sCommand(rm_cmd), cMode::Silent))
|
||||
{
|
||||
if (!silent)
|
||||
std::cerr << "Failed to remove service directory" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!silent)
|
||||
std::cout << "Service " << service << " successfully uninstalled from " << server << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace dropshell
|
24
src/main.cpp
24
src/main.cpp
@ -158,14 +158,6 @@ int old_main(int argc, char* argv[]) {
|
||||
|
||||
std::string cmd = argv[1];
|
||||
|
||||
if (cmd == "autocomplete")
|
||||
{
|
||||
std::vector<std::string> argvec;
|
||||
for (int i = 0; i < argc; i++)
|
||||
argvec.push_back(argv[i]);
|
||||
return autocomplete::autocomplete(argvec) ? 0 : 1;
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// from here we require the config file to be loaded.
|
||||
@ -183,22 +175,6 @@ int old_main(int argc, char* argv[]) {
|
||||
if (gTemplateManager().is_loaded() && gTemplateManager().get_source_count() > 0)
|
||||
gTemplateManager().print_sources();
|
||||
|
||||
if (cmd == "server" || cmd == "servers" || cmd == "list" || cmd == "view")
|
||||
switch (argc)
|
||||
{
|
||||
case 2:
|
||||
list_servers();
|
||||
return 0;
|
||||
case 3:
|
||||
show_server_details(argv[2]);
|
||||
return 0;
|
||||
case 4:
|
||||
cmd="logs";
|
||||
break;
|
||||
default:
|
||||
return die("dropshell server: too many arguments");
|
||||
}
|
||||
|
||||
HAPPYEXIT("templates", gTemplateManager().list_templates());
|
||||
|
||||
if (cmd == "create-template") {
|
||||
|
@ -72,75 +72,6 @@ ServerInfo get_server_info(const std::string &server_name)
|
||||
}
|
||||
|
||||
|
||||
void show_server_details(const std::string& server_name) {
|
||||
server_env_manager env(server_name);
|
||||
if (!env.is_valid()) {
|
||||
std::cerr << "Error: Invalid server environment file: " << server_name << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
//---------------------
|
||||
// Check if server is reachable via SSH
|
||||
std::string ssh_address = env.get_SSH_HOST();
|
||||
std::string ssh_user = env.get_SSH_USER();
|
||||
std::string ssh_port = env.get_SSH_PORT();
|
||||
if (!ssh_address.empty()) {
|
||||
std::cout << std::endl << "Server Status:" << std::endl;
|
||||
std::cout << 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 + " 'echo connected' 2>/dev/null";
|
||||
int result = system(cmd.c_str());
|
||||
if (result == 0) {
|
||||
std::cout << "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 {
|
||||
std::cout << "Status: Offline" << std::endl;
|
||||
}
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
//---------------------
|
||||
{
|
||||
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});
|
||||
}
|
||||
tp.print();
|
||||
}
|
||||
|
||||
//---------------------
|
||||
// list services, and run healthcheck on each
|
||||
{
|
||||
tableprint tp("Services: " + server_name, false);
|
||||
tp.add_row({"Status", "Service", "Ports"});
|
||||
|
||||
|
||||
std::map<std::string, ServiceStatus> status = service_runner::get_all_services_status(server_name);
|
||||
|
||||
std::set<int> ports_used;
|
||||
std::string serviceticks = "";
|
||||
for (const auto& [service_name, service_status] : status) {
|
||||
std::string healthy = service_runner::HealthStatus2String(service_status.health);
|
||||
|
||||
std::string ports_str = "";
|
||||
for (const auto& port : service_status.ports)
|
||||
ports_str += std::to_string(port) + " ";
|
||||
|
||||
tp.add_row({healthy, service_name, ports_str});
|
||||
} // end of for (const auto& service : services)
|
||||
tp.print();
|
||||
} // end of list services
|
||||
} // end of show_server_details
|
||||
|
||||
bool create_server(const std::string &server_name)
|
||||
{
|
||||
// 1. check if server name already exists
|
||||
|
@ -20,10 +20,6 @@ namespace dropshell {
|
||||
|
||||
ServerInfo get_server_info(const std::string& server_name);
|
||||
|
||||
|
||||
void list_servers();
|
||||
void show_server_details(const std::string& server_name);
|
||||
|
||||
bool create_server(const std::string& server_name);
|
||||
|
||||
void get_all_used_commands(std::set<std::string> &commands);
|
||||
|
Loading…
x
Reference in New Issue
Block a user