Compare commits

...

6 Commits

Author SHA1 Message Date
d0152c3ec7 dropshell release 2025.0518.1150
Some checks failed
Dropshell Test / Build_and_Test (push) Has been cancelled
2025-05-18 11:50:23 +12:00
ebb101e381 dropshell release 2025.0518.1145
Some checks failed
Dropshell Test / Build_and_Test (push) Has been cancelled
2025-05-18 11:45:37 +12:00
dc2f694ebe .
Some checks failed
Dropshell Test / Build_and_Test (push) Has been cancelled
2025-05-17 23:12:43 +12:00
038d08e638 dropshell release 2025.0517.2231
Some checks failed
Dropshell Test / Build_and_Test (push) Has been cancelled
2025-05-17 22:31:29 +12:00
27f86e95e7 .
Some checks failed
Dropshell Test / Build_and_Test (push) Has been cancelled
2025-05-17 22:24:25 +12:00
891f0d023f Self-test.
Some checks failed
Dropshell Test / Build_and_Test (push) Has been cancelled
2025-05-17 20:41:17 +12:00
14 changed files with 345 additions and 26 deletions

View File

@ -51,7 +51,6 @@ add_dependencies(dropshell run_createagent)
# Set include directories
# build dir goes first so that we can use the generated version.hpp
target_include_directories(dropshell PRIVATE
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/src>
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/src/autogen>
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/src/utils

7
source/agent/selftest.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash
echo "Running remote agent self-test..."
echo "Completed remote agent self-test."
exit 0

View File

@ -105,6 +105,22 @@ static bool _recreate_file_(const std::filesystem::path& outpath, uint64_t file_
bool recreate_tree(std::string destination_folder) {
namespace fs = std::filesystem;
bool any_written = false;
{
// File: selftest.sh
fs::path outpath = fs::path(destination_folder) / "selftest.sh";
static const char filedata_base64[] = "IyEvYmluL2Jhc2gKCmVjaG8gIlJ1bm5pbmcgcmVtb3RlIGFnZW50IHNlbGYtdGVzdC4uLiIKCmVj"\
"aG8gIkNvbXBsZXRlZCByZW1vdGUgYWdlbnQgc2VsZi10ZXN0LiIKCmV4aXQgMAo=";
// Decode Base64 data
size_t decoded_size = (strlen(filedata_base64) * 3) / 4;
unsigned char* decoded_data = new unsigned char[decoded_size];
size_t actual_size;
base64_decode(filedata_base64, strlen(filedata_base64), decoded_data, &actual_size);
bool file_written = _recreate_file_(outpath, 11594895391899191874ULL, std::filesystem::perms(493), decoded_data, actual_size);
delete[] decoded_data;
any_written = any_written || file_written;
}
{
// File: datacommands.sh
fs::path outpath = fs::path(destination_folder) / "datacommands.sh";

View File

@ -0,0 +1,11 @@
// version.hpp (dummy for linter/IntelliSense)
#pragma once
#include <string>
namespace dropshell {
extern const std::string VERSION;
extern const std::string RELEASE_DATE;
extern const std::string AUTHOR;
extern const std::string LICENSE;
}

View File

@ -17,9 +17,9 @@ namespace dropshell
static std::vector<std::string> create_service_name_list = {"create-service"};
// Static registration
struct UninstallCommandRegister
struct CreateServiceCommandRegister
{
UninstallCommandRegister()
CreateServiceCommandRegister()
{
CommandRegistry::instance().register_command({create_service_name_list,
create_service_handler,

View File

@ -57,10 +57,13 @@ void help_autocomplete(const CommandContext& ctx) {
void show_command(const std::string& cmd) {
const auto& cmd_info = CommandRegistry::instance().find_command(cmd);
if (!cmd_info)
{
std::cout << "Unknown command: " << cmd << std::endl;
return;
}
std::cout << " ";
print_left_aligned(cmd_info->help_usage, 30);
print_left_aligned(cmd_info->help_usage, 32);
std::cout << cmd_info->help_description << std::endl;
}
@ -119,6 +122,13 @@ int help_handler(const CommandContext& ctx) {
{
// show more!
show_command("list");
std::cout << std::endl;
show_command("install");
show_command("uninstall");
show_command("nuke");
std::cout << std::endl;
show_command("start");
show_command("stop");
}
return 0;
}

View File

@ -35,8 +35,8 @@ namespace dropshell
false, // requires_install
0, // min_args (after command)
2, // max_args (after command)
"install SERVER [SERVICE|all]",
"Install/reinstall service(s). Safe/non-destructive way to update.",
"install [SERVER] [SERVICE|all]",
"Install/reinstall host, remote servers, or service(s). Safe/non-destructive way to update.",
// heredoc
R"(
Install components on a server. This is safe to re-run (non-destructive) and used to update
@ -121,6 +121,7 @@ namespace dropshell
// Run install script
{
std::cout << "Running " << service_info.template_name << " install script on " << server << "..." << std::endl;
server_env.run_remote_template_command(service, "install", {}, silent, {});
}
@ -298,10 +299,12 @@ namespace dropshell
// now create the agent.
// copy across from the local agent files.
std::cout << "Copying local agent files to remote server... " << std::flush;
shared_commands::rsync_tree_to_remote(localpath::files_for_remote_agent(), agent_path, server_env, false);
std::cout << "done." << std::endl;
// add in bb64. We can't use execute_remote_command() here, as that relies on bb64 which we're installing!
std::cout << "Installing bb64 on " << server << std::endl << std::flush;
std::cout << "Installing bb64 on " << server << "..." << std::endl << std::flush;
std::string remote_cmd =
"ssh -p " + server_env.get_SSH_INFO().port + " " + server_env.get_SSH_INFO().user + "@" + server_env.get_SSH_INFO().host +
@ -315,11 +318,14 @@ namespace dropshell
std::cout << "Downloaded bb64 to " << agent_path << " on remote server." << std::endl;
// just test all is ok
// run the self-test.
std::string output;
bool okay = execute_ssh_command(server_env.get_SSH_INFO(), sCommand(agent_path, "./bb64 -i VGhlIGRyb3BzaGVsbCByZW1vdGUgYWdlbnQgaXMgY29ycmVjdGx5IGluc3RhbGxlZC4=", {}), cMode::CaptureOutput, &output);
bool okay = execute_ssh_command(server_env.get_SSH_INFO(), sCommand(agent_path, "./selftest.sh", {}), cMode::Defaults, &output);
if (!okay)
{
std::cerr << "Failed to install bb64 on " << server << std::endl;
std::cerr << "ERROR: Failed to install remote agent on " << server << std::endl;
std::cerr << "ERROR: Output: " << output << std::endl;
return 1;
}

View File

@ -7,6 +7,7 @@
#include "tableprint.hpp"
#include "transwarp.hpp"
#include "server_env_manager.hpp"
#include "services.hpp"
#include <unistd.h>
#include <cstring>
@ -161,7 +162,7 @@ void show_server_details(const std::string& server_name) {
// list services, and run healthcheck on each
{
tableprint tp("Services: " + server_name, false);
tp.add_row({"Status", "Service", "Ports"});
tp.add_row({"Status", "Service", "Template","Ports"});
std::map<std::string, shared_commands::ServiceStatus> status = shared_commands::get_all_services_status(server_name);
@ -175,7 +176,7 @@ void show_server_details(const std::string& server_name) {
for (const auto& port : service_status.ports)
ports_str += std::to_string(port) + " ";
tp.add_row({healthy, service_name, ports_str});
tp.add_row({healthy, service_name, get_service_info(server_name,service_name).template_name, ports_str});
} // end of for (const auto& service : services)
tp.print();
} // end of list services

View File

@ -10,8 +10,6 @@
#include "utils/assert.hpp"
#pragma message ("TODO: Fix issues with Nuke below.")
namespace dropshell {
int nuke_handler(const CommandContext& ctx);
@ -33,9 +31,16 @@ struct NukeCommandRegister {
"Nuke a service on a server. Destroys everything, both local and remote!",
// heredoc
R"(
Nuke a service on a server. Destroys everything, both local and remote!
Nuke a service.
Examples:
nuke SERVER SERVICE nuke the given service on the given server.
nuke SERVER all nuke all services on the given server.
Note: This command is destructive and will destroy all data and all configuration,
both on the dropshell host and on the remote server.
Use with caution!
)"
});
}
@ -60,11 +65,14 @@ int nuke_one(std::string server, std::string service)
// otherwise just uninstall.
if (gTemplateManager().template_command_exists(service_info.template_name, "nuke"))
{
std::cout << "Running nuke script for " << service << " on " << server << std::endl;
if (!server_env.run_remote_template_command(service, "nuke", {}, false, {}))
std::cerr << "Warning: Failed to run nuke script: " << service << std::endl;
}
else
{
std::cout << "No nuke script found for " << service << " on " << server << std::endl;
std::cout << "Running uninstall script instead and will clean directories." << std::endl;
if (!server_env.run_remote_template_command(service, "uninstall", {}, false, {}))
std::cerr << "Warning: Failed to uninstall service: " << service << std::endl;
}

View File

@ -0,0 +1,81 @@
#include "command_registry.hpp"
#include "config.hpp"
#include "utils/utils.hpp"
#include "utils/directories.hpp"
#include "shared_commands.hpp"
#include "server_env_manager.hpp"
#include "services.hpp"
#include "servers.hpp"
namespace dropshell
{
int ssh_handler(const CommandContext &ctx);
static std::vector<std::string> ssh_name_list = {"ssh"};
// Static registration
struct SSHCommandRegister
{
SSHCommandRegister()
{
CommandRegistry::instance().register_command({ssh_name_list,
ssh_handler,
shared_commands::std_autocomplete,
false, // hidden
true, // requires_config
true, // requires_install
1, // min_args (after command)
2, // max_args (after command)
"ssh SERVER",
"SSH into a server, or into a docker container for a service.",
R"(
ssh SERVER SERVICE SSH into a docker container for a service.
ssh SERVER SSH into a server.
)"});
}
} ssh_command_register;
bool ssh_into_server(const std::string &server)
{
server_env_manager server_env(server);
if (!server_env.is_valid())
{
std::cerr << "Error: Server " << server << " is not valid" << std::endl;
return false;
}
execute_ssh_command(server_env.get_SSH_INFO(), sCommand(remotepath::DROPSHELL_DIR(server), "ls --color && bash", {}), cMode::Interactive);
return true;
}
bool ssh_into_service(const std::string &server, const std::string &service)
{
return true;
}
int ssh_handler(const CommandContext &ctx)
{
if (ctx.args.size() < 1)
{
std::cerr << "Error: Server name is required" << std::endl;
return 1;
}
std::string server = safearg(ctx.args, 0);
if (ctx.args.size() < 2)
{
// ssh into the server
return ssh_into_server(server) ? 0 : 1;
}
std::string service = safearg(ctx.args, 1);
// ssh into the specific service.
return ssh_into_service(server, service) ? 0 : 1;
}
} // namespace dropshell

View File

@ -0,0 +1,91 @@
#include "command_registry.hpp"
#include "config.hpp"
#include "utils/utils.hpp"
#include "utils/directories.hpp"
#include "shared_commands.hpp"
#include "server_env_manager.hpp"
#include "services.hpp"
#include "servers.hpp"
namespace dropshell
{
int start_handler(const CommandContext &ctx);
static std::vector<std::string> start_name_list = {"start", "start-service"};
// Static registration
struct StartCommandRegister
{
StartCommandRegister()
{
CommandRegistry::instance().register_command({start_name_list,
start_handler,
shared_commands::std_autocomplete_allowall,
false, // hidden
true, // requires_config
true, // requires_install
1, // min_args (after command)
2, // max_args (after command)
"start SERVER SERVICE|all",
"Start a service or all services on a server.",
R"(
start SERVER SERVICE Starts the given service on the given server.
start SERVER all Starts all services on the given server.
Note: This command will not create any data or configuration.
It will simply start the service on the remote server.
Stop the service with stop, or uninstall with uninstall.
)"});
}
} start_command_register;
bool start_service(const std::string &server, const std::string &service)
{
server_env_manager server_env(server);
if (!server_env.is_valid())
{
std::cerr << "Error: Server " << server << " is not valid" << std::endl;
return false;
}
// run the start script.
bool started = server_env.run_remote_template_command(service, "start", {}, false, {});
if (started)
{
std::cout << "Service " << service << " on server " << server << " started." << std::endl;
return true;
}
std::cerr << "Error: Failed to start service " << service << " on server " << server << std::endl;
return false;
}
int start_handler(const CommandContext &ctx)
{
if (ctx.args.size() < 2)
{
std::cerr << "Error: Server name and service name are both required" << std::endl;
return 1;
}
std::string server = safearg(ctx.args, 0);
std::string service = safearg(ctx.args, 1);
if (service == "all")
{
// install all services on the server
maketitle("Stopping all services on " + server);
bool okay = true;
std::vector<LocalServiceInfo> services = get_server_services_info(server);
for (const auto &service : services)
okay &= start_service(server, service.service_name);
return okay ? 0 : 1;
}
// start the specific service.
return start_service(server, service) ? 0 : 1;
}
} // namespace dropshell

View File

@ -0,0 +1,91 @@
#include "command_registry.hpp"
#include "config.hpp"
#include "utils/utils.hpp"
#include "utils/directories.hpp"
#include "shared_commands.hpp"
#include "server_env_manager.hpp"
#include "services.hpp"
#include "servers.hpp"
namespace dropshell
{
int stop_handler(const CommandContext &ctx);
static std::vector<std::string> stop_name_list = {"stop", "stop-service"};
// Static registration
struct StopCommandRegister
{
StopCommandRegister()
{
CommandRegistry::instance().register_command({stop_name_list,
stop_handler,
shared_commands::std_autocomplete_allowall,
false, // hidden
true, // requires_config
true, // requires_install
1, // min_args (after command)
2, // max_args (after command)
"stop SERVER SERVICE|all",
"Stop a service or all services on a server.",
R"(
stop SERVER SERVICE Stops the given service on the given server.
stop SERVER all Stops all services on the given server.
Note: This command will not destroy any data or configuration.
It will simply stop the service on the remote server.
Restart the service with start, or update and start it with install.
)"});
}
} stop_command_register;
bool stop_service(const std::string &server, const std::string &service)
{
server_env_manager server_env(server);
if (!server_env.is_valid())
{
std::cerr << "Error: Server " << server << " is not valid" << std::endl;
return false;
}
// run the stop script.
bool stopped = server_env.run_remote_template_command(service, "stop", {}, false, {});
if (stopped)
{
std::cout << "Service " << service << " on server " << server << " stopped." << std::endl;
return true;
}
std::cerr << "Error: Failed to stop service " << service << " on server " << server << std::endl;
return false;
}
int stop_handler(const CommandContext &ctx)
{
if (ctx.args.size() < 2)
{
std::cerr << "Error: Server name and service name are both required" << std::endl;
return 1;
}
std::string server = safearg(ctx.args, 0);
std::string service = safearg(ctx.args, 1);
if (service == "all")
{
// install all services on the server
maketitle("Stopping all services on " + server);
bool okay = true;
std::vector<LocalServiceInfo> services = get_server_services_info(server);
for (const auto &service : services)
okay &= stop_service(server, service.service_name);
return okay ? 0 : 1;
}
// stop the specific service.
return stop_service(server, service) ? 0 : 1;
}
} // namespace dropshell

View File

@ -23,25 +23,25 @@ namespace dropshell
uninstall_handler,
shared_commands::std_autocomplete_allowall,
false, // hidden
true, // requires_config
true, // requires_install
true, // requires_config
true, // requires_install
2, // min_args (after command)
2, // max_args (after command)
"uninstall SERVER SERVICE|all",
"Uninstall a service on a server. Does not remove configuration or user data.",
// heredoc
R"(
Uninstall a service on a server. Does not remove configuration or user data.
uninstall SERVER SERVICE uninstall the given service on the given server.
uninstall SERVER all uninstall all services on the given server.
Uninstall a service, leaving all configuration and data intact.
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.
)"});
}
} uninstall_command_register;
bool uninstall_service(const std::string &server, const std::string &service, bool silent=false)
bool uninstall_service(const std::string &server, const std::string &service, bool silent = false)
{
if (!silent)
maketitle("Uninstalling " + service + " on " + server);
@ -81,7 +81,6 @@ namespace dropshell
return true;
}
int uninstall_handler(const CommandContext &ctx)
{
if (ctx.args.size() < 1)
@ -109,5 +108,4 @@ namespace dropshell
return uninstall_service(server, service) ? 0 : 1;
}
} // namespace dropshell

View File

@ -204,7 +204,7 @@ bool server_env_manager::run_remote_template_command(const std::string &service_
if (scommand->get_command_to_run().empty())
return false;
cMode mode = (command=="ssh") ? (cMode::Interactive) : cMode::Silent;
cMode mode = (command=="ssh") ? (cMode::Interactive) : (silent ? cMode::Silent : cMode::Defaults);
return execute_ssh_command(get_SSH_INFO(), scommand.value(), mode);
}