Refactoring backups.
This commit is contained in:
parent
b07e3830de
commit
e033489f9b
16
src/main.cpp
16
src/main.cpp
@ -181,6 +181,22 @@ int main(int argc, char* argv[]) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cmd == "backup" || cmd=="backups") {
|
||||||
|
if (argc < 4) {
|
||||||
|
std::cerr << "Error: backup requires a target server and target service to back up" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return dropshell::main_commands::backup(argvec);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd == "restore") {
|
||||||
|
if (argc < 4) {
|
||||||
|
std::cerr << "Error: restore requires a target server, target service the backup file to restore" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return dropshell::main_commands::restore(argvec);
|
||||||
|
}
|
||||||
|
|
||||||
// handle running a command.
|
// handle running a command.
|
||||||
std::set<std::string> commands;
|
std::set<std::string> commands;
|
||||||
dropshell::get_all_used_commands(commands);
|
dropshell::get_all_used_commands(commands);
|
||||||
|
@ -3,8 +3,12 @@
|
|||||||
#include "utils/directories.hpp"
|
#include "utils/directories.hpp"
|
||||||
#include "utils/utils.hpp"
|
#include "utils/utils.hpp"
|
||||||
#include "utils/readmes.hpp"
|
#include "utils/readmes.hpp"
|
||||||
|
#include "service_runner.hpp"
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
|
#include "templates.hpp"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
namespace dropshell {
|
namespace dropshell {
|
||||||
|
|
||||||
@ -44,6 +48,158 @@ int init(const std::vector<std::string> &args)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int restore(const std::vector<std::string> &args)
|
||||||
|
{
|
||||||
|
if (args.size() < 3) {
|
||||||
|
std::cerr << "Error: restore command requires a directory argument" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string server_name = args[2];
|
||||||
|
std::string service_name = args[3];
|
||||||
|
std::string backup_file = args[4];
|
||||||
|
|
||||||
|
|
||||||
|
ServiceInfo service_info = get_service_info(server_name, service_name);
|
||||||
|
if (service_info.path.empty()) {
|
||||||
|
std::cerr << "Error: Service not found" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! std::filesystem::exists(backup_file)) {
|
||||||
|
std::cerr << "Error: Backup file not found" << 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) {
|
||||||
|
std::cerr << "Error: Backup file format is incompatible, - in one of the names?" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool name_breaks_backups(std::string name)
|
||||||
|
{
|
||||||
|
// if name contains -_-, return true
|
||||||
|
return name.find("-_-") != std::string::npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// backup the service over ssh, using the credentials from server.env (via server_env.hpp)
|
||||||
|
// 1. run backup.sh on the server
|
||||||
|
// 2. create a backup file with format server-service-datetime.tgz
|
||||||
|
// 3. store it in the server's DROPSHELL_DIR/backups folder
|
||||||
|
// 4. copy it to the local user_dir/backups folder
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Backup the service.
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
int backup(const std::vector<std::string> & args) {
|
||||||
|
std::string server_name = args[2];
|
||||||
|
std::string service_name = args[3];
|
||||||
|
|
||||||
|
service_runner runner;
|
||||||
|
if (!runner.init(server_name, service_name))
|
||||||
|
{
|
||||||
|
std::cerr << "Error: Failed to initialise service runner" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
server_env env(server_name);
|
||||||
|
if (!env.is_valid()) {
|
||||||
|
std::cerr << "Error: Invalid server environment" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string command = "backup";
|
||||||
|
|
||||||
|
std::string script_path = runner.sr_get_local_service_template_path() + "/" + command + ".sh";
|
||||||
|
if (!template_command_exists(runner.sr_get_service_template_name(), command)) {
|
||||||
|
std::cout << "No backup script for " << runner.sr_get_service_template_name() << std::endl;
|
||||||
|
return true; // nothing to back up.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if basic installed stuff is in place.
|
||||||
|
if (!runner.check_remote_items_exist({runner.sr_get_remote_service_path(),script_path,runner.sr_get_remote_service_env_file()}))
|
||||||
|
{
|
||||||
|
std::cerr << "Error: Required service directories not found on remote server" << std::endl;
|
||||||
|
std::cerr << "Is the service installed?" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
std::string mkdir_cmd = "'mkdir -p " + quote(remote_backups_dir) + "'";
|
||||||
|
if (!runner.execute_ssh_command(mkdir_cmd, "Failed to create backups directory on server")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create backups directory locally if it doesn't exist
|
||||||
|
std::string local_backups_dir = get_local_backup_path();
|
||||||
|
if (local_backups_dir.empty()) {
|
||||||
|
std::cerr << "Error: Local backups directory not found - is DropShell initialised?" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!std::filesystem::exists(local_backups_dir))
|
||||||
|
std::filesystem::create_directories(local_backups_dir);
|
||||||
|
|
||||||
|
// Get current datetime for backup filename
|
||||||
|
auto now = std::chrono::system_clock::now();
|
||||||
|
auto time = std::chrono::system_clock::to_time_t(now);
|
||||||
|
std::stringstream datetime;
|
||||||
|
datetime << std::put_time(std::localtime(&time), "%Y-%m-%d_%H-%M-%S");
|
||||||
|
|
||||||
|
if (name_breaks_backups(server_name)) {std::cerr << "Error: Server name contains invalid character sequence ( -_- ) that would break backup naming scheme" << std::endl; return 1;}
|
||||||
|
if (name_breaks_backups(service_name)) {std::cerr << "Error: Service name contains invalid character sequence ( -_- ) that would break backup naming scheme" << std::endl; return 1;}
|
||||||
|
if (name_breaks_backups(runner.sr_get_service_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 + "-_-" + runner.sr_get_service_template_name() + "-_-" + service_name + "-_-" + 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));
|
||||||
|
|
||||||
|
// Run backup script
|
||||||
|
std::string backup_cmd = "'cd " + quote(runner.sr_get_remote_service_template_path()) +
|
||||||
|
" && /bin/bash "+quote(script_path)+" "+quote(runner.sr_get_remote_service_config_path())+" "+
|
||||||
|
quote(remote_backup_file_path)+"'";
|
||||||
|
|
||||||
|
if (!runner.execute_ssh_command(backup_cmd, "Backup script failed")) {
|
||||||
|
std::cerr << "Backup script failed: " << backup_cmd << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy backup file from server to local
|
||||||
|
std::string scp_cmd = "scp -P " + runner.sr_get_server_env().get_SSH_PORT() + " " +
|
||||||
|
runner.sr_get_server_env().get_SSH_USER() + "@" + runner.sr_get_server_env().get_SSH_HOST() + ":" +
|
||||||
|
quote(remote_backup_file_path) + " " + quote(local_backup_file_path);
|
||||||
|
if (!runner.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;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace main_commands
|
} // namespace main_commands
|
||||||
|
|
||||||
} // namespace dropshell
|
} // namespace dropshell
|
||||||
|
@ -9,7 +9,8 @@ namespace dropshell {
|
|||||||
namespace main_commands {
|
namespace main_commands {
|
||||||
|
|
||||||
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 backup(const std::vector<std::string> &args);
|
||||||
} // namespace main_commands
|
} // namespace main_commands
|
||||||
|
|
||||||
} // namespace dropshell
|
} // namespace dropshell
|
||||||
|
@ -279,8 +279,6 @@ bool service_runner::run_command(const std::string& command) {
|
|||||||
|
|
||||||
if (command == "uninstall")
|
if (command == "uninstall")
|
||||||
return uninstall();
|
return uninstall();
|
||||||
if (command == "backup")
|
|
||||||
return backup();
|
|
||||||
if (command == "ssh") {
|
if (command == "ssh") {
|
||||||
interactive_ssh_service();
|
interactive_ssh_service();
|
||||||
return true;
|
return true;
|
||||||
@ -292,81 +290,6 @@ bool service_runner::run_command(const std::string& command) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
|
||||||
// Backup the service.
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
|
||||||
bool service_runner::backup() {
|
|
||||||
maketitle("Backing up " + m_service_info.service_name + " (" + m_service_info.template_name + ") on " + m_server_name);
|
|
||||||
|
|
||||||
if (!m_server_env) return false; // should never hit this.
|
|
||||||
|
|
||||||
std::string command = "backup";
|
|
||||||
|
|
||||||
std::string script_path = mRemote_service_template_path + "/" + command + ".sh";
|
|
||||||
if (!template_command_exists(m_service_info.template_name, command)) {
|
|
||||||
std::cout << "No backup script for " << m_service_info.template_name << std::endl;
|
|
||||||
return true; // nothing to back up.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if basic installed stuff is in place.
|
|
||||||
if (!check_remote_items_exist({mRemote_service_path,script_path,mRemote_service_env_file}))
|
|
||||||
{
|
|
||||||
std::cerr << "Error: Required service directories not found on remote server" << std::endl;
|
|
||||||
std::cerr << "Is the service installed?" << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create backups directory on server if it doesn't exist
|
|
||||||
std::string server_backups_dir = m_server_env->get_DROPSHELL_DIR() + "/backups";
|
|
||||||
std::string mkdir_cmd = "'mkdir -p " + quote(server_backups_dir) + "'";
|
|
||||||
if (!execute_ssh_command(mkdir_cmd, "Failed to create backups directory on server")) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create backups directory locally if it doesn't exist
|
|
||||||
std::string local_backups_dir = get_local_backup_path();
|
|
||||||
if (local_backups_dir.empty()) {
|
|
||||||
std::cerr << "Error: Local backups directory not found - is DropShell initialised?" << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!fs::exists(local_backups_dir)) {
|
|
||||||
fs::create_directories(local_backups_dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get current datetime for backup filename
|
|
||||||
auto now = std::chrono::system_clock::now();
|
|
||||||
auto time = std::chrono::system_clock::to_time_t(now);
|
|
||||||
std::stringstream datetime;
|
|
||||||
datetime << std::put_time(std::localtime(&time), "%Y-%m-%d_%H-%M-%S");
|
|
||||||
|
|
||||||
// Construct backup filename
|
|
||||||
std::string backup_filename = m_server_name + "-" + m_service_info.service_name + "-" + datetime.str() + ".tgz";
|
|
||||||
std::string server_backup_path = server_backups_dir + "/" + backup_filename;
|
|
||||||
std::string local_backup_path = (fs::path(local_backups_dir) / backup_filename).string();
|
|
||||||
|
|
||||||
// Run backup script
|
|
||||||
std::string backup_cmd = "'cd " + quote(mRemote_service_template_path) +
|
|
||||||
" && /bin/bash "+quote(script_path)+" "+quote(mRemote_service_config_path)+" "+
|
|
||||||
quote(server_backup_path)+"'";
|
|
||||||
|
|
||||||
if (!execute_ssh_command(backup_cmd, "Backup script failed")) {
|
|
||||||
std::cerr << "Backup script failed: " << backup_cmd << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy backup file from server to local
|
|
||||||
std::string scp_cmd = "scp -P " + m_server_env->get_SSH_PORT() + " " +
|
|
||||||
m_server_env->get_SSH_USER() + "@" + m_server_env->get_SSH_HOST() + ":" +
|
|
||||||
quote(server_backup_path) + " " + quote(local_backup_path);
|
|
||||||
if (!execute_local_command(scp_cmd, "Failed to copy backup file from server")) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "Backup created successfully: " << local_backup_path << std::endl;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::map<std::string, ServiceStatus> service_runner::get_all_services_status(std::string server_name)
|
std::map<std::string, ServiceStatus> service_runner::get_all_services_status(std::string server_name)
|
||||||
{
|
{
|
||||||
std::map<std::string, ServiceStatus> status;
|
std::map<std::string, ServiceStatus> status;
|
||||||
|
@ -11,6 +11,8 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include "server_env.hpp"
|
#include "server_env.hpp"
|
||||||
#include "services.hpp"
|
#include "services.hpp"
|
||||||
|
#include "utils/utils.hpp"
|
||||||
|
#include "utils/assert.hpp"
|
||||||
|
|
||||||
namespace dropshell {
|
namespace dropshell {
|
||||||
|
|
||||||
@ -73,13 +75,6 @@ class service_runner {
|
|||||||
// 4. remove the service directory from the server
|
// 4. remove the service directory from the server
|
||||||
bool uninstall();
|
bool uninstall();
|
||||||
|
|
||||||
// backup the service over ssh, using the credentials from server.env (via server_env.hpp)
|
|
||||||
// 1. run backup.sh on the server
|
|
||||||
// 2. create a backup file with format server-service-datetime.tgz
|
|
||||||
// 3. store it in the server's DROPSHELL_DIR/backups folder
|
|
||||||
// 4. copy it to the local user_dir/backups folder
|
|
||||||
bool backup();
|
|
||||||
|
|
||||||
// restore the service over ssh, using the credentials from server.env (via server_env.hpp)
|
// restore the service over ssh, using the credentials from server.env (via server_env.hpp)
|
||||||
// 1. copy the backup file to the server's DROPSHELL_DIR/backups folder
|
// 1. copy the backup file to the server's DROPSHELL_DIR/backups folder
|
||||||
// 2. run the restore.sh script on the server, passing the {service_name}.env file as an argument
|
// 2. run the restore.sh script on the server, passing the {service_name}.env file as an argument
|
||||||
@ -102,7 +97,23 @@ class service_runner {
|
|||||||
std::string mRemote_service_template_path;
|
std::string mRemote_service_template_path;
|
||||||
std::string mRemote_service_env_file;
|
std::string mRemote_service_env_file;
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::string sr_get_server_name() const { return m_server_name; }
|
||||||
|
std::string sr_get_service_name() const { return m_service_info.service_name; }
|
||||||
|
std::string sr_get_service_path() const { return m_service_info.path; }
|
||||||
|
std::string sr_get_service_template_name() const { return m_service_info.template_name; }
|
||||||
|
std::string sr_get_local_service_template_path() const { return m_service_info.template_local_path; }
|
||||||
|
|
||||||
|
ServiceInfo sr_get_service_info() const { return m_service_info; }
|
||||||
|
const server_env& sr_get_server_env() const { ASSERT(m_server_env); return *m_server_env; }
|
||||||
|
|
||||||
|
const std::string& sr_get_remote_service_path() const { return mRemote_service_path; }
|
||||||
|
const std::string& sr_get_remote_service_config_path() const { return mRemote_service_config_path; }
|
||||||
|
const std::string& sr_get_remote_service_template_path() const { return mRemote_service_template_path; }
|
||||||
|
const std::string& sr_get_remote_service_env_file() const { return mRemote_service_env_file; }
|
||||||
|
|
||||||
// Helper methods
|
// Helper methods
|
||||||
|
public:
|
||||||
static std::string construct_ssh_cmd(const server_env &env);
|
static std::string construct_ssh_cmd(const server_env &env);
|
||||||
|
|
||||||
std::string construct_ssh_cmd() const;
|
std::string construct_ssh_cmd() const;
|
||||||
|
@ -83,7 +83,7 @@ ServiceInfo get_service_info(const std::string &server_name, const std::string &
|
|||||||
}
|
}
|
||||||
|
|
||||||
// find the template path
|
// find the template path
|
||||||
service.template_path = tinfo.path;
|
service.template_local_path = tinfo.path;
|
||||||
|
|
||||||
return service;
|
return service;
|
||||||
}
|
}
|
||||||
@ -96,14 +96,14 @@ std::set<std::string> get_used_commands(const std::string &server_name, const st
|
|||||||
return commands;
|
return commands;
|
||||||
|
|
||||||
ServiceInfo service = get_service_info(server_name, service_name);
|
ServiceInfo service = get_service_info(server_name, service_name);
|
||||||
if (service.template_path.empty()) {
|
if (service.template_local_path.empty()) {
|
||||||
std::cerr << "Error: Service not found: " << service_name << std::endl;
|
std::cerr << "Error: Service not found: " << service_name << std::endl;
|
||||||
return commands;
|
return commands;
|
||||||
}
|
}
|
||||||
|
|
||||||
// iterate over all files in the template path, and add the command name to the set.
|
// iterate over all files in the template path, and add the command name to the set.
|
||||||
// commands are .sh files that don't begin with _
|
// commands are .sh files that don't begin with _
|
||||||
fs::path template_path = fs::path(service.template_path);
|
fs::path template_path = fs::path(service.template_local_path);
|
||||||
for (const auto& entry : fs::directory_iterator(template_path)) {
|
for (const auto& entry : fs::directory_iterator(template_path)) {
|
||||||
if (fs::is_regular_file(entry) && entry.path().extension() == ".sh" && (entry.path().filename().string().rfind("_", 0) != 0))
|
if (fs::is_regular_file(entry) && entry.path().extension() == ".sh" && (entry.path().filename().string().rfind("_", 0) != 0))
|
||||||
commands.insert(entry.path().stem().string());
|
commands.insert(entry.path().stem().string());
|
||||||
|
@ -11,7 +11,7 @@ namespace dropshell {
|
|||||||
std::string path;
|
std::string path;
|
||||||
std::string service_name;
|
std::string service_name;
|
||||||
std::string template_name;
|
std::string template_name;
|
||||||
std::string template_path;
|
std::string template_local_path;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<ServiceInfo> get_server_services_info(const std::string& server_name);
|
std::vector<ServiceInfo> get_server_services_info(const std::string& server_name);
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
#include "server_env.hpp"
|
#include "server_env.hpp"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <boost/filesystem.hpp>
|
#include <filesystem>
|
||||||
|
|
||||||
namespace fs = boost::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
namespace dropshell {
|
namespace dropshell {
|
||||||
|
|
||||||
@ -187,14 +187,14 @@ std::string get_local_service_env_path(const std::string &server_name, const std
|
|||||||
return (fs::path(service_path) / "template").string();
|
return (fs::path(service_path) / "template").string();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string get_remote_service_backups_path(const std::string &server_name, const std::string &service_name)
|
std::string get_remote_backups_path(const std::string &server_name)
|
||||||
{
|
{
|
||||||
if (server_name.empty() || service_name.empty())
|
if (server_name.empty())
|
||||||
return std::string();
|
return std::string();
|
||||||
std::string service_path = get_remote_service_path(server_name, service_name);
|
std::string dropshell_path = get_remote_DROPSHELL_path(server_name);
|
||||||
if (service_path.empty())
|
if (dropshell_path.empty())
|
||||||
return std::string();
|
return std::string();
|
||||||
return (fs::path(service_path) / "backups").string();
|
return (fs::path(dropshell_path) / "backups").string();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string get_remote_service_env_file(const std::string &server_name, const std::string &service_name)
|
std::string get_remote_service_env_file(const std::string &server_name, const std::string &service_name)
|
||||||
|
@ -57,7 +57,7 @@ namespace dropshell {
|
|||||||
std::string get_remote_service_path(const std::string &server_name, const std::string &service_name);
|
std::string get_remote_service_path(const std::string &server_name, const std::string &service_name);
|
||||||
std::string get_remote_service_config_path(const std::string &server_name, const std::string &service_name);
|
std::string get_remote_service_config_path(const std::string &server_name, const std::string &service_name);
|
||||||
std::string get_remote_service_template_path(const std::string &server_name, const std::string &service_name);
|
std::string get_remote_service_template_path(const std::string &server_name, const std::string &service_name);
|
||||||
std::string get_remote_service_backups_path(const std::string &server_name, const std::string &service_name);
|
std::string get_remote_backups_path(const std::string &server_name);
|
||||||
|
|
||||||
std::string get_remote_service_env_file(const std::string &server_name, const std::string &service_name);
|
std::string get_remote_service_env_file(const std::string &server_name, const std::string &service_name);
|
||||||
} // namespace dropshell
|
} // namespace dropshell
|
||||||
|
@ -157,4 +157,94 @@ void ensure_directories_exist(std::vector<std::string> directories)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//https://www.geeksforgeeks.org/kmp-algorithm-for-pattern-searching/
|
||||||
|
void constructLps(const std::string &pat, std::vector<int> &lps) {
|
||||||
|
|
||||||
|
// len stores the length of longest prefix which
|
||||||
|
// is also a suffix for the previous index
|
||||||
|
int len = 0;
|
||||||
|
|
||||||
|
// lps[0] is always 0
|
||||||
|
lps[0] = 0;
|
||||||
|
|
||||||
|
int i = 1;
|
||||||
|
while (i < pat.length()) {
|
||||||
|
|
||||||
|
// If characters match, increment the size of lps
|
||||||
|
if (pat[i] == pat[len]) {
|
||||||
|
len++;
|
||||||
|
lps[i] = len;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is a mismatch
|
||||||
|
else {
|
||||||
|
if (len != 0) {
|
||||||
|
|
||||||
|
// Update len to the previous lps value
|
||||||
|
// to avoid reduntant comparisons
|
||||||
|
len = lps[len - 1];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
// If no matching prefix found, set lps[i] to 0
|
||||||
|
lps[i] = 0;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> search(const std::string &pat, const std::string &txt) {
|
||||||
|
int n = txt.length();
|
||||||
|
int m = pat.length();
|
||||||
|
|
||||||
|
std::vector<int> lps(m);
|
||||||
|
std::vector<int> res;
|
||||||
|
|
||||||
|
constructLps(pat, lps);
|
||||||
|
|
||||||
|
// Pointers i and j, for traversing
|
||||||
|
// the text and pattern
|
||||||
|
int i = 0;
|
||||||
|
int j = 0;
|
||||||
|
|
||||||
|
while (i < n) {
|
||||||
|
|
||||||
|
// If characters match, move both pointers forward
|
||||||
|
if (txt[i] == pat[j]) {
|
||||||
|
i++;
|
||||||
|
j++;
|
||||||
|
|
||||||
|
// If the entire pattern is matched
|
||||||
|
// store the start index in result
|
||||||
|
if (j == m) {
|
||||||
|
res.push_back(i - j);
|
||||||
|
|
||||||
|
// Use LPS of previous index to
|
||||||
|
// skip unnecessary comparisons
|
||||||
|
j = lps[j - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is a mismatch
|
||||||
|
else {
|
||||||
|
|
||||||
|
// Use lps value of previous index
|
||||||
|
// to avoid redundant comparisons
|
||||||
|
if (j != 0)
|
||||||
|
j = lps[j - 1];
|
||||||
|
else
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count_substring(const std::string &substring, const std::string &text) {
|
||||||
|
std::vector<int> positions = search(substring, text);
|
||||||
|
return positions.size();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace dropshell
|
} // namespace dropshell
|
@ -28,4 +28,8 @@ void recursive_copy(const std::string & source, const std::string & destination)
|
|||||||
|
|
||||||
void ensure_directories_exist(std::vector<std::string> directories);
|
void ensure_directories_exist(std::vector<std::string> directories);
|
||||||
|
|
||||||
|
// KMP algorithm
|
||||||
|
std::vector<int> search(const std::string &pat, const std::string &txt);
|
||||||
|
int count_substring(const std::string &substring, const std::string &text);
|
||||||
|
|
||||||
} // namespace dropshell
|
} // namespace dropshell
|
Loading…
x
Reference in New Issue
Block a user