#include "command_registry.hpp" #include "shared_commands.hpp" #include "config.hpp" #include "services.hpp" #include "servers.hpp" #include "utils/directories.hpp" #include "servers.hpp" #include "templates.hpp" #include "utils/utils.hpp" #include namespace dropshell { int destroy_handler(const CommandContext &ctx); static std::vector destroy_name_list = {"destroy", "nuke", "nuke-service","erase","destroy-service"}; // Static registration struct DestroyCommandRegister { DestroyCommandRegister() { CommandRegistry::instance().register_command({destroy_name_list, destroy_handler, shared_commands::std_autocomplete, false, // hidden true, // requires_config true, // requires_install 2, // min_args (after command) 2, // max_args (after command) "destroy SERVER SERVICE|all", "Destroy a service on a server. Erases everything, both local and remote!", // heredoc R"( Destroy a service. Examples: destroy SERVER SERVICE destroy the given service on the given server. destroy SERVER all destroy all services on the given server. Note: This command is destructive and will destroy all data and all configuration, both on the dropshell host and on the remote server. Use with caution! )"}); } } destroy_command_register; namespace shared_commands { bool destroy_service(const std::string &server, const std::string &service) { ServerConfig server_env(server); // step 1 - destroy on remote server. if (server_env.is_valid()) { std::string user = server_env.get_user_for_service(service); // returns empty string if no user found. if (user.empty()) { warning << "No user found for service " << service << " on " << server << std::endl; for (auto sshuser : server_env.get_users()) { if (server_env.check_remote_dir_exists(remotepath(server, sshuser.user).service(service), sshuser.user)) { info << "Found a remote service directory here: " << remotepath(server, sshuser.user).service(service) << std::endl; info << "Deleting it as user " << sshuser.user << std::endl; user = sshuser.user; break; } } } if (user.empty()) warning << "No remote service directory found for " << service << " on " << server << std::endl; else { // user is not empty. LocalServiceInfo service_info; service_info = get_service_info(server, service); bool service_valid = SIvalid(service_info); if (!service_valid) warning << "No valid service definition found for " << service << std::endl; if (server_env.check_remote_dir_exists(remotepath(server, user).service(service), user)) { // run the destroy script on the remote server if it exists. // otherwise just uninstall. if (service_valid) { if (gTemplateManager().template_command_exists(service_info.template_name, "destroy")) { info << "Running destroy script for " << service << " on " << server << std::endl; if (!server_env.run_remote_template_command(service, "destroy", {}, false, {})) warning << "Failed to run destroy script: " << service << std::endl; } else { info << "No destroy script found for " << service << " on " << server << std::endl; info << "Running uninstall script instead and will clean directories." << std::endl; if (!server_env.run_remote_template_command(service, "uninstall", {}, false, {})) warning << "Failed to uninstall service: " << service << std::endl; } } // Remove the service directory from the server, running in a docker container as root. if (server_env.remove_remote_dir(remotepath(server, user).service(service), true, user)) { ASSERT(!server_env.check_remote_dir_exists(remotepath(server, user).service(service), user), "Service directory still found on server after uninstall"); info << "Remote service directory removed: " << remotepath(server, user).service(service) << std::endl; } else warning << "Failed to remove remote service directory" << std::endl; } else warning << "No remote service directory found for " << service << " on "<< server << std::endl; } // user is not empty. } // server_env is valid. else error << "No valid local server information for server " << server << std::endl; // step 2 - destroy the local service directory, if it exists. std::string local_service_path = localpath::service(server, service); if (local_service_path.empty() || !std::filesystem::exists(local_service_path)) { warning << "No local service directory found for " << service << " on " << server << std::endl; } else { auto itemsdeleted = std::filesystem::remove_all(local_service_path); if (itemsdeleted == 0) error << "Failed to remove local service directory" << std::endl; else info << "Local service directory removed: " << local_service_path << std::endl; } info << "Finished destroying service " << service << " on server " << server << std::endl; return true; } } // namespace shared_commands int destroy_handler(const CommandContext &ctx) { ASSERT(ctx.args.size() == 2, "Usage: destroy SERVER SERVICE|all (requires 2 args - you supplied " + std::to_string(ctx.args.size()) + ")"); 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); if (service == "all") { int rval = 0; // iterate through all service folders in the server directory. std::string server_path = localpath::server(server); if (server_path.empty()) { error << "Server not found: " << server << std::endl; return 1; } for (const auto &entry : std::filesystem::directory_iterator(server_path)) { if (entry.is_directory() && entry.path().filename().string().find(".") != 0) { std::string service_name = entry.path().filename().string(); rval |= (shared_commands::destroy_service(server, service_name) ? 0 : 1); } } return rval; } else { return (shared_commands::destroy_service(server, service) ? 0 : 1); } } } // namespace dropshell