restore partially implemented.

This commit is contained in:
Your Name 2025-04-26 22:35:43 +12:00
parent 71cb39d82e
commit 029187c0aa
7 changed files with 99 additions and 41 deletions

View File

@ -92,7 +92,7 @@ void dropshell::autocomplete_list_commands()
if (dropshell::get_global_config()->is_config_set()) if (dropshell::get_global_config()->is_config_set())
commands.merge(std::set<std::string>{ commands.merge(std::set<std::string>{
"server","templates","create-service","create-template","create-server","edit","ssh", "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) { for (const auto& command : commands) {
std::cout << command << std::endl; std::cout << command << std::endl;

View File

@ -32,7 +32,7 @@ void print_help() {
std::cout << std::endl; std::cout << std::endl;
std::cout << "Service commands: (if no service is specified, all services for the server are affected)" << 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 << " 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 << " 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 << " COMMAND SERVER [SERVICE] Run a command on service(s)." << std::endl;
std::cout << std::endl; std::cout << std::endl;
@ -112,7 +112,7 @@ int main(int argc, char* argv[]) {
std::cout << "["<< dir << "] "; std::cout << "["<< dir << "] ";
std::cout << std::endl; 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) switch (argc)
{ {
case 2: case 2:

View File

@ -16,6 +16,10 @@ namespace dropshell {
namespace main_commands { namespace main_commands {
static const std::string magic_string = "-_-";
int init(const std::vector<std::string> &args) int init(const std::vector<std::string> &args)
{ {
dropshell::config *cfg = dropshell::get_global_config(); dropshell::config *cfg = dropshell::get_global_config();
@ -52,8 +56,8 @@ int init(const std::vector<std::string> &args)
int restore(const std::vector<std::string> &args) int restore(const std::vector<std::string> &args)
{ {
if (args.size() < 3) { if (args.size() < 4) {
std::cerr << "Error: restore command requires a directory argument" << std::endl; std::cerr << "Error: not enough arguments. dropshell restore <server> <service> <backup-file>" << std::endl;
return 1; return 1;
} }
@ -67,30 +71,63 @@ int restore(const std::vector<std::string> &args)
return 1; return 1;
} }
if (! std::filesystem::exists(backup_file)) { std::string local_backups_dir = get_local_backup_path();
std::cerr << "Error: Backup file not found" << std::endl; 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; return 1;
} }
// backup_file is in format: server_name-template_name-service_name-YYYY-MM-DD_HH-MM-SS.tgz
// count '-' in backup_file // split the backup filename into parts based on the magic string
int dash_count = std::count(backup_file.begin(), backup_file.end(), '-'); std::vector<std::string> parts = dropshell::split(backup_file, magic_string);
if (dash_count != 3) { if (parts.size() != 4) {
std::cerr << "Error: Backup file format is incompatible, - in one of the names?" << std::endl; std::cerr << "Error: Backup file format is incompatible, - in one of the names?" << std::endl;
return 1; 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 "<<backup_server_name<<", onto "<<server_name<<std::endl;
std::cout << std::endl;
std::cout << "*** ALL DATA FOR "<<server_name<<"/"<<service_name<<" WILL BE OVERWRITTEN! ***"<<std::endl;
std::cout << std::endl;
std::cout << "Are you sure you want to continue? (y/n)" << std::endl;
char confirm;
std::cin >> 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<std::string> 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;
// run the restore script
// 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;
return 0; return 0;
} }
@ -110,7 +147,12 @@ bool name_breaks_backups(std::string name)
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Backup the service. // Backup the service.
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
int backup(const std::vector<std::string> & args) { int backup(const std::vector<std::string> & 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 server_name = args[2];
std::string service_name = args[3]; std::string service_name = args[3];
@ -126,7 +168,7 @@ int backup(const std::vector<std::string> & args) {
return 1; return 1;
} }
std::string command = "backup"; const std::string command = "backup";
if (!template_command_exists(service_info.template_name, command)) { if (!template_command_exists(service_info.template_name, command)) {
std::cout << "No backup script for " << service_info.template_name << std::endl; std::cout << "No backup script for " << service_info.template_name << std::endl;
@ -135,12 +177,12 @@ int backup(const std::vector<std::string> & args) {
// Check if basic installed stuff is in place. // 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_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_command_script_file = remote_service_template_path + "/" + command + ".sh";
std::string remote_service_env_file = get_remote_service_env_file(server_name, service_name); std::string remote_service_config_path = get_remote_service_config_path(server_name, service_name);
if (!env.check_remote_items_exist({ if (!env.check_remote_items_exist({
get_remote_service_path(server_name, service_name), get_remote_service_path(server_name, service_name),
remote_script_path, remote_command_script_file,
remote_service_env_file}) get_remote_service_env_file(server_name, service_name)})
) )
{ {
std::cerr << "Error: Required service directories not found on remote server" << std::endl; std::cerr << "Error: Required service directories not found on remote server" << std::endl;
@ -150,7 +192,7 @@ int backup(const std::vector<std::string> & args) {
// Create backups directory on server if it doesn't exist // Create backups directory on server if it doesn't exist
std::string remote_backups_dir = get_remote_backups_path(server_name); 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) + "'"; std::string mkdir_cmd = "'mkdir -p " + quote(remote_backups_dir) + "'";
if (!env.execute_ssh_command(mkdir_cmd, "Failed to create backups directory on server")) { if (!env.execute_ssh_command(mkdir_cmd, "Failed to create backups directory on server")) {
return false; return false;
@ -176,33 +218,32 @@ int backup(const std::vector<std::string> & 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;} 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 // 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 remote_backup_file_path = remote_backups_dir + "/" + backup_filename;
std::string local_backup_file_path = (std::filesystem::path(local_backups_dir) / backup_filename).string(); 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 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 // Run backup script
std::string backup_cmd = "'cd " + quote(remote_service_template_path) + std::string backup_cmd = "'cd " + quote(remote_service_template_path) +
" && /bin/bash "+quote(remote_script_path)+" "+quote(remote_service_env_file)+" "+ " && /bin/bash "+quote(remote_command_script_file)+" "+quote(remote_service_config_path)+" "+
quote(remote_backup_file_path)+"'"; quote(remote_backup_file_path)+"'"+ (silent ? " > /dev/null 2>&1" : "");
if (!env.execute_ssh_command(backup_cmd, "Backup script failed")) { if (!env.execute_ssh_command(backup_cmd, "Backup script failed")) {
std::cerr << "Backup script failed: " << backup_cmd << std::endl; std::cerr << "Backup script failed: " << backup_cmd << std::endl;
return false; 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 // Copy backup file from server to local
std::string scp_cmd = "scp -P " + env.get_SSH_PORT() + " " + std::string scp_cmd = "scp -P " + env.get_SSH_PORT() + " " +
env.get_SSH_USER() + "@" + env.get_SSH_HOST() + ":" + 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")) { if (!env.execute_local_command(scp_cmd, "Failed to copy backup file from server")) {
return false; 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; return true;
} }

View File

@ -10,8 +10,8 @@ namespace dropshell {
int init(const std::vector<std::string> &args); int init(const std::vector<std::string> &args);
int restore(const std::vector<std::string> &args); int restore(const std::vector<std::string> &args);
int backup(const std::vector<std::string> &args); int backup(const std::vector<std::string> &args, bool silent=false);
} // namespace main_commands } // namespace main_commands
} // namespace dropshell } // namespace dropshell

View File

@ -247,4 +247,20 @@ int count_substring(const std::string &substring, const std::string &text) {
return positions.size(); return positions.size();
} }
std::vector<std::string> split(const std::string& str, const std::string& delimiter) {
std::vector<std::string> 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 } // namespace dropshell

View File

@ -21,6 +21,7 @@ std::string dequote(std::string str);
std::string quote(std::string str); std::string quote(std::string str);
std::string multi2string(std::vector<std::string> values); std::string multi2string(std::vector<std::string> values);
std::vector<std::string> string2multi(std::string values); std::vector<std::string> string2multi(std::string values);
std::vector<std::string> split(const std::string& str, const std::string& delimiter);
int str2int(const std::string & str); int str2int(const std::string & str);

View File

@ -37,10 +37,10 @@ load_env() {
return 1 return 1
fi fi
env_file="$1/service.env" env_file="$1"
if [ ! -f "$env_file" ]; then 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 return 1
fi fi