Making install work.
Some checks failed
Dropshell Test / Build_and_Test (push) Has been cancelled

This commit is contained in:
Your Name
2025-05-13 20:16:39 +12:00
parent 5201e92cb3
commit b260c9813e
23 changed files with 224 additions and 234 deletions

View File

@@ -1,5 +1,6 @@
#include "command_registry.hpp"
#include "utils/utils.hpp"
#include "config.hpp"
namespace dropshell {
@@ -37,7 +38,13 @@ std::vector<std::string> CommandRegistry::list_primary_commands(bool include_hid
for (const auto& cmd : all_commands_) {
if (!cmd->hidden || include_hidden) {
if (cmd->names.size() > 0)
out.insert(cmd->names[0]);
{
if (cmd->requires_config && !gConfig().is_config_set())
continue;
if (cmd->requires_install && !gConfig().is_agent_installed())
continue;
out.insert(cmd->names[0]);
}
}
}
return std::vector<std::string>(out.begin(), out.end());

View File

@@ -24,7 +24,8 @@ struct CommandInfo {
std::function<int(const CommandContext&)> handler;
std::function<void(const CommandContext&)> autocomplete; // optional
bool hidden = false;
bool requires_config = false;
bool requires_config = true;
bool requires_install = true;
int min_args = 0;
int max_args = -1; // -1 = unlimited
std::string help_usage; // install SERVER [SERVICE]

View File

@@ -28,6 +28,7 @@ struct HelpCommandRegister {
help_autocomplete,
false, // hidden
false, // requires_config
false, // requires_install
0, // min_args (after command)
1, // max_args (after command)
"help [COMMAND]",

View File

@@ -12,151 +12,178 @@
#include <filesystem>
#include <libassert/assert.hpp>
namespace dropshell {
namespace dropshell
{
int install_handler(const CommandContext& ctx);
int install_handler(const CommandContext &ctx);
static std::vector<std::string> install_name_list={"install"};
static std::vector<std::string> install_name_list = {"install"};
// Static registration
struct InstallCommandRegister {
InstallCommandRegister() {
CommandRegistry::instance().register_command({
install_name_list,
install_handler,
std_autocomplete,
false, // hidden
false, // requires_config
1, // min_args (after command)
2, // max_args (after command)
"install SERVER [SERVICE]",
"Install/reinstall/update service(s). Safe/non-destructive.",
// heredoc
R"(
Install a service(s) on a server.
install <server> <service> (re)install the given service on the given server.
install <server> (re)install all configured services on the given server.
// Static registration
struct InstallCommandRegister
{
InstallCommandRegister()
{
CommandRegistry::instance().register_command({install_name_list,
install_handler,
std_autocomplete_allowallservices,
false, // hidden
false, // requires_config
false, // requires_install
1, // min_args (after command)
2, // max_args (after command)
"install SERVER [SERVICE]",
"Install/reinstall/update service(s). Safe/non-destructive.",
// heredoc
R"(
Install components on a server. This is safe to re-run (non-destructive) and used to update
servers and their services.
install (re)install dropshell components on this computer.
install SERVER (re)install dropshell agent on the given server.
install SERVER [SERVICE|*] (re)install the given service (or all services) on the given server.
Note you need to create the service first with:
dropshell create-service <server> <template> <service>
)"
});
}
} install_command_register;
// ------------------------------------------------------------------------------------------------
// rsync_tree_to_remote : SHARED COMMAND
// ------------------------------------------------------------------------------------------------
bool rsync_tree_to_remote(
const std::string &local_path,
const std::string &remote_path,
server_env_manager &server_env,
bool silent)
{
ASSERT(!local_path.empty() && !remote_path.empty());
std::string rsync_cmd = "rsync --delete --mkpath -zrpc -e 'ssh -p " + server_env.get_SSH_PORT() + "' " +
quote(local_path + "/") + " "+
quote(server_env.get_SSH_USER() + "@" + server_env.get_SSH_HOST() + ":" +
remote_path + "/");
return execute_local_command(rsync_cmd, nullptr, (silent ? cMode::Silent : cMode::None) );
}
// ------------------------------------------------------------------------------------------------
// install service over ssh : SHARED COMMAND
// ------------------------------------------------------------------------------------------------
bool install_service(const std::string& server, const std::string& service, bool silent) {
LocalServiceInfo service_info = get_service_info(server, service);
if (!SIvalid(service_info))
return false;
server_env_manager server_env(server);
if (!server_env.is_valid())
return false;
maketitle("Installing " + service + " (" + service_info.template_name + ") on " + server);
if (!server_env.is_valid()) return false; // should never hit this.
// Check if template exists
template_info tinfo = gTemplateManager().get_template_info(service_info.template_name);
if (!tinfo.is_set())
return false;
// Create service directory
std::string remote_service_path = remotepath::service(server, service);
std::string mkdir_cmd = "mkdir -p " + quote(remote_service_path);
if (!execute_ssh_command(server_env.get_SSH_INFO(), sCommand("", mkdir_cmd, {}), cMode::Silent))
{
std::cerr << "Failed to create service directory " << remote_service_path << std::endl;
return false;
}
// Check if rsync is installed on remote host
std::string check_rsync_cmd = "which rsync";
if (!execute_ssh_command(server_env.get_SSH_INFO(), sCommand("", check_rsync_cmd, {}), cMode::Silent))
{
std::cerr << "rsync is not installed on the remote host" << std::endl;
return false;
}
// Copy template files
std::cout << "Copying: [LOCAL] " << tinfo.local_template_path() << std::endl << std::string(8,' ')<<"[REMOTE] " << remotepath::service_template(server, service) << "/" << std::endl;
if (!rsync_tree_to_remote(tinfo.local_template_path().string(), remotepath::service_template(server, service),
server_env, silent))
{
std::cerr << "Failed to copy template files using rsync" << std::endl;
return false;
}
// Copy service files
std::cout << "Copying: [LOCAL] " << localpath::service(server,service) << std::endl << std::string(8,' ')<<"[REMOTE] " << remotepath::service_config(server,service) << std::endl;
if (!rsync_tree_to_remote(localpath::service(server,service), remotepath::service_config(server,service),
server_env, silent))
{
std::cerr << "Failed to copy service files using rsync" << std::endl;
return false;
}
// Run install script
{
server_env.run_remote_template_command(service, "install", {}, silent, {});
}
// print health tick
std::cout << "Health: " << healthtick(server,service) << std::endl;
return true;
}
// ------------------------------------------------------------------------------------------------
// install command implementation
// ------------------------------------------------------------------------------------------------
int install_handler(const CommandContext& ctx) {
if (ctx.args.size() < 1) {
std::cerr << "Error: install requires a server and (optionally) a service" << std::endl;
return 1;
}
std::string server = safearg(ctx.args,0);
if (ctx.args.size() == 1) {
// install all services on the server
bool okay=true;
std::vector<LocalServiceInfo> services = get_server_services_info(server);
for (const auto& service : services) {
if (!install_service(server, service.service_name, false))
okay=false;
)"});
}
return okay ? 0 : 1;
} install_command_register;
// ------------------------------------------------------------------------------------------------
// rsync_tree_to_remote : SHARED COMMAND
// ------------------------------------------------------------------------------------------------
bool rsync_tree_to_remote(
const std::string &local_path,
const std::string &remote_path,
server_env_manager &server_env,
bool silent)
{
ASSERT(!local_path.empty() && !remote_path.empty());
std::string rsync_cmd = "rsync --delete --mkpath -zrpc -e 'ssh -p " + server_env.get_SSH_PORT() + "' " +
quote(local_path + "/") + " " +
quote(server_env.get_SSH_USER() + "@" + server_env.get_SSH_HOST() + ":" +
remote_path + "/");
return execute_local_command(rsync_cmd, nullptr, (silent ? cMode::Silent : cMode::None));
}
std::string service = safearg(ctx.args,1);
return install_service(server, service, false) ? 0 : 1;
}
// ------------------------------------------------------------------------------------------------
// install service over ssh : SHARED COMMAND
// ------------------------------------------------------------------------------------------------
bool install_service(const std::string &server, const std::string &service, bool silent)
{
LocalServiceInfo service_info = get_service_info(server, service);
if (!SIvalid(service_info))
return false;
server_env_manager server_env(server);
if (!server_env.is_valid())
return false;
maketitle("Installing " + service + " (" + service_info.template_name + ") on " + server);
if (!server_env.is_valid())
return false; // should never hit this.
// Check if template exists
template_info tinfo = gTemplateManager().get_template_info(service_info.template_name);
if (!tinfo.is_set())
return false;
// Create service directory
std::string remote_service_path = remotepath::service(server, service);
std::string mkdir_cmd = "mkdir -p " + quote(remote_service_path);
if (!execute_ssh_command(server_env.get_SSH_INFO(), sCommand("", mkdir_cmd, {}), cMode::Silent))
{
std::cerr << "Failed to create service directory " << remote_service_path << std::endl;
return false;
}
// Check if rsync is installed on remote host
std::string check_rsync_cmd = "which rsync";
if (!execute_ssh_command(server_env.get_SSH_INFO(), sCommand("", check_rsync_cmd, {}), cMode::Silent))
{
std::cerr << "rsync is not installed on the remote host" << std::endl;
return false;
}
// Copy template files
std::cout << "Copying: [LOCAL] " << tinfo.local_template_path() << std::endl
<< std::string(8, ' ') << "[REMOTE] " << remotepath::service_template(server, service) << "/" << std::endl;
if (!rsync_tree_to_remote(tinfo.local_template_path().string(), remotepath::service_template(server, service),
server_env, silent))
{
std::cerr << "Failed to copy template files using rsync" << std::endl;
return false;
}
// Copy service files
std::cout << "Copying: [LOCAL] " << localpath::service(server, service) << std::endl
<< std::string(8, ' ') << "[REMOTE] " << remotepath::service_config(server, service) << std::endl;
if (!rsync_tree_to_remote(localpath::service(server, service), remotepath::service_config(server, service),
server_env, silent))
{
std::cerr << "Failed to copy service files using rsync" << std::endl;
return false;
}
// Run install script
{
server_env.run_remote_template_command(service, "install", {}, silent, {});
}
// print health tick
std::cout << "Health: " << healthtick(server, service) << std::endl;
return true;
}
int install_host()
{
// install the local dropshell agent.
}
int install_server(const std::string &server)
{
// install the dropshell agent on the given server.
}
// ------------------------------------------------------------------------------------------------
// install command implementation
// ------------------------------------------------------------------------------------------------
int install_handler(const CommandContext &ctx)
{
if (ctx.args.size() < 1)
{ // install host
return install_host();
}
std::string server = safearg(ctx.args, 0);
if (ctx.args.size() == 1)
{ // install server
return install_server(server);
}
// install service(s)
if (safearg(ctx.args, 1) == "*")
{
// install all services on the server
bool okay = true;
std::vector<LocalServiceInfo> services = get_server_services_info(server);
for (const auto &service : services)
{
if (!install_service(server, service.service_name, false))
okay = false;
}
return okay ? 0 : 1;
}
else
{ // install the specific service.
std::string service = safearg(ctx.args, 1);
return install_service(server, service, false) ? 0 : 1;
}
}
} // namespace dropshell

View File

@@ -23,6 +23,7 @@ namespace dropshell {
// defined in standard_autocomplete.cpp
void std_autocomplete(const CommandContext& ctx);
void std_autocomplete_allowallservices(const CommandContext& ctx);
} // namespace dropshell
#endif

View File

@@ -10,7 +10,7 @@ namespace dropshell {
void std_autocomplete(const CommandContext &ctx)
{
if (ctx.args.size() == 0) { // just the command health.
if (ctx.args.size() == 0) { // just the command, no args yet.
// list servers
std::vector<ServerInfo> servers = get_configured_servers();
for (const auto& server : servers) {
@@ -26,4 +26,11 @@ void std_autocomplete(const CommandContext &ctx)
}
}
void std_autocomplete_allowallservices(const CommandContext &ctx)
{
std_autocomplete(ctx);
if (ctx.args.size() == 1)
std::cout << "*" << std::endl;
}
} // namespace dropshell