From 029187c0aacf69990cef61171f48c68db24f0e62 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 26 Apr 2025 22:35:43 +1200 Subject: [PATCH] restore partially implemented. --- src/autocomplete.cpp | 2 +- src/main.cpp | 4 +- src/main_commands.cpp | 109 ++++++++++++++++++++++---------- src/main_commands.hpp | 4 +- src/utils/utils.cpp | 16 +++++ src/utils/utils.hpp | 1 + templates/watchtower/_common.sh | 4 +- 7 files changed, 99 insertions(+), 41 deletions(-) diff --git a/src/autocomplete.cpp b/src/autocomplete.cpp index 98d881c..bcfb09e 100644 --- a/src/autocomplete.cpp +++ b/src/autocomplete.cpp @@ -92,7 +92,7 @@ void dropshell::autocomplete_list_commands() if (dropshell::get_global_config()->is_config_set()) commands.merge(std::set{ "server","templates","create-service","create-template","create-server","edit","ssh", - "view" // only if we have a config. + "list" // only if we have a config. }); for (const auto& command : commands) { std::cout << command << std::endl; diff --git a/src/main.cpp b/src/main.cpp index 0b14024..3f31beb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -32,7 +32,7 @@ void print_help() { std::cout << std::endl; std::cout << "Service commands: (if no service is specified, all services for the server are affected)" << std::endl; std::cout << " install SERVER [SERVICE] Install/reinstall/update service(s). Non-destructive." << std::endl; - std::cout << " view [SERVER] [SERVICE] View status/details of all servers/server/service." << std::endl; + std::cout << " list [SERVER] [SERVICE] List status/details of all servers/server/service." << std::endl; std::cout << " edit [SERVER] [SERVICE] Edit the configuration of dropshell/server/service." << std::endl; std::cout << " COMMAND SERVER [SERVICE] Run a command on service(s)." << std::endl; std::cout << std::endl; @@ -112,7 +112,7 @@ int main(int argc, char* argv[]) { std::cout << "["<< dir << "] "; std::cout << std::endl; - if (cmd == "server" || cmd == "servers" || cmd == "view" || cmd == "views" || cmd == "v") + if (cmd == "server" || cmd == "servers" || cmd == "list" || cmd == "view") switch (argc) { case 2: diff --git a/src/main_commands.cpp b/src/main_commands.cpp index de4a310..6133e2e 100644 --- a/src/main_commands.cpp +++ b/src/main_commands.cpp @@ -16,6 +16,10 @@ namespace dropshell { namespace main_commands { + +static const std::string magic_string = "-_-"; + + int init(const std::vector &args) { dropshell::config *cfg = dropshell::get_global_config(); @@ -52,8 +56,8 @@ int init(const std::vector &args) int restore(const std::vector &args) { - if (args.size() < 3) { - std::cerr << "Error: restore command requires a directory argument" << std::endl; + if (args.size() < 4) { + std::cerr << "Error: not enough arguments. dropshell restore " << std::endl; return 1; } @@ -67,30 +71,63 @@ int restore(const std::vector &args) return 1; } - if (! std::filesystem::exists(backup_file)) { - std::cerr << "Error: Backup file not found" << std::endl; + std::string local_backups_dir = get_local_backup_path(); + std::string local_backup_file_path = (std::filesystem::path(local_backups_dir) / backup_file).string(); + + + if (! std::filesystem::exists(local_backup_file_path)) { + std::cerr << "Error: Backup file not found at " << local_backup_file_path << std::endl; return 1; } - // backup_file is in format: server_name-template_name-service_name-YYYY-MM-DD_HH-MM-SS.tgz - // count '-' in backup_file - int dash_count = std::count(backup_file.begin(), backup_file.end(), '-'); - if (dash_count != 3) { + + // split the backup filename into parts based on the magic string + std::vector parts = dropshell::split(backup_file, magic_string); + if (parts.size() != 4) { std::cerr << "Error: Backup file format is incompatible, - in one of the names?" << std::endl; return 1; } + + std::string backup_server_name = parts[0]; + std::string backup_template_name = parts[1]; + std::string backup_service_name = parts[2]; + std::string backup_datetime = parts[3]; + + if (backup_template_name != service_info.template_name) { + std::cerr << "Error: Backup template does not match service template. Can't restore." << std::endl; + return 1; + } + + std::string nicedate = std::string(backup_datetime).substr(0, 10); + + std::cout << "Restoring " << nicedate << " backup of " << backup_template_name << " taken from "<> confirm; + if (confirm != 'y') { + std::cout << "Restore cancelled." << std::endl; + return 1; + } + + // run the restore script + std::cout << "OK, here goes..." << std::endl; + std::cout << "1) Backing up existing service... " << std::flush; + + std::vector backup_args = {"dropshell","backup",server_name, service_name}; + if (!backup(backup_args,true)) // silent=true + { + std::cerr << std::endl; + std::cerr << "Error: Backup failed, restore aborted." << std::endl; + return 1; + } + std::cout << "Backup complete." << std::endl; + + std::cout << "2) Restoring service from backup..." << std::endl; - - // if (service_info.template_name != backup_template_name) { - // std::cerr << "Error: Backup template does not match service template. Can't restore." << std::endl; - // return 1; - // } - - // std::string restore_cmd = "tar -xzvf " + backup_file + " -C " + service_info.path; - // std::string restore_cmd = "tar -xzvf " + backup_file + " -C " + service_info.path; - - - + // run the restore script return 0; } @@ -110,7 +147,12 @@ bool name_breaks_backups(std::string name) // ------------------------------------------------------------------------------------------------ // Backup the service. // ------------------------------------------------------------------------------------------------ -int backup(const std::vector & args) { +int backup(const std::vector & args, bool silent) { + if (args.size() < 4) { + std::cerr << "Error: backup command requires a server name and service name" << std::endl; + return 1; + } + std::string server_name = args[2]; std::string service_name = args[3]; @@ -126,7 +168,7 @@ int backup(const std::vector & args) { return 1; } - std::string command = "backup"; + const std::string command = "backup"; if (!template_command_exists(service_info.template_name, command)) { std::cout << "No backup script for " << service_info.template_name << std::endl; @@ -135,12 +177,12 @@ int backup(const std::vector & args) { // Check if basic installed stuff is in place. std::string remote_service_template_path = get_remote_service_template_path(server_name, service_name); - std::string remote_script_path = remote_service_template_path + "/" + command + ".sh"; - std::string remote_service_env_file = get_remote_service_env_file(server_name, service_name); + std::string remote_command_script_file = remote_service_template_path + "/" + command + ".sh"; + std::string remote_service_config_path = get_remote_service_config_path(server_name, service_name); if (!env.check_remote_items_exist({ get_remote_service_path(server_name, service_name), - remote_script_path, - remote_service_env_file}) + remote_command_script_file, + get_remote_service_env_file(server_name, service_name)}) ) { std::cerr << "Error: Required service directories not found on remote server" << std::endl; @@ -150,7 +192,7 @@ int backup(const std::vector & args) { // Create backups directory on server if it doesn't exist std::string remote_backups_dir = get_remote_backups_path(server_name); - std::cout << "Remote backups directory on "<< server_name <<": " << remote_backups_dir << std::endl; + if (!silent) std::cout << "Remote backups directory on "<< server_name <<": " << remote_backups_dir << std::endl; std::string mkdir_cmd = "'mkdir -p " + quote(remote_backups_dir) + "'"; if (!env.execute_ssh_command(mkdir_cmd, "Failed to create backups directory on server")) { return false; @@ -176,33 +218,32 @@ int backup(const std::vector & args) { if (name_breaks_backups(service_info.template_name)) {std::cerr << "Error: Service template name contains invalid character sequence ( -_- ) that would break backup naming scheme" << std::endl; return 1;} // Construct backup filename - std::string backup_filename = server_name + "-_-" + service_info.template_name + "-_-" + service_name + "-_-" + datetime.str() + ".tgz"; + std::string backup_filename = server_name + magic_string + service_info.template_name + magic_string + service_name + magic_string + datetime.str() + ".tgz"; std::string remote_backup_file_path = remote_backups_dir + "/" + backup_filename; std::string local_backup_file_path = (std::filesystem::path(local_backups_dir) / backup_filename).string(); // assert that the backup filename is valid - -_- appears exactly 3 times in local_backup_file_path. - ASSERT(3 == count_substring("-_-", local_backup_file_path)); + ASSERT(3 == count_substring(magic_string, local_backup_file_path)); // Run backup script std::string backup_cmd = "'cd " + quote(remote_service_template_path) + - " && /bin/bash "+quote(remote_script_path)+" "+quote(remote_service_env_file)+" "+ - quote(remote_backup_file_path)+"'"; + " && /bin/bash "+quote(remote_command_script_file)+" "+quote(remote_service_config_path)+" "+ + quote(remote_backup_file_path)+"'"+ (silent ? " > /dev/null 2>&1" : ""); if (!env.execute_ssh_command(backup_cmd, "Backup script failed")) { std::cerr << "Backup script failed: " << backup_cmd << std::endl; return false; - } std::cout << "Remote backups directory on "<< server_name <<": " << get_remote_backups_path(server_name) << std::endl; - + } // Copy backup file from server to local std::string scp_cmd = "scp -P " + env.get_SSH_PORT() + " " + env.get_SSH_USER() + "@" + env.get_SSH_HOST() + ":" + - quote(remote_backup_file_path) + " " + quote(local_backup_file_path); + quote(remote_backup_file_path) + " " + quote(local_backup_file_path) + (silent ? " > /dev/null 2>&1" : ""); if (!env.execute_local_command(scp_cmd, "Failed to copy backup file from server")) { return false; } - std::cout << "Backup created successfully: " << local_backup_file_path << std::endl; + if (!silent) std::cout << "Backup created successfully: " << local_backup_file_path << std::endl; return true; } diff --git a/src/main_commands.hpp b/src/main_commands.hpp index 9a5994a..b0830ee 100644 --- a/src/main_commands.hpp +++ b/src/main_commands.hpp @@ -10,8 +10,8 @@ namespace dropshell { int init(const std::vector &args); int restore(const std::vector &args); - int backup(const std::vector &args); - } // namespace main_commands + int backup(const std::vector &args, bool silent=false); + } // namespace main_commands } // namespace dropshell diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp index 03c9808..56a95e5 100644 --- a/src/utils/utils.cpp +++ b/src/utils/utils.cpp @@ -247,4 +247,20 @@ int count_substring(const std::string &substring, const std::string &text) { return positions.size(); } +std::vector split(const std::string& str, const std::string& delimiter) { + std::vector tokens; + size_t start = 0; + size_t end = 0; + + while ((end = str.find(delimiter, start)) != std::string::npos) { + tokens.push_back(str.substr(start, end - start)); + start = end + delimiter.length(); + } + + // Add the last token + tokens.push_back(str.substr(start)); + + return tokens; +} + } // namespace dropshell \ No newline at end of file diff --git a/src/utils/utils.hpp b/src/utils/utils.hpp index 00334e2..ad8ba52 100644 --- a/src/utils/utils.hpp +++ b/src/utils/utils.hpp @@ -21,6 +21,7 @@ std::string dequote(std::string str); std::string quote(std::string str); std::string multi2string(std::vector values); std::vector string2multi(std::string values); +std::vector split(const std::string& str, const std::string& delimiter); int str2int(const std::string & str); diff --git a/templates/watchtower/_common.sh b/templates/watchtower/_common.sh index e4e99d5..7961272 100755 --- a/templates/watchtower/_common.sh +++ b/templates/watchtower/_common.sh @@ -37,10 +37,10 @@ load_env() { return 1 fi - env_file="$1/service.env" + env_file="$1" if [ ! -f "$env_file" ]; then - echo "Warning: service.env file not found at $1/service.env" + echo "Warning: service.env file not found at $1" return 1 fi