Compare commits

...

4 Commits

Author SHA1 Message Date
ddc57173cb .
Some checks failed
Dropshell Test / Build_and_Test (push) Failing after 11s
2025-05-24 19:27:48 +12:00
343e597d84 Working on nuke
Some checks failed
Dropshell Test / Build_and_Test (push) Has been cancelled
2025-05-24 17:16:19 +12:00
60907e5e02 List tidy
Some checks failed
Dropshell Test / Build_and_Test (push) Failing after 11s
2025-05-24 17:00:43 +12:00
b3398582ca dropshell release 2025.0524.1314
Some checks failed
Dropshell Test / Build_and_Test (push) Failing after 12s
2025-05-24 13:14:51 +12:00
31 changed files with 252 additions and 708 deletions

View File

@ -81,8 +81,26 @@ if(WIN32)
)
endif()
# Configure libassert
include(FetchContent)
FetchContent_Declare(
libassert
GIT_REPOSITORY https://github.com/jeremy-rifkin/libassert.git
GIT_TAG v2.1.5
)
FetchContent_MakeAvailable(libassert)
include(FetchContent)
FetchContent_Declare(
cpptrace
GIT_REPOSITORY https://github.com/jeremy-rifkin/cpptrace.git
GIT_TAG v0.8.3
)
FetchContent_MakeAvailable(cpptrace)
# Link libraries
target_link_libraries(dropshell PRIVATE
libassert::assert
cpptrace::cpptrace
)
# Install targets

View File

@ -19,8 +19,8 @@ _autocommandrun_volume() {
echo "Creating volume ${volume_name}"
docker volume create ${volume_name}
;;
nuke)
echo "Nuking volume ${volume_name}"
destroy)
echo "Destroying volume ${volume_name}"
docker volume rm ${volume_name}
;;
backup)
@ -50,14 +50,14 @@ _autocommandrun_path() {
echo "Creating path ${path}"
mkdir -p ${path}
;;
nuke)
echo "Nuking path ${path}"
destroy)
echo "Destroying path ${path}"
local path_parent=$(dirname ${path})
local path_child=$(basename ${path})
if [ -d "${path_parent}/${path_child}" ]; then
docker run --rm -v ${path_parent}:/volume debian bash -c "rm -rfv /volume/${path_child}" || echo "Failed to nuke path ${path}"
docker run --rm -v ${path_parent}:/volume debian bash -c "rm -rfv /volume/${path_child}" || echo "Failed to destroy path ${path}"
else
echo "Path ${path} does not exist - nothing to nuke"
echo "Path ${path} does not exist - nothing to destroy"
fi
;;
backup)
@ -95,7 +95,7 @@ _autocommandrun_file() {
mkdir -p ${filepath_parent}
fi
;;
nuke)
destroy)
rm -f ${filepath}
;;
backup)
@ -181,8 +181,8 @@ datacreate() {
}
datanuke() {
_autocommandparse nuke none "$@"
datadestroy() {
_autocommandparse destroy none "$@"
}
databackup() {

View File

@ -5,7 +5,7 @@
#include "services.hpp"
#include "servers.hpp"
#include <assert.hpp>
#include <libassert/assert.hpp>
#include <algorithm>
#include <iostream>
@ -14,9 +14,7 @@ namespace autocomplete {
const std::set<std::string> system_commands_noargs = {"templates","autocomplete_list_servers","autocomplete_list_services","autocomplete_list_commands"};
const std::set<std::string> system_commands_always_available = {"help","edit"};
const std::set<std::string> system_commands_require_config = {"server","templates","create-service","create-template","create-server","ssh","list"};
const std::set<std::string> system_commands_hidden = {"nuke","_allservicesstatus"};
const std::set<std::string> service_commands_require_config = {"ssh","edit","nuke","_allservicesstatus"};
const std::set<std::string> system_commands_hidden = {"_allservicesstatus"};
void merge_commands(std::set<std::string> &commands, const std::set<std::string> &new_commands)
{

View File

@ -5,7 +5,7 @@
#include <filesystem>
#include "utils/output.hpp"
#include "utils/assert.hpp"
#include <libassert/assert.hpp>
#include "utils/utils.hpp"
#include "command_registry.hpp"
#include "config.hpp"
@ -53,7 +53,7 @@ namespace dropshell
namespace shared_commands
{
bool backupdata_service(const server_config &server_env, const std::string &service)
bool backupdata_service(const ServerConfig &server_env, const std::string &service)
{
ASSERT(server_env.is_valid(), "Invalid server environment for " + server_env.get_server_name());
std::string server = server_env.get_server_name();

View File

@ -1,7 +1,7 @@
#include "command_registry.hpp"
#include "config.hpp"
#include "utils/output.hpp"
#include "utils/assert.hpp"
#include <libassert/assert.hpp>
namespace dropshell {

View File

@ -10,7 +10,7 @@
#include <iostream>
#include <sstream>
#include <filesystem>
#include "utils/assert.hpp"
#include <libassert/assert.hpp>
namespace dropshell {

View File

@ -3,7 +3,7 @@
#include "shared_commands.hpp"
#include "templates.hpp"
#include "utils/assert.hpp"
#include <libassert/assert.hpp>
#include "utils/utils.hpp"
#include "services.hpp"
@ -103,7 +103,7 @@ namespace dropshell
if (server_name.empty() || template_name.empty() || service_name.empty())
return false;
server_config server_info(server_name);
ServerConfig server_info(server_name);
if (!server_info.is_valid())
{
error << "Server " << server_name << " is not valid" << std::endl;
@ -152,8 +152,9 @@ namespace dropshell
recursive_copy(tinfo.local_template_path() / "config", service_dir);
// append TEMPLATE_HASH to the .template_info.env file
std::string template_info_env_file = service_dir + "/" + filenames::template_info_env;
std::ofstream template_info_env_file_out(template_info_env_file);
std::string template_info_env_file = localfile::template_info_env(server_name,service_name);
ASSERT(std::filesystem::exists(template_info_env_file), "Template info env file not found: " + template_info_env_file);
std::ofstream template_info_env_file_out(template_info_env_file, std::ios::app); // append to the file.
template_info_env_file_out << "TEMPLATE_HASH=" << tinfo.hash() << std::endl;
template_info_env_file_out.close();
@ -249,9 +250,20 @@ namespace dropshell
ASSERT(tinfo.is_set(), "Failed to load template " + service_info.template_name);
// copy across .template_info.env file
std::string template_service_env_file = "";
std::string target_service_env_file = localfile::template_info_env(server_name, service_name);
ASSERT(std::filesystem::exists(template_service_env_file), "Template service env file not found: " + template_service_env_file);
std::string template_info_env_file = tinfo.local_template_path() / "config" / filenames::template_info_env;
std::string target_template_info_env_file = localfile::template_info_env(server_name, service_name);
ASSERT(std::filesystem::exists(template_info_env_file), "Template service env file not found: " + template_info_env_file);
std::filesystem::remove(target_template_info_env_file);
std::filesystem::copy(template_info_env_file, target_template_info_env_file);
#pragma message("TODO: merge the template info env file")
// update hash in template info env file
// append TEMPLATE_HASH to the .template_info.env file
ASSERT(std::filesystem::exists(target_template_info_env_file), "Template info env file not found: " + target_template_info_env_file);
std::ofstream template_info_env_file_out(target_template_info_env_file, std::ios::app); // append to the file.
template_info_env_file_out << "TEMPLATE_HASH=" << tinfo.hash() << std::endl;
template_info_env_file_out.close();
return true;
}

View File

@ -4,7 +4,7 @@
#include "utils/directories.hpp"
#include "shared_commands.hpp"
#include "version.hpp"
#include "utils/assert.hpp"
#include <libassert/assert.hpp>
#include "templates.hpp"
#include <unistd.h>

View File

@ -8,21 +8,21 @@
#include "templates.hpp"
#include "utils/utils.hpp"
#include "utils/assert.hpp"
#include <libassert/assert.hpp>
namespace dropshell
{
int nuke_handler(const CommandContext &ctx);
static std::vector<std::string> nuke_name_list = {"destroy","nuke"};
int destroy_handler(const CommandContext &ctx);
static std::vector<std::string> destroy_name_list = {"destroy"};
// Static registration
struct NukeCommandRegister
struct DestroyCommandRegister
{
NukeCommandRegister()
DestroyCommandRegister()
{
CommandRegistry::instance().register_command({nuke_name_list,
nuke_handler,
CommandRegistry::instance().register_command({destroy_name_list,
destroy_handler,
shared_commands::std_autocomplete,
false, // hidden
true, // requires_config
@ -33,7 +33,7 @@ namespace dropshell
"Destroy a service on a server. Erases everything, both local and remote!",
// heredoc
R"(
Nuke a service.
Destroy a service.
Examples:
destroy SERVER SERVICE destroy the given service on the given server.
@ -45,41 +45,46 @@ namespace dropshell
Use with caution!
)"});
}
} nuke_command_register;
} destroy_command_register;
namespace shared_commands
{
bool nuke_service(const std::string &server, const std::string &service)
bool destroy_service(const std::string &server, const std::string &service)
{
server_config server_env(server);
ServerConfig server_env(server);
// step 1 - nuke on remote server.
// step 1 - destroy on remote server.
if (server_env.is_valid())
{
LocalServiceInfo service_info;
service_info = get_service_info(server, service);
if (!SIvalid(service_info))
error << "Invalid service: " << service << std::endl;
bool service_valid = SIvalid(service_info);
if (!service_valid)
warning << "Invalid service: " << service << std::endl;
std::string user = server_env.get_user_for_service(service);
if (server_env.check_remote_dir_exists(remotepath(server, user).service(service), user))
bool remote_dir_exists = server_env.check_remote_dir_exists(remotepath(server, user).service(service), user);
if (remote_dir_exists)
{
// run the nuke script on the remote server if it exists.
// run the destroy script on the remote server if it exists.
// otherwise just uninstall.
if (gTemplateManager().template_command_exists(service_info.template_name, "nuke"))
if (service_valid)
{
info << "Running nuke script for " << service << " on " << server << std::endl;
if (!server_env.run_remote_template_command(service, "nuke", {}, false, {}))
warning << "Failed to run nuke script: " << service << std::endl;
}
else
{
info << "No nuke 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;
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.
@ -95,9 +100,9 @@ namespace dropshell
warning << "Service not found on remote server: " << remotepath(server, user).service(service) << std::endl;
}
else
warning << "Can't nuke the remote service as the server is invalid: " << server << std::endl;
warning << "Can't destroy the remote service as the server is invalid: " << server << std::endl;
// step 2 - nuke the local service directory.
// step 2 - destroy the local service directory.
std::string local_service_path = localpath::service(server, service);
if (local_service_path.empty() || !std::filesystem::exists(local_service_path))
{
@ -110,15 +115,15 @@ namespace dropshell
error << "Failed to remove local service directory" << std::endl;
}
info << "Nuked service " << service << " on server " << server << std::endl;
info << "Destroyed service " << service << " on server " << server << std::endl;
return true;
}
} // namespace shared_commands
int nuke_handler(const CommandContext &ctx)
int destroy_handler(const CommandContext &ctx)
{
ASSERT(ctx.args.size() == 2, "Usage: nuke SERVER SERVICE|all (requires 2 args - you supplied " + std::to_string(ctx.args.size()) + ")");
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);
@ -141,14 +146,14 @@ namespace dropshell
if (entry.is_directory() && entry.path().filename().string().find(".") != 0)
{
std::string service_name = entry.path().filename().string();
rval |= (shared_commands::nuke_service(server, service_name) ? 0 : 1);
rval |= (shared_commands::destroy_service(server, service_name) ? 0 : 1);
}
}
return rval;
}
else
{
return (shared_commands::nuke_service(server, service) ? 0 : 1);
return (shared_commands::destroy_service(server, service) ? 0 : 1);
}
}

View File

@ -9,7 +9,7 @@
#include <iostream>
#include <sstream>
#include <filesystem>
#include "utils/assert.hpp"
#include <libassert/assert.hpp>
namespace dropshell {

View File

@ -10,7 +10,7 @@
#include <iostream>
#include <sstream>
#include <filesystem>
#include "utils/assert.hpp"
#include <libassert/assert.hpp>
namespace dropshell {
@ -141,7 +141,7 @@ int help_handler(const CommandContext& ctx) {
info << std::endl;
show_command("install");
show_command("uninstall");
show_command("nuke");
show_command("destroy");
info << std::endl;
show_command("start");
show_command("stop");

View File

@ -16,7 +16,7 @@
#include <iostream>
#include <sstream>
#include <filesystem>
#include "utils/assert.hpp"
#include <libassert/assert.hpp>
#include "servers.hpp"
namespace dropshell
@ -63,14 +63,12 @@ namespace dropshell
// ------------------------------------------------------------------------------------------------
// install service over ssh : SHARED COMMAND
// ------------------------------------------------------------------------------------------------
bool install_service(const server_config &server_env, const std::string &service)
bool install_service(const ServerConfig &server_env, const std::string &service)
{
std::string server = server_env.get_server_name();
LocalServiceInfo service_info = get_service_info(server_env.get_server_name(), service);
if (!SIvalid(service_info))
return false;
if (!service_info.service_template_hash_match)
if (!SIvalid(service_info) || !service_info.service_template_hash_match)
{
warning << "Service " << service << " is using an old template. Updating. " << std::endl;
if (!merge_updated_service_template(server_env.get_server_name(), service))
@ -78,6 +76,13 @@ namespace dropshell
error << "Failed to merge updated service template. " << std::endl;
return false;
}
service_info = get_service_info(server_env.get_server_name(), service);
if (!SIvalid(service_info) || !service_info.service_template_hash_match)
{
error << "Merged updated service template, but it is still not valid. " << std::endl;
return false;
}
}
maketitle("Installing " + service + " (" + service_info.template_name + ") on " + server);
@ -266,7 +271,7 @@ namespace dropshell
return 0;
}
int install_server(const server_config &server)
int install_server(const ServerConfig &server)
{
// install the dropshell agent on the given server.
maketitle("Installing dropshell agent on " + server.get_server_name(), sColour::INFO);
@ -276,7 +281,7 @@ namespace dropshell
info << "Installing agent for user " << user.user << " on " << server.get_server_name() << std::endl;
std::string agent_path = remotepath(server.get_server_name(),user.user).agent();
ASSERT(agent_path == user.dir, "Agent path does not match user directory for "+user.user+"@" + server.get_server_name() + " : " + agent_path + " != " + user.dir);
ASSERT(agent_path == user.dir+"/agent", "Agent path does not match user directory for "+user.user+"@" + server.get_server_name() + " : " + agent_path + " != " + user.dir);
ASSERT(!agent_path.empty(), "Agent path is empty for " + user.user + "@" + server.get_server_name());
// now create the agent.
@ -316,7 +321,7 @@ namespace dropshell
return rval;
// install the dropshell agent on all servers.
std::vector<server_config> servers = get_configured_servers();
std::vector<ServerConfig> servers = get_configured_servers();
for (const auto &server : servers)
{
rval = install_server(server);
@ -359,7 +364,7 @@ namespace dropshell
return 1;
}
server_config server_env(server);
ServerConfig server_env(server);
ASSERT(server_env.is_valid(), "Invalid server environment for " + server);
if (safearg(ctx.args, 1) == "all")
{

View File

@ -14,7 +14,7 @@
#include <iostream>
#include <sstream>
#include <filesystem>
#include "utils/assert.hpp"
#include <libassert/assert.hpp>
namespace dropshell {
@ -83,44 +83,61 @@ void list_servers() {
tableprint tp("All DropShell Servers");
tp.add_row({"Name", "Address", "User", "Health", "Ports"});
info << "Checking "<<servers.size() << " servers: " << std::flush;
typedef std::map<std::string, shared_commands::ServiceStatus> tServiceStatusMap;
std::vector<tServiceStatusMap> service_status_maps;
typedef struct {dropshell::ServerConfig server; dropshell::UserConfig user;} server_user_pair;
std::vector<server_user_pair> server_user_pairs;
for (const auto& server : servers)
for (const auto& user : server.get_users())
server_user_pairs.push_back({server, user});
// mutex for the tableprint
std::mutex tp_mutex;
info << "Checking "<<server_user_pairs.size() << " agents: " << std::flush;
int checked = 0;
transwarp::parallel exec{servers.size()};
auto task = transwarp::for_each(exec, servers.begin(), servers.end(), [&](const server_config& server) {
server_config server_env(server.get_server_name());
transwarp::parallel exec{server_user_pairs.size()};
auto task = transwarp::for_each(exec, server_user_pairs.begin(), server_user_pairs.end(), [&](const server_user_pair& sup) {
ServerConfig server_env(sup.server.get_server_name());
if (!server_env.is_valid())
{
error << "Invalid server environment for " << server.get_server_name() << std::endl;
error << "Invalid server environment for " << sup.server.get_server_name() << std::endl;
return;
}
int first=true;
for (const auto &user : server_env.get_users())
{
std::map<std::string, shared_commands::ServiceStatus> status = shared_commands::get_all_services_status(server.get_server_name(),user.user);
std::string serviceticks = "";
std::string ports_used_str = "";
std::set<int> ports_used;
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 += shared_commands::HealthStatus2String(service_status.health) + " ";
}
std::string ports_used_str = "";
for (const auto& port : ports_used)
ports_used_str += std::to_string(port) + " ";
std::map<std::string, shared_commands::ServiceStatus> status = shared_commands::get_all_services_status(sup.server.get_server_name(),sup.user.user);
tp.add_row({(first ? server.get_server_name() : ""), (first ? server.get_SSH_HOST() : ""), user.user, serviceticks, ports_used_str});
first = false;
}
for (const auto& [service_name, service_status] : status) {
ports_used.insert(service_status.ports.begin(), service_status.ports.end());
serviceticks += shared_commands::HealthStatus2String(service_status.health) + " ";
}
++checked;
// print out a tick character for each server checked.
info << checked << "" << std::flush;
for (const auto& port : ports_used)
ports_used_str += std::to_string(port) + " ";
// critical section
{
std::lock_guard<std::mutex> lock(tp_mutex);
tp.add_row({sup.server.get_server_name(), sup.server.get_SSH_HOST(), sup.user.user, serviceticks, ports_used_str});
++checked;
// print out a tick character for each server checked.
info << checked << "" << std::flush;
}
});
task->wait();
info << std::endl << std::endl;
tp.sort({0,2});
tp.print();
}
@ -128,7 +145,7 @@ void list_servers() {
void show_server_details(const std::string& server_name) {
server_config env(server_name);
ServerConfig env(server_name);
if (!env.is_valid()) {
error << "Error: Invalid server environment file: " << server_name << std::endl;
return;

View File

@ -5,7 +5,7 @@
#include <filesystem>
#include "utils/output.hpp"
#include "utils/assert.hpp"
#include <libassert/assert.hpp>
#include "utils/utils.hpp"
#include "command_registry.hpp"
#include "config.hpp"
@ -92,7 +92,7 @@ namespace dropshell
std::string service = ctx.args[1];
std::string backup_arg = ctx.args[2];
server_config server_env(server);
ServerConfig server_env(server);
if (!server_env.is_valid())
{
error << "Server " << server << " is not valid" << std::endl;
@ -175,9 +175,9 @@ namespace dropshell
info << "Backup complete." << std::endl;
}
{ // nuke the old service
info << "2) Nuking old service..." << std::endl;
if (!shared_commands::nuke_service(server, service))
{ // Destroy the old service
info << "2) Destroying old service..." << std::endl;
if (!shared_commands::destroy_service(server, service))
return 1;
}
@ -189,7 +189,7 @@ namespace dropshell
{ // installing fresh service
info << "4) Install of fresh service..." << std::endl;
server_config server_env(server);
ServerConfig server_env(server);
if (!shared_commands::install_service(server_env, service))
return 1;
}

View File

@ -1,5 +1,5 @@
#include "shared_commands.hpp"
#include "utils/assert.hpp"
#include <libassert/assert.hpp>
#include "utils/utils.hpp"
#include "servers.hpp"
#include "directories.hpp"
@ -21,7 +21,7 @@ namespace dropshell
if (ctx.args.size() == 0)
{ // just the command, no args yet.
// list servers
std::vector<server_config> servers = get_configured_servers();
std::vector<ServerConfig> servers = get_configured_servers();
for (const auto &server : servers)
{
rawout << server.get_server_name() << std::endl;
@ -54,7 +54,7 @@ namespace dropshell
bool rsync_tree_to_remote(
const std::string &local_path,
const std::string &remote_path,
const server_config &server_env,
const ServerConfig &server_env,
bool silent,
std::string user)
{
@ -85,7 +85,7 @@ namespace dropshell
// ------------------------------------------------------------------------------------------------
// cRemoteTempFolder : SHARED CLASS
// ------------------------------------------------------------------------------------------------
cRemoteTempFolder::cRemoteTempFolder(const server_config &server_env, std::string user) :
cRemoteTempFolder::cRemoteTempFolder(const ServerConfig &server_env, std::string user) :
mServerEnv(server_env), mUser(user)
{
std::string p = remotepath(server_env.get_server_name(),user).temp_files() + "/" + random_alphanumeric_string(10);
@ -110,7 +110,7 @@ namespace dropshell
// ------------------------------------------------------------------------------------------------
// get_all_services_status : SHARED COMMAND
// ------------------------------------------------------------------------------------------------
std::map<std::string, ServiceStatus> get_all_services_status(const server_config & server_env)
std::map<std::string, ServiceStatus> get_all_services_status(const ServerConfig & server_env)
{
std::map<std::string, ServiceStatus> status;
for (const auto& user : server_env.get_users()) {
@ -119,7 +119,7 @@ namespace dropshell
return status;
}
std::map<std::string, ServiceStatus> get_all_services_status(const server_config & server_env, std::string user)
std::map<std::string, ServiceStatus> get_all_services_status(const ServerConfig & server_env, std::string user)
{
std::map<std::string, ServiceStatus> status;
std::string server_name = server_env.get_server_name();
@ -216,7 +216,7 @@ namespace dropshell
// ------------------------------------------------------------------------------------------------
HealthStatus is_healthy(const std::string &server, const std::string &service)
{
server_config env(server);
ServerConfig env(server);
if (!env.is_valid())
{
error << "Server service not initialized" << std::endl;
@ -309,7 +309,7 @@ namespace dropshell
// ------------------------------------------------------------------------------------------------
// scp_file_to_remote : SHARED COMMAND
// ------------------------------------------------------------------------------------------------
bool scp_file_to_remote(const server_config &server_env, const std::string &local_path, const std::string &remote_path, bool silent, std::string user)
bool scp_file_to_remote(const ServerConfig &server_env, const std::string &local_path, const std::string &remote_path, bool silent, std::string user)
{
if (!server_env.is_valid())
{
@ -324,7 +324,7 @@ namespace dropshell
// ------------------------------------------------------------------------------------------------
// scp_file_from_remote : SHARED COMMAND
// ------------------------------------------------------------------------------------------------
bool scp_file_from_remote(const server_config &server_env, const std::string &remote_path, const std::string &local_path, bool silent, std::string user)
bool scp_file_from_remote(const ServerConfig &server_env, const std::string &remote_path, const std::string &local_path, bool silent, std::string user)
{
if (!server_env.is_valid())
{

View File

@ -31,26 +31,26 @@ namespace dropshell
class cRemoteTempFolder
{
public:
cRemoteTempFolder(const server_config &server_env, std::string user); // create a temp folder on the remote server
cRemoteTempFolder(const ServerConfig &server_env, std::string user); // create a temp folder on the remote server
~cRemoteTempFolder(); // delete the temp folder on the remote server
std::string path() const; // get the path to the temp folder on the remote server
private:
std::string mPath;
const server_config &mServerEnv;
const ServerConfig &mServerEnv;
std::string mUser;
};
bool rsync_tree_to_remote(
const std::string &local_path,
const std::string &remote_path,
const server_config &server_env,
const ServerConfig &server_env,
bool silent,
std::string user);
std::string get_arch();
std::map<std::string, ServiceStatus> get_all_services_status(const server_config & server_env);
std::map<std::string, ServiceStatus> get_all_services_status(const server_config & server_env, std::string user);
std::map<std::string, ServiceStatus> get_all_services_status(const ServerConfig & server_env);
std::map<std::string, ServiceStatus> get_all_services_status(const ServerConfig & server_env, std::string user);
std::string healthtick(const std::string &server, const std::string &service);
std::string HealthStatus2String(HealthStatus status);
@ -81,20 +81,20 @@ namespace dropshell
std::string mDatetime;
};
bool scp_file_to_remote(const server_config &server_env, const std::string &local_path, const std::string &remote_path, bool silent, std::string user);
bool scp_file_from_remote(const server_config &server_env, const std::string &remote_path, const std::string &local_path, bool silent, std::string user);
bool scp_file_to_remote(const ServerConfig &server_env, const std::string &local_path, const std::string &remote_path, bool silent, std::string user);
bool scp_file_from_remote(const ServerConfig &server_env, const std::string &remote_path, const std::string &local_path, bool silent, std::string user);
// defined in backupdata.cpp, used by restoredata.cpp.
bool backupdata_service(const server_config &server_env, const std::string& service);
bool backupdata_service(const ServerConfig &server_env, const std::string& service);
// defined in uninstall.cpp
bool uninstall_service(const server_config &server_env, const std::string &service);
bool uninstall_service(const ServerConfig &server_env, const std::string &service);
// defined in nuke.cpp
bool nuke_service(const std::string &server, const std::string &service);
// defined in destroy.cpp
bool destroy_service(const std::string &server, const std::string &service);
// defined in install.cpp
bool install_service(const server_config &server_env, const std::string &service);
bool install_service(const ServerConfig &server_env, const std::string &service);
// defined in create-service.cpp
bool create_service(const std::string &server_name, const std::string &template_name, const std::string &service_name, std::string user_override="");

View File

@ -7,7 +7,7 @@
#include "services.hpp"
#include "servers.hpp"
#include "templates.hpp"
#include "assert.hpp"
#include <libassert/assert.hpp>
namespace dropshell
{
@ -42,7 +42,7 @@ namespace dropshell
bool ssh_into_server(const std::string &server, std::string user)
{
server_config server_env(server);
ServerConfig server_env(server);
if (!server_env.is_valid())
{
error << "Server " << server << " is not valid" << std::endl;
@ -54,7 +54,7 @@ namespace dropshell
bool ssh_into_service(const std::string &server, const std::string &service)
{
server_config server_env(server);
ServerConfig server_env(server);
if (!server_env.is_valid())
{
error << "Server " << server << " is not valid" << std::endl;
@ -109,7 +109,7 @@ namespace dropshell
server = arg1;
// get the first user from the server.env file, and ssh in as that user.
server_config server_env(server);
ServerConfig server_env(server);
if (!server_env.is_valid())
{
error << "Server " << server << " is not valid" << std::endl;

View File

@ -44,7 +44,7 @@ namespace dropshell
bool start_service(const std::string &server, const std::string &service)
{
server_config server_env(server);
ServerConfig server_env(server);
if (!server_env.is_valid())
{
error << "Server " << server << " is not valid" << std::endl;

View File

@ -44,7 +44,7 @@ namespace dropshell
bool stop_service(const std::string &server, const std::string &service)
{
server_config server_env(server);
ServerConfig server_env(server);
if (!server_env.is_valid())
{
error << "Server " << server << " is not valid" << std::endl;

View File

@ -3,7 +3,7 @@
#include "shared_commands.hpp"
#include "templates.hpp"
#include "utils/assert.hpp"
#include <libassert/assert.hpp>
#include "utils/utils.hpp"
#include "services.hpp"
@ -36,13 +36,13 @@ namespace dropshell
uninstall SERVER SERVICE Uninstall the given service on the given server.
uninstall SERVER all Uninstall all services on the given server.
Update and reinstall the service with install, or delete all configuration and data with nuke.
Update and reinstall the service with install, or delete all configuration and data with destroy.
)"});
}
} uninstall_command_register;
namespace shared_commands {
bool uninstall_service(const server_config & server_env, const std::string &service)
bool uninstall_service(const ServerConfig & server_env, const std::string &service)
{
ASSERT(server_env.is_valid(), "Invalid server environment for " + server_env.get_server_name());
std::string server = server_env.get_server_name();

View File

@ -16,7 +16,7 @@
#include <vector>
#include <iomanip>
#include <chrono>
#include <assert.hpp>
#include <libassert/assert.hpp>
#include <sstream>
#include <algorithm>
namespace dropshell {
@ -140,115 +140,6 @@ auto command_match = [](const std::string& cmd_list, int argc, char* argv[]) ->
} \
}
// int old_main(int argc, char* argv[]) {
// HAPPYEXIT("hash", hash_demo_raw(safearg(argc,argv,2)))
// HAPPYEXIT("version", printversion())
// BOOLEXIT("test-template", gTemplateManager().test_template(safearg(argc,argv,2)))
// ASSERT(safearg(argc,argv,1) != "assert", "Hello! Here is an assert.");
// try {
// // silently attempt to load the config file and templates.
// gConfig().load_config();
// if (gConfig().is_config_set())
// gTemplateManager().load_sources();
// std::string cmd = argv[1];
// // ------------------------------------------------------------
// // from here we require the config file to be loaded.
// if (!gConfig().is_config_set())
// return die("Please run 'dropshell edit' to set up the dropshell configuration.");
// const std::vector<std::string> & server_definition_paths = gConfig().get_local_server_definition_paths();
// if (server_definition_paths.size()>1) { // only show if there are multiple.
// std::cout << "Server definition paths: ";
// for (auto & dir : server_definition_paths)
// std::cout << "["<< dir << "] ";
// std::cout << std::endl;
// }
// if (gTemplateManager().is_loaded() && gTemplateManager().get_source_count() > 0)
// gTemplateManager().print_sources();
// HAPPYEXIT("templates", gTemplateManager().list_templates());
// if (cmd == "create-template") {
// if (argc < 3) return die("Error: create-template requires a template name");
// return (gTemplateManager().create_template(argv[2])) ? 0 : 1;
// }
// if (cmd == "create-server") {
// if (argc < 3) return die("Error: create-server requires a server name");
// return (create_server(argv[2])) ? 0 : 1;
// }
// if (cmd == "create-service") {
// if (argc < 5) return die("Error: not enough arguments.\ndropshell create-service server template service");
// return (create_service(argv[2], argv[3], argv[4])) ? 0 : 1;
// }
// if (cmd == "ssh" && argc < 4) {
// if (argc < 3) return die("Error: ssh requires a server name and optionally service name");
// service_runner::interactive_ssh(argv[2], "bash");
// return 0;
// }
// // handle running a command.
// std::set<std::string> commands;
// get_all_used_commands(commands);
// autocomplete::merge_commands(commands, autocomplete::service_commands_require_config); // handled by service_runner, but not in template_shell_commands.
// if (commands.count(cmd)) {
// std::set<std::string> safe_commands = {"nuke", "fullnuke"};
// if (safe_commands.count(cmd) && argc < 4)
// return die("Error: "+cmd+" requires a server name and service name. For safety, can't run on all services.");
// // get all the services to run the command on.
// ServerAndServices server_and_services;
// if (!getCLIServices(safearg(argc, argv, 2), safearg(argc, argv, 3), server_and_services))
// return die("Error: "+cmd+" command requires server name and optionally service name");
// // run the command on each service.
// for (const auto& service_info : server_and_services.servicelist) {
// if (!SIvalid(service_info))
// std::cerr<<"Error: Unable to get service information."<<std::endl;
// else {
// service_runner runner(server_and_services.server_name, service_info.service_name);
// if (!runner.isValid())
// return die("Error: Failed to initialize service");
// std::vector<std::string> additional_args;
// for (int i=4; i<argc; i++)
// additional_args.push_back(argv[i]);
// if (!runner.run_command(cmd, additional_args))
// return die(cmd+" failed on service "+service_info.service_name);
// }
// }
// // success!
// return 0;
// }
// // Unknown command
// std::cerr << "Error: Unknown command '" << cmd << "'" << std::endl;
// std::cerr << "Valid commands: ";
// for (const auto& command : commands) {
// if (!command.empty() && command[0]!='_')
// std::cerr << command << " ";
// }
// std::cerr << std::endl;
// return 1;
// } catch (const std::exception& e) {
// std::cerr << "Error: " << e.what() << std::endl;
// return 1;
// }
// }
} // namespace dropshell
int main(int argc, char* argv[]) {

View File

@ -6,7 +6,7 @@
#include "utils/utils.hpp"
#include "utils/execute.hpp"
#include "output.hpp"
#include "utils/assert.hpp"
#include <libassert/assert.hpp>
#include "config.hpp"
#include <iostream>
@ -23,7 +23,7 @@
namespace dropshell
{
server_config::server_config(const std::string &server_name) : mValid(false), mServerName(server_name)
ServerConfig::ServerConfig(const std::string &server_name) : mValid(false), mServerName(server_name)
{
if (server_name.empty())
return;
@ -110,22 +110,22 @@ namespace dropshell
}
}
std::string server_config::get_SSH_HOST() const
std::string ServerConfig::get_SSH_HOST() const
{
return get_variable("SSH_HOST");
}
std::string server_config::get_SSH_PORT() const
std::string ServerConfig::get_SSH_PORT() const
{
return get_variable("SSH_PORT");
}
std::vector<UserConfig> server_config::get_users() const
std::vector<UserConfig> ServerConfig::get_users() const
{
return mUsers;
}
std::string server_config::get_user_dir(const std::string &user) const
std::string ServerConfig::get_user_dir(const std::string &user) const
{
for (const auto &u : mUsers)
{
@ -137,12 +137,12 @@ namespace dropshell
return "";
}
std::string server_config::get_server_name() const
std::string ServerConfig::get_server_name() const
{
return mServerName;
}
std::string server_config::get_user_for_service(const std::string &service) const
std::string ServerConfig::get_user_for_service(const std::string &service) const
{
return dropshell::get_user_for_service(mServerName, service);
}
@ -158,7 +158,7 @@ namespace dropshell
return "";
}
sSSHInfo server_config::get_SSH_INFO(std::string user) const
sSSHInfo ServerConfig::get_SSH_INFO(std::string user) const
{
ASSERT(!user.empty(), "User is empty, cannot get SSH info.");
// Find user in mUsers vector
@ -169,24 +169,24 @@ namespace dropshell
return sSSHInfo(get_SSH_HOST(), it->user, get_SSH_PORT(), get_server_name(), it->dir);
}
bool server_config::hasRootUser() const
bool ServerConfig::hasRootUser() const
{
auto it = std::find_if(mUsers.begin(), mUsers.end(),[](const UserConfig &u)
{ return u.user == "root"; });
return it != mUsers.end();
}
bool server_config::hasDocker() const
bool ServerConfig::hasDocker() const
{
return get_variable("HAS_DOCKER") == "true";
}
bool server_config::hasRootDocker() const
bool ServerConfig::hasRootDocker() const
{
return get_variable("DOCKER_ROOTLESS") == "false";
}
bool server_config::hasUser(const std::string &user) const
bool ServerConfig::hasUser(const std::string &user) const
{
auto it = std::find_if(mUsers.begin(), mUsers.end(),
[&user](const UserConfig &u)
@ -194,19 +194,19 @@ namespace dropshell
return it != mUsers.end();
}
bool server_config::check_remote_dir_exists(const std::string &dir_path, std::string user) const
bool ServerConfig::check_remote_dir_exists(const std::string &dir_path, std::string user) const
{
sCommand scommand("", "test -d " + quote(dir_path), {});
return execute_ssh_command(get_SSH_INFO(user), scommand, cMode::Silent);
}
bool server_config::check_remote_file_exists(const std::string &file_path, std::string user) const
bool ServerConfig::check_remote_file_exists(const std::string &file_path, std::string user) const
{
sCommand scommand("", "test -f " + quote(file_path), {});
return execute_ssh_command(get_SSH_INFO(user), scommand, cMode::Silent);
}
bool server_config::check_remote_items_exist(const std::vector<std::string> &file_paths, std::string user) const
bool ServerConfig::check_remote_items_exist(const std::vector<std::string> &file_paths, std::string user) const
{
// convert file_paths to a single string, separated by spaces
std::string file_paths_str;
@ -229,7 +229,7 @@ namespace dropshell
return true;
}
bool server_config::remove_remote_dir(
bool ServerConfig::remove_remote_dir(
const std::string &dir_path, bool silent, std::string user) const
{
std::filesystem::path path(dir_path);
@ -258,7 +258,7 @@ namespace dropshell
return execute_ssh_command(sshinfo, scommand, mode);
}
bool server_config::run_remote_template_command(
bool ServerConfig::run_remote_template_command(
const std::string &service_name,
const std::string &command,
std::vector<std::string> args,
@ -280,7 +280,7 @@ namespace dropshell
return execute_ssh_command(get_SSH_INFO(user), scommand.value(), mode);
}
bool server_config::run_remote_template_command_and_capture_output(
bool ServerConfig::run_remote_template_command_and_capture_output(
const std::string &service_name,
const std::string &command,
std::vector<std::string> args,
@ -300,7 +300,7 @@ namespace dropshell
return execute_ssh_command(get_SSH_INFO(user), scommand.value(), cMode::Defaults, &output);
}
std::string server_config::get_variable(const std::string &name) const
std::string ServerConfig::get_variable(const std::string &name) const
{
auto it = mVariables.find(name);
if (it == mVariables.end())
@ -310,7 +310,7 @@ namespace dropshell
return it->second;
}
std::optional<sCommand> server_config::construct_standard_template_run_cmd(const std::string &service_name, const std::string &command, const std::vector<std::string> args, const bool silent) const
std::optional<sCommand> ServerConfig::construct_standard_template_run_cmd(const std::string &service_name, const std::string &command, const std::vector<std::string> args, const bool silent) const
{
if (command.empty())
return std::nullopt;
@ -346,9 +346,9 @@ namespace dropshell
return sc;
}
std::vector<server_config> get_configured_servers()
std::vector<ServerConfig> get_configured_servers()
{
std::vector<server_config> servers;
std::vector<ServerConfig> servers;
std::vector<std::string> lsdp = gConfig().get_local_server_definition_paths();
if (lsdp.empty())
@ -367,7 +367,7 @@ namespace dropshell
if (server_name.empty() || server_name[0] == '.' || server_name[0] == '_')
continue;
server_config env(server_name);
ServerConfig env(server_name);
if (!env.is_valid())
{
std::cerr << "Error: Invalid server environment file: " << entry.path().string() << std::endl;
@ -430,7 +430,7 @@ namespace dropshell
void get_all_used_commands(std::set<std::string> &commands)
{
std::vector<server_config> servers = get_configured_servers();
std::vector<ServerConfig> servers = get_configured_servers();
for (const auto &server : servers)
{
auto services = get_server_services_info(server.get_server_name());

View File

@ -32,10 +32,10 @@ namespace dropshell
// SSH_PORT
// the following replacements are made in the values:
// ${USER} -> the username of the user running dropshell
class server_config
class ServerConfig
{
public:
server_config(const std::string &server_name);
ServerConfig(const std::string &server_name);
bool is_valid() const { return mValid; }
@ -85,11 +85,11 @@ namespace dropshell
std::map<std::string, std::string> mVariables;
std::vector<UserConfig> mUsers;
bool mValid;
}; // class server_config
}; // class ServerConfig
std::vector<server_config> get_configured_servers();
std::vector<ServerConfig> get_configured_servers();
std::string get_user_for_service(const std::string &server, const std::string &service);

View File

@ -7,7 +7,7 @@
// #include <iomanip>
// #include <filesystem>
// #include <unistd.h>
// #include "utils/assert.hpp"
// #include <libassert/assert.hpp>
// #include "config.hpp"
// #include "servers.hpp"
@ -19,74 +19,6 @@
// #include "shared_commands.hpp"
// namespace dropshell {
// class service_runner {
// public:
// service_runner(const std::string& server_name, const std::string& service_name);
// bool isValid() const { return mValid; }
// // run a command over ssh, using the credentials from server.env (via server_env.hpp)
// // first check that the command corresponds to a valid .sh file in the service directory
// // then run the command, passing the {service_name}.env file as an argument
// // do a lot of checks, such as:
// // checking that we can ssh to the server.
// // checking whether the service directory exists on the server.
// // checking that the command exists in the service directory.
// // checking that the command is a valid .sh file.
// // checking that the {service_name}.env file exists in the service directory.
// bool run_command(const std::string& command, std::vector<std::string> additional_args={}, std::map<std::string, std::string> env_vars={});
// // check health of service. Silent.
// // 1. run status.sh on the server
// // 2. return the output of the status.sh script
// //HealthStatus is_healthy();
// // std::string healthtick();
// // std::string healthmark();
// public:
// // backup and restore
// bool backup(bool silent=false);
// bool restore(std::string backup_file, bool silent=false);
// // nuke the service
// bool nuke(bool silent=false); // nukes all data for this service on the remote server
// bool fullnuke(); // nuke all data for this service on the remote server, and then nukes all the local service definitionfiles
// // launch an interactive ssh session on a server or service
// // replaces the current dropshell process with the ssh process
// bool interactive_ssh_service();
// bool scp_file_to_remote(const std::string& local_path, const std::string& remote_path, bool silent=false);
// bool scp_file_from_remote(const std::string& remote_path, const std::string& local_path, bool silent=false);
// public:
// // utility functions
// static std::string get_latest_backup_file(const std::string& server, const std::string& service);
// static bool interactive_ssh(const std::string & server_name, const std::string & command);
// // static std::map<std::string, ServiceStatus> get_all_services_status(std::string server_name);
// private:
// std::string mServer;
// server_config mServerEnv;
// LocalServiceInfo mServiceInfo;
// std::string mService;
// bool mValid;
// // Helper methods
// public:
// };
// } // namespace dropshell
// namespace fs = std::filesystem;
// namespace dropshell {
@ -113,47 +45,6 @@
// bool service_runner::nuke(bool silent)
// {
// maketitle("Nuking " + mService + " (" + mServiceInfo.template_name + ") on " + mServer);
// if (!mServerEnv.is_valid()) return false; // should never hit this.
// std::string remote_service_path = remotepath::service(mServer, mService);
// info << "Service " << mService << " successfully nuked from " << mServer << std::endl;
// if (!silent) {
// info << "There's nothing left on the remote server." << std::endl;
// info << "You can remove the local files with:" << std::endl;
// info << " rm -rf " << localpath::service(mServer,mService) << std::endl;
// }
// return true;
// }
// bool service_runner::fullnuke()
// {
// if (!nuke(true))
// {
// warning << "Nuke script failed, aborting." << std::endl;
// return false;
// }
// std::string local_service_path = mServiceInfo.local_service_path;
// if (local_service_path.empty() || !fs::exists(local_service_path)) {
// error << "Service directory not found: " << local_service_path << std::endl;
// return false;
// }
// std::string rm_cmd = "rm -rf " + quote(local_service_path);
// if (!execute_local_command("", rm_cmd, {}, nullptr, cMode::Silent)) {
// error << "Failed to remove service directory" << std::endl;
// return false;
// }
// return true;
// }
// // ------------------------------------------------------------------------------------------------
// // Run a command on the service.
@ -169,15 +60,6 @@
// return false;
// }
// if (command == "fullnuke")
// return fullnuke();
// if (command == "nuke")
// {
// std::cout << "Nuking " << mService << " (" << mServiceInfo.template_name << ") on " << mServer << std::endl;
// return nuke();
// }
// if (!gTemplateManager().template_command_exists(mServiceInfo.template_name, command)) {
// std::cout << "No command script for " << mServiceInfo.template_name << " : " << command << std::endl;
// return true; // nothing to run.
@ -232,298 +114,4 @@
// }
// bool service_runner::interactive_ssh(const std::string & server_name, const std::string & command) {
// std::string serverpath = localpath::server(server_name);
// if (serverpath.empty()) {
// std::cerr << "Error: Server not found: " << server_name << std::endl;
// return false;
// }
// server_config env(server_name);
// if (!env.is_valid()) {
// std::cerr << "Error: Invalid server environment file: " << server_name << std::endl;
// return false;
// }
// sCommand scommand("", "bash",{});
// return execute_ssh_command(env.get_SSH_INFO(), scommand, cMode::Interactive);
// }
// bool service_runner::interactive_ssh_service()
// {
// std::set<std::string> used_commands = get_used_commands(mServer, mService);
// if (used_commands.find("ssh") == used_commands.end()) {
// std::cerr << "Error: "<< mService <<" does not support ssh" << std::endl;
// return false;
// }
// std::vector<std::string> args; // not passed through yet.
// return mServerEnv.run_remote_template_command(mService, "ssh", args, false, {});
// }
// bool service_runner::scp_file_to_remote(const std::string &local_path, const std::string &remote_path, bool silent)
// {
// std::string scp_cmd = "scp -P " + mServerEnv.get_SSH_PORT() + " " + quote(local_path) + " " + mServerEnv.get_SSH_UNPRIVILEGED_USER() + "@" + mServerEnv.get_SSH_HOST() + ":" + quote(remote_path) + (silent ? " > /dev/null 2>&1" : "");
// return execute_local_command("", scp_cmd, {}, nullptr, (silent ? cMode::Silent : cMode::Defaults));
// }
// bool service_runner::scp_file_from_remote(const std::string &remote_path, const std::string &local_path, bool silent)
// {
// std::string scp_cmd = "scp -P " + mServerEnv.get_SSH_PORT() + " " + mServerEnv.get_SSH_UNPRIVILEGED_USER() + "@" + mServerEnv.get_SSH_HOST() + ":" + quote(remote_path) + " " + quote(local_path) + (silent ? " > /dev/null 2>&1" : "");
// return execute_local_command("", scp_cmd, {}, nullptr, (silent ? cMode::Silent : cMode::Defaults));
// }
// bool service_runner::restore(std::string backup_file, bool silent)
// {
// if (backup_file.empty()) {
// std::cerr << "Error: not enough arguments. dropshell restore <server> <service> <backup-file>" << std::endl;
// return false;
// }
// std::string local_backups_dir = gConfig().get_local_backup_path();
// if (backup_file == "latest") {
// // get the latest backup file from the server
// backup_file = get_latest_backup_file(mServer, mService);
// }
// 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 false;
// }
// // split the backup filename into parts based on the magic string
// std::vector<std::string> parts = dropshell::split(backup_file, "-_-");
// if (parts.size() != 4) {
// std::cerr << "Error: Backup file format is incompatible, - in one of the names?" << std::endl;
// return false;
// }
// 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 != mServiceInfo.template_name) {
// std::cerr << "Error: Backup template does not match service template. Can't restore." << std::endl;
// return false;
// }
// 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 "<<mServer<<"/"<<mService<<std::endl;
// std::cout << std::endl;
// std::cout << "*** ALL DATA FOR "<<mServer<<"/"<<mService<<" WILL BE OVERWRITTEN! ***"<<std::endl;
// // run the restore script
// std::cout << "OK, here goes..." << std::endl;
// { // backup existing service
// maketitle("1) Backing up old service... ");
// if (!backup(true)) // silent=true
// {
// std::cerr << std::endl;
// std::cerr << "Error: Backup failed, restore aborted." << std::endl;
// std::cerr << "You can try using dropshell install "<<mServer<<" "<<mService<<" to install the service afresh." << std::endl;
// std::cerr << "Otherwise, stop the service, create and initialise a new one, then restore to that." << std::endl;
// return false;
// }
// std::cout << "Backup complete." << std::endl;
// }
// { // uninstall service, then nuke it.
// maketitle("2) Uninstalling old service...");
// // if (!uninstall(true))
// // return false;
// maketitle("3) Nuking old service...");
// // if (!nuke(true))
// // return false;
// }
// { // restore service from backup
// maketitle("4) Restoring service data from backup...");
// std::string remote_backups_dir = remotepath::backups(mServer);
// std::string remote_backup_file_path = remote_backups_dir + "/" + backup_file;
// // Copy backup file from local to server
// if (!scp_file_to_remote(local_backup_file_path, remote_backup_file_path, silent)) {
// std::cerr << "Failed to copy backup file from local to server" << std::endl;
// return false;
// }
// shared_commands::cRemoteTempFolder remote_temp_folder(mServerEnv);
// mServerEnv.run_remote_template_command(mService, "restore", {}, silent, {{"BACKUP_FILE", remote_backup_file_path}, {"TEMP_DIR", remote_temp_folder.path()}});
// } // dtor of remote_temp_folder will clean up the temp folder on the server
// // { // installing fresh service
// // maketitle("5) Non-destructive install of fresh service...");
// // if (!install_service(mServer, mService, true))
// // return false;
// // }
// bool healthy = false;
// {// healthcheck the service
// maketitle("6) Healthchecking service...");
// std::string green_tick = "\033[32m✓\033[0m";
// std::string red_cross = "\033[31m✗\033[0m";
// healthy= (mServerEnv.run_remote_template_command(mService, "status", {}, silent, {}));
// if (!silent)
// std::cout << (healthy ? green_tick : red_cross) << " Service is " << (healthy ? "healthy" : "NOT healthy") << std::endl;
// }
// return healthy;
// }
// // 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.
// // ------------------------------------------------------------------------------------------------
// bool service_runner::backup(bool silent) {
// auto service_info = get_service_info(mServer, mService);
// if (service_info.local_service_path.empty()) {
// std::cerr << "Error: Service not found" << std::endl;
// return 1;
// }
// const std::string command = "backup";
// if (!gTemplateManager().template_command_exists(service_info.template_name, command)) {
// std::cout << "No backup script for " << service_info.template_name << std::endl;
// return true; // nothing to back up.
// }
// // Check if basic installed stuff is in place.
// std::string remote_service_template_path = remotepath::service_template(mServer, mService);
// std::string remote_command_script_file = remote_service_template_path + "/" + command + ".sh";
// std::string remote_service_config_path = remotepath::service_config(mServer, mService);
// if (!mServerEnv.check_remote_items_exist({
// remotepath::service(mServer, mService),
// remote_command_script_file,
// remotefile::service_env(mServer, mService)})
// )
// {
// 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 = remotepath::backups(mServer);
// if (!silent) std::cout << "Remote backups directory on "<< mServer <<": " << remote_backups_dir << std::endl;
// std::string mkdir_cmd = "mkdir -p " + quote(remote_backups_dir);
// if (!execute_ssh_command(mServerEnv.get_SSH_INFO(), sCommand("",mkdir_cmd, {}), cMode::Silent)) {
// std::cerr << "Failed to create backups directory on server" << std::endl;
// return false;
// }
// // Create backups directory locally if it doesn't exist
// std::string local_backups_dir = gConfig().get_local_backup_path();
// if (local_backups_dir.empty()) {
// std::cerr << "Error: Local backups directory not found" << std::endl;
// std::cerr << "Run 'dropshell edit' to configure DropShell" << 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");
// // Construct backup filename
// shared_commands::cBackupFileName backup_filename_construction(mServer, mService, service_info.template_name);
// if (!backup_filename_construction.is_valid()) {
// std::cerr << "Invalid backup filename" << std::endl;
// return false;
// }
// std::string backup_filename = backup_filename_construction.get_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();
// // assert that the backup filename is valid - -_- appears exactly 3 times in local_backup_file_path.
// ASSERT(3 == count_substring("-_-", local_backup_file_path), "Invalid backup filename");
// { // Run backup script
// shared_commands::cRemoteTempFolder remote_temp_folder(mServerEnv);
// if (!mServerEnv.run_remote_template_command(mService, command, {}, silent, {{"BACKUP_FILE", remote_backup_file_path}, {"TEMP_DIR", remote_temp_folder.path()}})) {
// std::cerr << "Backup script failed on remote server: " << remote_backup_file_path << std::endl;
// return false;
// }
// // Copy backup file from server to local
// if (!scp_file_from_remote(remote_backup_file_path, local_backup_file_path, silent)) {
// std::cerr << "Failed to copy backup file from server" << std::endl;
// return false;
// }
// } // dtor of remote_temp_folder will clean up the temp folder on the server
// if (!silent) {
// std::cout << "Backup created successfully. Restore with:"<<std::endl;
// std::cout << " dropshell restore " << mServer << " " << mService << " " << backup_filename << std::endl;
// }
// return true;
// }
// // Helper function to get the latest backup file for a given server and service
// std::string service_runner::get_latest_backup_file(const std::string& server, const std::string& service) {
// std::string local_backups_dir = gConfig().get_local_backup_path();
// if (local_backups_dir.empty() || !std::filesystem::exists(local_backups_dir)) {
// std::cerr << "Error: Local backups directory not found: " << local_backups_dir << std::endl;
// return "";
// }
// // Get the template name for this service
// LocalServiceInfo info = get_service_info(server, service);
// if (info.template_name.empty()) {
// std::cerr << "Error: Could not determine template name for service: " << service << std::endl;
// return "";
// }
// // Build the expected prefix for backup files
// std::string prefix = server + "-_-" + info.template_name + "-_-" + service + "-_-";
// std::string latest_file;
// std::string latest_datetime;
// std::cout << "Looking for backup files in " << local_backups_dir << std::endl;
// for (const auto& entry : std::filesystem::directory_iterator(local_backups_dir)) {
// if (!entry.is_regular_file()) continue;
// std::string filename = entry.path().filename().string();
// if (filename.rfind(prefix, 0) == 0) { // starts with prefix
// // Extract the datetime part
// size_t dt_start = prefix.size();
// size_t dt_end = filename.find(".tgz", dt_start);
// if (dt_end == std::string::npos) continue;
// std::string datetime = filename.substr(dt_start, dt_end - dt_start);
// std::cout << "Found backup file: " << filename << " with datetime: " << datetime << std::endl;
// if (datetime > latest_datetime) {
// latest_datetime = datetime;
// latest_file = filename;
// }
// }
// }
// if (latest_file.empty()) {
// std::cerr << "Error: No backup files found for " << server << ", " << service << std::endl;
// }
// std::cout << "Latest backup file: " << latest_file << std::endl;
// return latest_file;
// }
// } // namespace dropshell

View File

@ -4,7 +4,7 @@
#include "templates.hpp"
#include "config.hpp"
#include "utils/utils.hpp"
#include "assert.hpp"
#include <libassert/assert.hpp>
#include <iostream>
#include <filesystem>
@ -14,7 +14,7 @@ namespace fs = std::filesystem;
namespace dropshell
{
#pragma TODO : Smart test that the service is fully valid.
#pragma message("TODO : Smart test that the service is fully valid.")
bool SIvalid(const LocalServiceInfo &service_info)
{
return !service_info.service_name.empty() &&
@ -102,7 +102,7 @@ namespace dropshell
auto it = variables.find("TEMPLATE");
if (it == variables.end())
{
error << "Error: TEMPLATE variable not defined in service " << service_name << " on server " << server_name << std::endl;
error << "TEMPLATE variable not defined in service " << service_name << " on server " << server_name << std::endl;
return LocalServiceInfo();
}
service.template_name = it->second;
@ -136,12 +136,13 @@ namespace dropshell
{ // determine if the service template hash matches the template hash.
auto it = variables.find("TEMPLATE_HASH");
if (it == variables.end())
error << "Variable TEMPLATE_HASH not found in the service " << filenames::template_info_env << std::endl;
else
{
error << "TEMPLATE_HASH variable not defined in service " << service_name << " on server " << server_name << std::endl;
return LocalServiceInfo();
uint64_t service_template_hash = std::stoull(it->second);
service.service_template_hash_match = (service_template_hash == tinfo.hash());
//debug << "Service template hash: " << service_template_hash << " == " << tinfo.hash() << std::endl;
}
uint64_t service_template_hash = std::stoull(it->second);
service.service_template_hash_match = (service_template_hash == tinfo.hash());
}
return service;

View File

@ -6,7 +6,7 @@
#include <algorithm>
#include <iomanip>
#include <map>
#include "utils/assert.hpp"
#include <libassert/assert.hpp>
#include "utils/envmanager.hpp"
#include "utils/directories.hpp"

View File

@ -1,13 +0,0 @@
#ifndef ASSERT_HPP
#define ASSERT_HPP
#include "output.hpp"
#define ASSERT(condition, message) \
if (!(condition)) { \
dropshell::error << "Assertion failed: " << message << std::endl; \
std::exit(1); \
}
#endif // ASSERT_HPP

View File

@ -173,7 +173,7 @@ namespace localpath {
std::string remotepath::DROPSHELL_DIR() const
{
return server_config(mServer_name).get_user_dir(mUser);
return ServerConfig(mServer_name).get_user_dir(mUser);
}
std::string remotepath::services() const

View File

@ -6,7 +6,7 @@
#include <string>
#include <cstdlib>
#include <sstream>
#include "utils/assert.hpp"
#include <libassert/assert.hpp>
#include "execute.hpp"
#include "utils/utils.hpp"

View File

@ -147,6 +147,27 @@ void tableprint::set_title(const std::string title) {
this->title = title;
}
// gives the columns to sort by, starting at 0.
void tableprint::sort(std::vector<int> sort_columns)
{
// Skip header row and sort remaining rows
if (rows.size() <= 1) return; // Only header or empty table
// Create a custom comparator that compares rows based on the specified columns
auto comparator = [this, &sort_columns](const std::vector<std::string>& a, const std::vector<std::string>& b) {
for (int col : sort_columns) {
if (col >= 0 && col < a.size() && col < b.size()) {
int cmp = a[col].compare(b[col]);
if (cmp != 0) return cmp < 0;
}
}
return false; // Equal rows maintain original order
};
// Sort rows starting from index 1 (after header)
std::sort(rows.begin() + 1, rows.end(), comparator);
}
void tableprint::add_row(const std::vector<std::string>& row) {
std::vector<std::string> trimmed_row;
for (const auto& cell : row) {

View File

@ -16,6 +16,7 @@ class tableprint {
void add_row(const std::vector<std::string>& row);
void print();
void set_title(const std::string title);
void sort(std::vector<int> sort_columns);
private:
std::vector<std::vector<std::string>> rows;
std::string title;