Compare commits

..

7 Commits

Author SHA1 Message Date
9a141685de Update test.
Some checks failed
Dropshell Test / Build_and_Test (push) Failing after 12s
2025-05-24 21:27:02 +12:00
9c94510213 .
Some checks failed
Dropshell Test / Build_and_Test (push) Failing after 12s
2025-05-24 20:44:21 +12:00
bcc78859fc dropshell release 2025.0524.2033
Some checks failed
Dropshell Test / Build_and_Test (push) Failing after 11s
2025-05-24 20:33:55 +12:00
a5243a7e79 Install is broken - putting the wrong template on.
Some checks failed
Dropshell Test / Build_and_Test (push) Has been cancelled
2025-05-24 20:05:00 +12:00
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
33 changed files with 304 additions and 721 deletions

View File

@ -14,10 +14,10 @@ jobs:
uses: actions/checkout@v4
- name: Build
run: |
cd ${{ gitea.workspace }}/docker
./compile.sh
cd ${{ gitea.workspace }}/source
./multibuild.sh
- name: Test
run: |
cd ${{ gitea.workspace }}/docker/output
cd ${{ gitea.workspace }}/source/output
./dropshell_x86_64 list
./dropshell_x86_64 help

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

@ -20,6 +20,15 @@ void CommandRegistry::register_command(const CommandInfo& info) {
const CommandInfo* CommandRegistry::find_command(const std::string& name) const {
auto it = command_map_.find(name);
if (it != command_map_.end()) return it->second.get();
// go deep now.
for (const auto& cmd : all_commands_) {
if (cmd->names.size() > 0) {
for (const auto& altname : cmd->names) {
if (name == altname) return cmd.get();
}
}
}
return nullptr;
}

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;
@ -194,10 +194,10 @@ namespace dropshell
info << "Setting SSH_USER to " << sshuser << " in the " << filenames::service_env << " file" << std::endl;
{ // edit the service.env file to set the SSH_USER.
std::string template_service_env_file = tinfo.local_template_path() / "config" / filenames::service_env;
ASSERT(std::filesystem::exists(template_service_env_file), "Template service env file not found: " + template_service_env_file);
std::ifstream template_service_env_file_in(template_service_env_file);
std::ofstream service_env_file_out(localfile::template_info_env(server_name, service_name));
std::string source_service_env = tinfo.local_template_path() / "config" / filenames::service_env;
ASSERT(std::filesystem::exists(source_service_env), "Template service env file not found: " + source_service_env);
std::ifstream template_service_env_file_in(source_service_env);
std::ofstream service_env_file_out(localfile::service_env(server_name, service_name));
std::string line;
while (std::getline(template_service_env_file_in, line))
{

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", "nuke", "nuke-service","erase","destroy-service"};
// 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,42 +45,64 @@ 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())
{
std::string user = server_env.get_user_for_service(service); // returns empty string if no user found.
if (user.empty())
{
warning << "No user found for service " << service << " on " << server << std::endl;
for (auto sshuser : server_env.get_users())
{
if (server_env.check_remote_dir_exists(remotepath(server, sshuser.user).service(service), sshuser.user))
{
info << "Found a remote service directory here: " << remotepath(server, sshuser.user).service(service) << std::endl;
info << "Deleting it as user " << sshuser.user << std::endl;
user = sshuser.user;
break;
}
}
}
if (user.empty())
warning << "No remote service directory found for " << service << " on " << server << std::endl;
else
{ // user is not empty.
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 << "No valid service definition found for " << 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))
{
// 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;
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 nuke script found for " << service << " on " << server << std::endl;
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.
if (server_env.remove_remote_dir(remotepath(server, user).service(service), true, user))
@ -92,33 +114,36 @@ namespace dropshell
warning << "Failed to remove remote service directory" << std::endl;
}
else
warning << "Service not found on remote server: " << remotepath(server, user).service(service) << std::endl;
}
warning << "No remote service directory found for " << service << " on "<< server << std::endl;
} // user is not empty.
} // server_env is valid.
else
warning << "Can't nuke the remote service as the server is invalid: " << server << std::endl;
error << "No valid local server information for server " << server << std::endl;
// step 2 - nuke the local service directory.
// step 2 - destroy the local service directory, if it exists.
std::string local_service_path = localpath::service(server, service);
if (local_service_path.empty() || !std::filesystem::exists(local_service_path))
{
warning << "Local service directory not found: " << local_service_path << std::endl;
warning << "No local service directory found for " << service << " on " << server << std::endl;
}
else
{
auto itemsdeleted = std::filesystem::remove_all(local_service_path);
if (itemsdeleted == 0)
error << "Failed to remove local service directory" << std::endl;
else
info << "Local service directory removed: " << local_service_path << std::endl;
}
info << "Nuked service " << service << " on server " << server << std::endl;
info << "Finished destroying 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 +166,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,7 +63,7 @@ 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);
@ -271,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);
@ -321,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);
@ -364,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::set<int> ports_used;
std::string serviceticks = "";
std::string ports_used_str = "";
std::set<int> ports_used;
std::map<std::string, shared_commands::ServiceStatus> status = shared_commands::get_all_services_status(sup.server.get_server_name(),sup.user.user);
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) + " ";
tp.add_row({(first ? server.get_server_name() : ""), (first ? server.get_SSH_HOST() : ""), user.user, serviceticks, ports_used_str});
first = false;
}
// 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;
@ -105,6 +105,16 @@ namespace dropshell
return 1;
}
if (!gTemplateManager().template_command_exists(service_info.template_name, "backup") ||
!gTemplateManager().template_command_exists(service_info.template_name, "restore"))
{
info << service << " has no data to restore" << std::endl;
debug << "(no backup or restore script for " << service_info.template_name << ")" << std::endl;
return 0; // nothing to back up.
}
std::optional<shared_commands::cBackupFileName> backup_details;
if (backup_arg == "latest")
{ // special case.
@ -175,9 +185,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 +199,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,20 +194,38 @@ 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
{
if (user.empty())
{
debug << "Can't check remote directory exists for " << dir_path << " as user is empty" << std::endl;
return false;
}
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
{
if (user.empty())
{
debug << "Can't check remote file exists for " << file_path << " as user is empty" << std::endl;
return false;
}
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
{
if (user.empty())
{
debug << "Can't check remote items exist as user is empty" << std::endl;
return false;
}
// convert file_paths to a single string, separated by spaces
std::string file_paths_str;
std::string file_names_str;
@ -229,7 +247,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 +276,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 +298,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 +318,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 +328,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 +364,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 +385,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 +448,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>
@ -89,7 +89,7 @@ namespace dropshell
// check the service directory exists.
if (!fs::exists(service.local_service_path))
{
std::cerr << "Error: Service directory not found: " << service.local_service_path << std::endl;
warning << "Service directory not found: " << service.local_service_path << std::endl;
return LocalServiceInfo();
}

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;