Integrate in agent.
This commit is contained in:
parent
513431b7a3
commit
9409adf4f6
@ -6,6 +6,7 @@
|
||||
#include "utils/directories.hpp"
|
||||
#include "services.hpp"
|
||||
#include "config.hpp"
|
||||
#include "templates.hpp"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
@ -58,28 +59,19 @@ void list_servers() {
|
||||
tp.add_row({"Name", "Address", "Health", "Ports"});
|
||||
|
||||
std::for_each(std::execution::par, servers.begin(), servers.end(), [&](const ServerInfo& server) {
|
||||
std::vector<int> ports_used;
|
||||
std::string serviceticks = "";
|
||||
std::vector<ServiceInfo> services = get_server_services_info(server.name);
|
||||
|
||||
std::for_each(std::execution::par, services.begin(), services.end(), [&](const ServiceInfo& service) {
|
||||
service_runner ss;
|
||||
if (ss.init(server.name, service.service_name))
|
||||
serviceticks += ss.healthmark() + " ";
|
||||
else std::cout<<"Error: Failed to initialise service runner for server: ["<<server.name<<"] and service: ["<<service.service_name<<"]"<<std::endl;
|
||||
std::vector<int> ports = ss.get_ports();
|
||||
ports_used.insert(ports_used.end(), ports.begin(), ports.end());
|
||||
});
|
||||
// convert ports_used to string
|
||||
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) {
|
||||
ports_used.insert(service_status.ports.begin(), service_status.ports.end());
|
||||
serviceticks += service_runner::HealthStatus2String(service_status.health) + " ";
|
||||
}
|
||||
std::string ports_used_str = "";
|
||||
bool first = true;
|
||||
for (const auto& port : ports_used) {
|
||||
if (!first) {
|
||||
ports_used_str += ", ";
|
||||
}
|
||||
ports_used_str += std::to_string(port);
|
||||
first = false;
|
||||
}
|
||||
for (const auto& port : ports_used)
|
||||
ports_used_str += std::to_string(port) + " ";
|
||||
|
||||
tp.add_row({server.name, server.ssh_host, serviceticks, ports_used_str});
|
||||
});
|
||||
tp.print();
|
||||
@ -161,5 +153,4 @@ void show_server_details(const std::string& server_name) {
|
||||
} // end of list services
|
||||
} // end of show_server_details
|
||||
|
||||
|
||||
} // namespace dropshell
|
@ -4,6 +4,8 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "service_runner.hpp" // for ServiceStatus
|
||||
|
||||
namespace dropshell {
|
||||
|
||||
// Server information structure
|
||||
|
@ -49,15 +49,23 @@ bool service_runner::init(const std::string& server_name, const std::string& ser
|
||||
}
|
||||
|
||||
// Helper method implementations
|
||||
std::string service_runner::construct_ssh_cmd() const {
|
||||
std::string service_runner::construct_ssh_cmd(const server_env &env) {
|
||||
std::stringstream ssh_cmd;
|
||||
ssh_cmd << "ssh -p " << m_server_env->get_SSH_PORT() << " "
|
||||
<< m_server_env->get_SSH_USER() << "@" << m_server_env->get_SSH_HOST() << " ";
|
||||
ssh_cmd << "ssh -p " << env.get_SSH_PORT() << " "
|
||||
<< env.get_SSH_USER() << "@" << env.get_SSH_HOST() << " ";
|
||||
return ssh_cmd.str();
|
||||
}
|
||||
|
||||
bool service_runner::check_remote_dir_exists(const std::string& dir_path) const {
|
||||
std::string check_dir_cmd = construct_ssh_cmd() + "'test -d " + dir_path + "'";
|
||||
std::string service_runner::construct_ssh_cmd() const
|
||||
{
|
||||
if (!m_server_env)
|
||||
return std::string();
|
||||
return construct_ssh_cmd(*m_server_env);
|
||||
}
|
||||
|
||||
bool service_runner::check_remote_dir_exists(const std::string &dir_path) const
|
||||
{
|
||||
std::string check_dir_cmd = construct_ssh_cmd() + "'test -d " + quote(dir_path) + "'";
|
||||
if (system(check_dir_cmd.c_str()) != 0) {
|
||||
std::cerr << "Error: Directory not found on remote server:" << fs::path(dir_path).filename().string() << std::endl;
|
||||
return false;
|
||||
@ -66,7 +74,7 @@ bool service_runner::check_remote_dir_exists(const std::string& dir_path) const
|
||||
}
|
||||
|
||||
bool service_runner::check_remote_file_exists(const std::string& file_path) const {
|
||||
std::string check_cmd = construct_ssh_cmd() + "'test -f " + file_path + "'";
|
||||
std::string check_cmd = construct_ssh_cmd() + "'test -f " + quote(file_path) + "'";
|
||||
if (system(check_cmd.c_str()) != 0) {
|
||||
std::cerr << "Error: File not found on remote server: " << fs::path(file_path).filename().string() << std::endl;
|
||||
return false;
|
||||
@ -80,7 +88,7 @@ bool service_runner::check_remote_items_exist(const std::vector<std::string> &fi
|
||||
std::string file_paths_str;
|
||||
std::string file_names_str;
|
||||
for (const auto& file_path : file_paths) {
|
||||
file_paths_str += file_path + " ";
|
||||
file_paths_str += quote(file_path) + " ";
|
||||
file_names_str += fs::path(file_path).filename().string() + " ";
|
||||
}
|
||||
// check if all items in the vector exist on the remote server, in a single command.
|
||||
@ -104,6 +112,22 @@ bool service_runner::execute_local_command(const std::string& command, const std
|
||||
return okay;
|
||||
}
|
||||
|
||||
bool service_runner::execute_local_command_and_capture_output(const std::string &command, std::string &output)
|
||||
{
|
||||
std::string full_cmd = command + " 2>&1";
|
||||
FILE *pipe = popen(full_cmd.c_str(), "r");
|
||||
if (!pipe) {
|
||||
std::cerr << "Error: Failed to execute command: " << command << std::endl;
|
||||
return false;
|
||||
}
|
||||
char buffer[128];
|
||||
while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
|
||||
output += buffer;
|
||||
}
|
||||
pclose(pipe);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool service_runner::install() {
|
||||
maketitle("Installing " + m_service_info.service_name + " (" + m_service_info.template_name + ") on " + m_server_name);
|
||||
|
||||
@ -115,7 +139,7 @@ bool service_runner::install() {
|
||||
return false;
|
||||
|
||||
// Create service directory
|
||||
std::string mkdir_cmd = "'mkdir -p " + mRemote_service_path + "'";
|
||||
std::string mkdir_cmd = "'mkdir -p " + quote(mRemote_service_path) + "'";
|
||||
if (!execute_ssh_command(mkdir_cmd, "Failed to create service directory"))
|
||||
return false;
|
||||
|
||||
@ -128,9 +152,9 @@ bool service_runner::install() {
|
||||
{
|
||||
std::cout << "Copying template files from " << tinfo.path << " to " << mRemote_service_template_path << "/" << std::endl;
|
||||
std::string rsync_cmd = "rsync --delete -zrpc -e 'ssh -p " + m_server_env->get_SSH_PORT() + "' " +
|
||||
tinfo.path + "/ " +
|
||||
quote(tinfo.path + "/ ") +
|
||||
m_server_env->get_SSH_USER() + "@" + m_server_env->get_SSH_HOST() + ":" +
|
||||
mRemote_service_template_path + "/";
|
||||
quote(mRemote_service_template_path+"/");
|
||||
execute_local_command(rsync_cmd,"Failed to copy template files");
|
||||
}
|
||||
|
||||
@ -143,16 +167,16 @@ bool service_runner::install() {
|
||||
}
|
||||
std::cout << "Copying service files from " << local_service_path << " to " << mRemote_service_config_path << std::endl;
|
||||
std::string rsync_cmd = "rsync --delete -zrpc -e 'ssh -p " + m_server_env->get_SSH_PORT() + "' " +
|
||||
local_service_path + "/ " +
|
||||
quote(local_service_path + "/ ") +
|
||||
m_server_env->get_SSH_USER() + "@" + m_server_env->get_SSH_HOST() + ":" +
|
||||
mRemote_service_config_path + "/";
|
||||
quote(mRemote_service_config_path + "/");
|
||||
execute_local_command(rsync_cmd,"Failed to copy service files");
|
||||
}
|
||||
|
||||
// Run install script
|
||||
{
|
||||
std::string install_cmd = "'cd " + mRemote_service_template_path +
|
||||
" && /bin/bash install.sh " + mRemote_service_config_path + "'";
|
||||
std::string install_cmd = "'cd " + quote(mRemote_service_template_path) +
|
||||
" && /bin/bash install.sh " + quote(mRemote_service_config_path) + "'";
|
||||
bool ok= execute_ssh_command(install_cmd, "Failed to run install script");
|
||||
if (!ok)
|
||||
return false;
|
||||
@ -179,8 +203,8 @@ bool service_runner::uninstall() {
|
||||
bool script_exists = check_remote_file_exists(uninstall_script);
|
||||
|
||||
if (script_exists) {
|
||||
std::string uninstall_cmd = "'cd " + mRemote_service_template_path +
|
||||
" && /bin/bash _uninstall.sh " + mRemote_service_config_path + "'";
|
||||
std::string uninstall_cmd = "'cd " + quote(mRemote_service_template_path) +
|
||||
" && /bin/bash "+quote(uninstall_script)+" "+quote(mRemote_service_config_path)+"'";
|
||||
if (!execute_ssh_command(uninstall_cmd, "Failed to run uninstall script")) {
|
||||
std::cerr << "Warning: Uninstall script failed, but continuing with directory removal" << std::endl;
|
||||
}
|
||||
@ -190,7 +214,7 @@ bool service_runner::uninstall() {
|
||||
}
|
||||
|
||||
// 4. Remove the service directory from the server
|
||||
std::string rm_cmd = "'rm -rf " + mRemote_service_path + "'";
|
||||
std::string rm_cmd = "'rm -rf " + quote(mRemote_service_path) + "'";
|
||||
if (!execute_ssh_command(rm_cmd, "Failed to remove service directory")) {
|
||||
return false;
|
||||
}
|
||||
@ -241,8 +265,8 @@ bool service_runner::run_command(const std::string& command) {
|
||||
return backup();
|
||||
|
||||
// Run the generic command
|
||||
std::string run_cmd = "'cd " + mRemote_service_template_path +
|
||||
" && /bin/bash " + script_path + " " + mRemote_service_config_path + "'";
|
||||
std::string run_cmd = "'cd " + quote(mRemote_service_template_path) +
|
||||
" && /bin/bash "+quote(script_path)+" "+quote(mRemote_service_config_path)+"'";
|
||||
return execute_ssh_command(run_cmd, "Command returned error code: " + script_path);
|
||||
}
|
||||
|
||||
@ -269,7 +293,7 @@ bool service_runner::backup() {
|
||||
|
||||
// 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 " + server_backups_dir + "'";
|
||||
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;
|
||||
}
|
||||
@ -296,8 +320,8 @@ bool service_runner::backup() {
|
||||
std::string local_backup_path = (fs::path(local_backups_dir) / backup_filename).string();
|
||||
|
||||
// Run backup script
|
||||
std::string backup_cmd = "'cd " + mRemote_service_template_path +
|
||||
" && /bin/bash \""+script_path+"\" " + mRemote_service_env_file + " " + server_backup_path + "'";
|
||||
std::string backup_cmd = "'cd " + quote(mRemote_service_template_path) +
|
||||
" && /bin/bash "+quote(script_path)+" "+quote(mRemote_service_env_file)+" "+quote(server_backup_path)+"'";
|
||||
if (!execute_ssh_command(backup_cmd, "Backup script failed")) {
|
||||
return false;
|
||||
}
|
||||
@ -305,7 +329,7 @@ bool service_runner::backup() {
|
||||
// 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() + ":" +
|
||||
server_backup_path + " " + local_backup_path;
|
||||
quote(server_backup_path) + " " + quote(local_backup_path);
|
||||
if (!execute_local_command(scp_cmd, "Failed to copy backup file from server")) {
|
||||
return false;
|
||||
}
|
||||
@ -314,7 +338,73 @@ bool service_runner::backup() {
|
||||
return true;
|
||||
}
|
||||
|
||||
service_runner::HealthStatus service_runner::is_healthy()
|
||||
std::map<std::string, ServiceStatus> service_runner::get_all_services_status(std::string server_name)
|
||||
{
|
||||
std::map<std::string, ServiceStatus> status;
|
||||
|
||||
std::string command = "_allservicesstatus.sh";
|
||||
std::string service_name = "dropshell-agent";
|
||||
|
||||
if (!template_command_exists(service_name, command))
|
||||
{
|
||||
std::cerr << "Error: " << service_name << " does not contain the _allservicesstatus.sh script" << std::endl;
|
||||
return status;
|
||||
}
|
||||
|
||||
std::string remote_service_path = get_remote_service_path(server_name, service_name);
|
||||
std::string remote_service_config_path = get_remote_service_config_path(server_name, service_name);
|
||||
std::string remote_service_template_path = get_remote_service_template_path(server_name, service_name);
|
||||
std::string remote_service_env_file = get_remote_service_env_file(server_name, service_name);
|
||||
|
||||
|
||||
std::string script_path = remote_service_template_path + "/" + command;
|
||||
|
||||
server_env env(server_name);
|
||||
if (!env.is_valid()) {
|
||||
std::cerr << "Error: Invalid server environment" << std::endl;
|
||||
return status;
|
||||
}
|
||||
|
||||
std::string ssh_cmd = construct_ssh_cmd(env) + "'cd " + quote(remote_service_template_path) +
|
||||
" && /bin/bash "+quote(script_path)+" "+quote(remote_service_config_path)+"'";
|
||||
|
||||
std::string output;
|
||||
if (!execute_local_command_and_capture_output(ssh_cmd, output))
|
||||
return status;
|
||||
|
||||
std::stringstream ss(output);
|
||||
std::string line;
|
||||
while (std::getline(ss, line)) {
|
||||
std::string key, value;
|
||||
std::size_t pos = line.find("=");
|
||||
if (pos != std::string::npos) {
|
||||
key = dequote(trim(line.substr(0, pos)));
|
||||
value = dequote(trim(line.substr(pos + 1)));
|
||||
|
||||
// decode key, it's of format SERVICENAME_[HEALTH|PORTS]
|
||||
std::string service_name = key.substr(0, key.find_last_of("_"));
|
||||
std::string status_type = key.substr(key.find_last_of("_") + 1);
|
||||
|
||||
if (status_type == "HEALTH") { // healthy|unhealthy|unknown
|
||||
if (value == "healthy")
|
||||
status[service_name].health = HealthStatus::HEALTHY;
|
||||
else if (value == "unhealthy")
|
||||
status[service_name].health = HealthStatus::UNHEALTHY;
|
||||
else if (value == "unknown")
|
||||
status[service_name].health = HealthStatus::UNKNOWN;
|
||||
else
|
||||
status[service_name].health = HealthStatus::ERROR;
|
||||
} else if (status_type == "PORTS") { // port1,port2,port3
|
||||
std::vector<std::string> ports = string2multi(value);
|
||||
for (const auto& port : ports)
|
||||
status[service_name].ports.push_back(std::stoi(port));
|
||||
}
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
HealthStatus service_runner::is_healthy()
|
||||
{
|
||||
if (!m_server_env) {
|
||||
std::cerr << "Error: Server service not initialized" << std::endl;
|
||||
@ -360,9 +450,8 @@ std::string service_runner::healthtick()
|
||||
return yellow_exclamation;
|
||||
}
|
||||
|
||||
std::string service_runner::healthmark()
|
||||
std::string service_runner::HealthStatus2String(HealthStatus status)
|
||||
{
|
||||
HealthStatus status = is_healthy();
|
||||
if (status == HealthStatus::HEALTHY)
|
||||
return ":tick:";
|
||||
else if (status == HealthStatus::UNHEALTHY)
|
||||
@ -375,6 +464,12 @@ std::string service_runner::healthmark()
|
||||
return ":error:";
|
||||
}
|
||||
|
||||
std::string service_runner::healthmark()
|
||||
{
|
||||
HealthStatus status = is_healthy();
|
||||
return HealthStatus2String(status);
|
||||
}
|
||||
|
||||
std::vector<int> service_runner::get_ports()
|
||||
{
|
||||
std::vector<int> ports;
|
||||
|
@ -3,6 +3,9 @@
|
||||
// manage a service on a server
|
||||
//
|
||||
|
||||
#ifndef SERVICE_RUNNER_HPP
|
||||
#define SERVICE_RUNNER_HPP
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
@ -11,6 +14,19 @@
|
||||
|
||||
namespace dropshell {
|
||||
|
||||
typedef enum HealthStatus {
|
||||
HEALTHY,
|
||||
UNHEALTHY,
|
||||
NOTINSTALLED,
|
||||
ERROR,
|
||||
UNKNOWN
|
||||
} HealthStatus;
|
||||
|
||||
typedef struct ServiceStatus {
|
||||
HealthStatus health;
|
||||
std::vector<int> ports;
|
||||
} ServiceStatus;
|
||||
|
||||
class service_runner {
|
||||
public:
|
||||
service_runner();
|
||||
@ -30,13 +46,7 @@ class service_runner {
|
||||
// check health of service. Silent.
|
||||
// 1. run status.sh on the server
|
||||
// 2. return the output of the status.sh script
|
||||
enum class HealthStatus {
|
||||
HEALTHY,
|
||||
UNHEALTHY,
|
||||
NOTINSTALLED,
|
||||
ERROR,
|
||||
UNKNOWN
|
||||
};
|
||||
|
||||
HealthStatus is_healthy();
|
||||
|
||||
// get the ports of the service
|
||||
@ -47,6 +57,9 @@ class service_runner {
|
||||
std::string healthtick();
|
||||
std::string healthmark();
|
||||
|
||||
// get the status of all services on the server
|
||||
static std::map<std::string, ServiceStatus> get_all_services_status(std::string server_name);
|
||||
static std::string HealthStatus2String(HealthStatus status);
|
||||
|
||||
private:
|
||||
// install the service over ssh, using the credentials from server.env (via server_env.hpp), by:
|
||||
@ -73,6 +86,7 @@ class service_runner {
|
||||
bool backup();
|
||||
|
||||
|
||||
|
||||
private:
|
||||
std::string m_server_name;
|
||||
ServiceInfo m_service_info;
|
||||
@ -84,12 +98,18 @@ class service_runner {
|
||||
std::string mRemote_service_env_file;
|
||||
|
||||
// Helper methods
|
||||
static std::string construct_ssh_cmd(const server_env &env);
|
||||
|
||||
std::string construct_ssh_cmd() const;
|
||||
bool check_remote_dir_exists(const std::string& dir_path) const;
|
||||
bool check_remote_file_exists(const std::string& file_path) const;
|
||||
bool check_remote_items_exist(const std::vector<std::string>& file_paths) const;
|
||||
bool execute_ssh_command(const std::string& command, const std::string& error_msg) const;
|
||||
bool execute_local_command(const std::string& command, const std::string& error_msg) const;
|
||||
static bool execute_local_command_and_capture_output(const std::string& command, std::string & output);
|
||||
};
|
||||
|
||||
|
||||
} // namespace dropshell
|
||||
|
||||
#endif // SERVICE_RUNNER_HPP
|
||||
|
@ -39,7 +39,6 @@ namespace dropshell {
|
||||
std::string get_remote_service_backups_path(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);
|
||||
std::string get_remote_service_env_file_parent(const std::string &server_name, const std::string &service_name);
|
||||
} // namespace dropshell
|
||||
|
||||
#endif
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "envmanager.hpp"
|
||||
#include "utils/utils.hpp"
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
@ -97,20 +98,6 @@ void envmanager::clear_variables() {
|
||||
m_variables.clear();
|
||||
}
|
||||
|
||||
std::string trim(std::string str) {
|
||||
// Trim leading whitespace
|
||||
str.erase(str.begin(), std::find_if(str.begin(), str.end(), [](unsigned char ch) {
|
||||
return !std::isspace(ch);
|
||||
}));
|
||||
|
||||
// Trim trailing whitespace
|
||||
str.erase(std::find_if(str.rbegin(), str.rend(), [](unsigned char ch) {
|
||||
return !std::isspace(ch);
|
||||
}).base(), str.end());
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string envmanager::expand_patterns(std::string str) const {
|
||||
// Combined regex pattern for both ${var} and $var formats
|
||||
std::regex var_pattern("\\$(?:\\{([^}]+)\\}|([a-zA-Z0-9_]+))");
|
||||
@ -132,62 +119,4 @@ std::string envmanager::expand_patterns(std::string str) const {
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string multi2string(std::vector<std::string> values)
|
||||
{
|
||||
std::string result;
|
||||
for (const auto& value : values) {
|
||||
// remove any " contained in the string value, if present
|
||||
std::string quoteless_value = value;
|
||||
quoteless_value.erase(std::remove(quoteless_value.begin(), quoteless_value.end(), '"'), quoteless_value.end());
|
||||
result += "\"" + trim(quoteless_value) + "\",";
|
||||
}
|
||||
if (!result.empty())
|
||||
result.pop_back(); // Remove the last comma
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string> string2multi(std::string values)
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
|
||||
// Return values separated by commas, but ignore commas within quotes
|
||||
bool inside_quotes = false;
|
||||
std::string current_item;
|
||||
|
||||
for (char c : values) {
|
||||
if (c == '"') {
|
||||
inside_quotes = !inside_quotes;
|
||||
} else if (c == ',' && !inside_quotes) {
|
||||
if (!current_item.empty()) {
|
||||
// Remove quotes if present
|
||||
if (current_item.front() == '"' && current_item.back() == '"') {
|
||||
current_item = current_item.substr(1, current_item.length() - 2);
|
||||
}
|
||||
std::string final = trim(current_item);
|
||||
if (!final.empty()) {
|
||||
result.push_back(final);
|
||||
}
|
||||
current_item.clear();
|
||||
}
|
||||
} else {
|
||||
current_item += c;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the last item if not empty
|
||||
if (!current_item.empty()) {
|
||||
// Remove quotes if present
|
||||
if (current_item.front() == '"' && current_item.back() == '"') {
|
||||
current_item = current_item.substr(1, current_item.length() - 2);
|
||||
}
|
||||
std::string final = trim(current_item);
|
||||
if (!final.empty()) {
|
||||
result.push_back(final);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace dropshell
|
||||
|
@ -45,7 +45,8 @@ class envmanager {
|
||||
};
|
||||
|
||||
// utility functions
|
||||
std::string trim(std::string str);
|
||||
std::string trim(std::string str);
|
||||
std::string dequote(std::string str);
|
||||
std::string multi2string(std::vector<std::string> values);
|
||||
std::vector<std::string> string2multi(std::string values);
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
#include <algorithm>
|
||||
namespace dropshell {
|
||||
|
||||
void maketitle(const std::string& title) {
|
||||
@ -42,4 +42,92 @@ bool replace_line_in_file(const std::string& file_path, const std::string& searc
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string trim(std::string str) {
|
||||
// Trim leading whitespace
|
||||
str.erase(str.begin(), std::find_if(str.begin(), str.end(), [](unsigned char ch) {
|
||||
return !std::isspace(ch);
|
||||
}));
|
||||
|
||||
// Trim trailing whitespace
|
||||
str.erase(std::find_if(str.rbegin(), str.rend(), [](unsigned char ch) {
|
||||
return !std::isspace(ch);
|
||||
}).base(), str.end());
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string dequote(std::string str)
|
||||
{
|
||||
if (str.length() < 2)
|
||||
return str;
|
||||
if (str.front() == '"' && str.back() == '"') {
|
||||
return str.substr(1, str.length() - 2);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string quote(std::string str)
|
||||
{
|
||||
return "\""+str+"\"";
|
||||
}
|
||||
|
||||
std::string multi2string(std::vector<std::string> values)
|
||||
{
|
||||
std::string result;
|
||||
for (const auto& value : values) {
|
||||
// remove any " contained in the string value, if present
|
||||
std::string quoteless_value = value;
|
||||
quoteless_value.erase(std::remove(quoteless_value.begin(), quoteless_value.end(), '"'), quoteless_value.end());
|
||||
result += "\"" + trim(quoteless_value) + "\",";
|
||||
}
|
||||
if (!result.empty())
|
||||
result.pop_back(); // Remove the last comma
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string> string2multi(std::string values)
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
|
||||
// Return values separated by commas, but ignore commas within quotes
|
||||
bool inside_quotes = false;
|
||||
std::string current_item;
|
||||
|
||||
for (char c : values) {
|
||||
if (c == '"') {
|
||||
inside_quotes = !inside_quotes;
|
||||
} else if (c == ',' && !inside_quotes) {
|
||||
if (!current_item.empty()) {
|
||||
// Remove quotes if present
|
||||
if (current_item.front() == '"' && current_item.back() == '"') {
|
||||
current_item = current_item.substr(1, current_item.length() - 2);
|
||||
}
|
||||
std::string final = trim(current_item);
|
||||
if (!final.empty()) {
|
||||
result.push_back(final);
|
||||
}
|
||||
current_item.clear();
|
||||
}
|
||||
} else {
|
||||
current_item += c;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the last item if not empty
|
||||
if (!current_item.empty()) {
|
||||
// Remove quotes if present
|
||||
if (current_item.front() == '"' && current_item.back() == '"') {
|
||||
current_item = current_item.substr(1, current_item.length() - 2);
|
||||
}
|
||||
std::string final = trim(current_item);
|
||||
if (!final.empty()) {
|
||||
result.push_back(final);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
} // namespace dropshell
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace dropshell {
|
||||
|
||||
@ -13,4 +14,13 @@ void maketitle(const std::string& title);
|
||||
|
||||
bool replace_line_in_file(const std::string& file_path, const std::string& search_string, const std::string& replacement_line);
|
||||
|
||||
|
||||
// utility functions
|
||||
std::string trim(std::string str);
|
||||
std::string dequote(std::string str);
|
||||
std::string quote(std::string str);
|
||||
std::string multi2string(std::vector<std::string> values);
|
||||
std::vector<std::string> string2multi(std::string values);
|
||||
|
||||
|
||||
} // namespace dropshell
|
41
templates/dropshell-agent/backup.sh
Normal file
41
templates/dropshell-agent/backup.sh
Normal file
@ -0,0 +1,41 @@
|
||||
#!/bin/bash
|
||||
|
||||
# BACKUP SCRIPT
|
||||
# The backup script is OPTIONAL.
|
||||
# It is used to backup the service on the server.
|
||||
# It is called with:
|
||||
# 1) the path to the server specific env file as the frist argument.
|
||||
# 2) the path to the destination backup file as the second argument.
|
||||
# If the backup file already exists, the script should exit with a message.
|
||||
|
||||
source "$(dirname "$0")/_common.sh"
|
||||
load_env "$1" || die "Failed to load environment variables"
|
||||
|
||||
# Required environment variables
|
||||
check_required_env_vars "CONTAINER_NAME" "LOCAL_DATA_FOLDER"
|
||||
|
||||
# Get backup file path from second argument
|
||||
BACKUP_FILE="$2"
|
||||
if [ -z "$BACKUP_FILE" ]; then
|
||||
die "Backup file path not provided"
|
||||
fi
|
||||
|
||||
# Check if backup file already exists
|
||||
if [ -f "$BACKUP_FILE" ]; then
|
||||
die "Backup file $BACKUP_FILE already exists"
|
||||
fi
|
||||
|
||||
# Stop container before backup
|
||||
_stop_container "$CONTAINER_NAME"
|
||||
|
||||
Create backup of data folder
|
||||
echo "Creating backup of $LOCAL_DATA_FOLDER..."
|
||||
if ! tar zcvf "$BACKUP_FILE" -C "$LOCAL_DATA_FOLDER" .; then
|
||||
_start_container "$CONTAINER_NAME"
|
||||
die "Failed to create backup"
|
||||
fi
|
||||
|
||||
# Start container after backup
|
||||
_start_container "$CONTAINER_NAME"
|
||||
|
||||
echo "Backup created successfully: $BACKUP_FILE"
|
Loading…
x
Reference in New Issue
Block a user