|
|
|
@@ -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
|