Merge branch 'main' of https://gitea.jde.nz/j/dropshell
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 30s
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 30s
This commit is contained in:
commit
1f72141e13
@ -9,12 +9,12 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
void dropshell::autocomplete(const std::vector<std::string> &args)
|
bool dropshell::autocomplete(const std::vector<std::string> &args)
|
||||||
{
|
{
|
||||||
if (args.size() < 3) // dropshell autocomplete ???
|
if (args.size() < 3) // dropshell autocomplete ???
|
||||||
{
|
{
|
||||||
autocomplete_list_commands();
|
autocomplete_list_commands();
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(args.size() >= 3);
|
ASSERT(args.size() >= 3);
|
||||||
@ -27,23 +27,23 @@ void dropshell::autocomplete(const std::vector<std::string> &args)
|
|||||||
std::filesystem::directory_iterator dir_iter(std::filesystem::current_path());
|
std::filesystem::directory_iterator dir_iter(std::filesystem::current_path());
|
||||||
for (const auto& entry : dir_iter)
|
for (const auto& entry : dir_iter)
|
||||||
std::cout << entry.path().filename().string() << std::endl;
|
std::cout << entry.path().filename().string() << std::endl;
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string noargcmds[] = {"templates","autocomplete_list_servers","autocomplete_list_services","autocomplete_list_commands"};
|
std::string noargcmds[] = {"templates","autocomplete_list_servers","autocomplete_list_services","autocomplete_list_commands"};
|
||||||
if (std::find(std::begin(noargcmds), std::end(noargcmds), cmd) != std::end(noargcmds))
|
if (std::find(std::begin(noargcmds), std::end(noargcmds), cmd) != std::end(noargcmds))
|
||||||
return;
|
return true;
|
||||||
|
|
||||||
if (!dropshell::gConfig().is_config_set())
|
if (!dropshell::gConfig().is_config_set())
|
||||||
return; // can't help without working config.
|
return false; // can't help without working config.
|
||||||
|
|
||||||
if (args.size()==3) // we have the command but nothing else. dropshell autocomplete command <server>
|
if (args.size()==3) // we have the command but nothing else. dropshell autocomplete command <server>
|
||||||
{
|
{
|
||||||
auto servers = dropshell::get_configured_servers();
|
auto servers = dropshell::get_configured_servers();
|
||||||
for (const auto& server : servers)
|
for (const auto& server : servers)
|
||||||
std::cout << server.name << std::endl;
|
std::cout << server.name << std::endl;
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.size()==4) // we have the command and the server. dropshell autocomplete command server <service>
|
if (args.size()==4) // we have the command and the server. dropshell autocomplete command server <service>
|
||||||
@ -55,13 +55,13 @@ void dropshell::autocomplete(const std::vector<std::string> &args)
|
|||||||
auto templates = dropshell::gTemplateManager().get_template_list();
|
auto templates = dropshell::gTemplateManager().get_template_list();
|
||||||
for (const auto& t : templates)
|
for (const auto& t : templates)
|
||||||
std::cout << t << std::endl;
|
std::cout << t << std::endl;
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto services = dropshell::get_server_services_info(server);
|
auto services = dropshell::get_server_services_info(server);
|
||||||
for (const auto& service : services)
|
for (const auto& service : services)
|
||||||
std::cout << service.service_name << std::endl;
|
std::cout << service.service_name << std::endl;
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.size()==5) // we have the command and the server and the service. dropshell autocomplete command server service_name <command?>
|
if (args.size()==5) // we have the command and the server and the service. dropshell autocomplete command server service_name <command?>
|
||||||
@ -73,17 +73,17 @@ void dropshell::autocomplete(const std::vector<std::string> &args)
|
|||||||
std::set<std::string> backups = dropshell::list_backups(server_name, service_name);
|
std::set<std::string> backups = dropshell::list_backups(server_name, service_name);
|
||||||
for (auto backup : backups)
|
for (auto backup : backups)
|
||||||
std::cout << backup << std::endl;
|
std::cout << backup << std::endl;
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return; // no more autocompletion possible - don't know what the argument is for.
|
return false; // no more autocompletion possible - don't know what the argument is for.
|
||||||
}
|
}
|
||||||
|
|
||||||
// args>5 - no more autocompletion possible - don't know what the argument is for.
|
// args>5 - no more autocompletion possible - don't know what the argument is for.
|
||||||
return; // catch-all.
|
return false; // catch-all.
|
||||||
}
|
}
|
||||||
|
|
||||||
void dropshell::autocomplete_list_commands()
|
bool dropshell::autocomplete_list_commands()
|
||||||
{
|
{
|
||||||
std::set<std::string> commands;
|
std::set<std::string> commands;
|
||||||
dropshell::get_all_used_commands(commands);
|
dropshell::get_all_used_commands(commands);
|
||||||
@ -100,5 +100,6 @@ void dropshell::autocomplete_list_commands()
|
|||||||
for (const auto& command : commands) {
|
for (const auto& command : commands) {
|
||||||
std::cout << command << std::endl;
|
std::cout << command << std::endl;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,9 +7,9 @@
|
|||||||
|
|
||||||
namespace dropshell {
|
namespace dropshell {
|
||||||
|
|
||||||
void autocomplete(const std::vector<std::string> &args);
|
bool autocomplete(const std::vector<std::string> &args);
|
||||||
|
|
||||||
void autocomplete_list_commands();
|
bool autocomplete_list_commands();
|
||||||
|
|
||||||
} // namespace dropshell
|
} // namespace dropshell
|
||||||
|
|
||||||
|
94
src/main.cpp
94
src/main.cpp
@ -24,7 +24,7 @@ extern const std::string RELEASE_DATE;
|
|||||||
extern const std::string AUTHOR;
|
extern const std::string AUTHOR;
|
||||||
extern const std::string LICENSE;
|
extern const std::string LICENSE;
|
||||||
|
|
||||||
void print_help() {
|
bool print_help() {
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
maketitle("DropShell version " + VERSION);
|
maketitle("DropShell version " + VERSION);
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
@ -40,14 +40,17 @@ void print_help() {
|
|||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
std::cout << "Service commands: (if no service is specified, all services for the server are affected)" << std::endl;
|
std::cout << "Service commands: (if no service is specified, all services for the server are affected)" << std::endl;
|
||||||
std::cout << " install SERVER [SERVICE] Install/reinstall/update service(s). Non-destructive." << std::endl;
|
|
||||||
std::cout << " list [SERVER] [SERVICE] List status/details of all servers/server/service." << std::endl;
|
std::cout << " list [SERVER] [SERVICE] List status/details of all servers/server/service." << std::endl;
|
||||||
std::cout << " edit [SERVER] [SERVICE] Edit the configuration of dropshell/server/service." << std::endl;
|
std::cout << " edit [SERVER] [SERVICE] Edit the configuration of dropshell/server/service." << std::endl;
|
||||||
std::cout << " COMMAND SERVER [SERVICE] Run a command on service(s)." << std::endl;
|
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
std::cout << "Standard commands: install, uninstall, backup, restore, start, stop" << std::endl;
|
std::cout << " install SERVER [SERVICE] Install/reinstall/update service(s). Safe/non-destructive." << std::endl;
|
||||||
|
std::cout << " uninstall SERVER [SERVICE] Uninstalls the service on the remote server. Leaves data intact." << std::endl;
|
||||||
|
std::cout << " nuke SERVER SERVICE Nuke the service on the remote server, deleting all remote data." << std::endl;
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
std::cout << " ssh SERVER [SERVICE] Launch an interactive shell on a server or service" << std::endl;
|
std::cout << " COMMAND SERVER [SERVICE] Run a command on service(s), e.g." << std::endl;
|
||||||
|
std::cout << " backup, restore, start, stop, logs" << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
std::cout << " ssh SERVER SERVICE Launch an interactive shell on a server or service" << std::endl;
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
std::cout << "Creation commands: (apply to the first local config directory)"<<std::endl;
|
std::cout << "Creation commands: (apply to the first local config directory)"<<std::endl;
|
||||||
std::cout << " create-template TEMPLATE" << std::endl;
|
std::cout << " create-template TEMPLATE" << std::endl;
|
||||||
@ -59,6 +62,7 @@ void print_help() {
|
|||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
std::cout << "Other commands available once initialised." << std::endl;
|
std::cout << "Other commands available once initialised." << std::endl;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -115,32 +119,27 @@ int main(int argc, char* argv[]) {
|
|||||||
if (gConfig().is_config_set())
|
if (gConfig().is_config_set())
|
||||||
gTemplateManager().load_sources();
|
gTemplateManager().load_sources();
|
||||||
|
|
||||||
if (argc < 2) {
|
if (argc < 2)
|
||||||
print_help();
|
return print_help() ? 0 : 1;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
std::string cmd = argv[1];
|
|
||||||
std::vector<std::string> argvec;
|
|
||||||
for (int i=0; i<argc; i++)
|
|
||||||
argvec.push_back(argv[i]);
|
|
||||||
|
|
||||||
|
std::string cmd = argv[1];
|
||||||
|
|
||||||
if (cmd == "autocomplete") {
|
if (cmd == "autocomplete") {
|
||||||
autocomplete(argvec);
|
std::vector<std::string> argvec;
|
||||||
return 0;
|
for (int i=0; i<argc; i++)
|
||||||
|
argvec.push_back(argv[i]);
|
||||||
|
return autocomplete(argvec) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd == "help" || cmd == "-h" || cmd == "--help" || cmd== "h" || cmd=="halp") {
|
if (cmd == "help" || cmd == "-h" || cmd == "--help" || cmd== "h" || cmd=="halp")
|
||||||
print_help();
|
return print_help() ? 0 : 1;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cmd == "edit" && argc < 3) {
|
if (cmd == "edit" && argc < 3) {
|
||||||
if (!gConfig().is_config_set())
|
if (!gConfig().is_config_set())
|
||||||
gConfig().save_config(false);
|
gConfig().save_config(false);
|
||||||
|
|
||||||
std::string config_file = localfile::dropshell_json();
|
std::string config_file = localfile::dropshell_json();
|
||||||
if (!edit_file(config_file) || !std::filesystem::exists(config_file))
|
if (!service_runner::edit_file(config_file) || !std::filesystem::exists(config_file))
|
||||||
return die("Error: Failed to edit config file.");
|
return die("Error: Failed to edit config file.");
|
||||||
|
|
||||||
gConfig().load_config();
|
gConfig().load_config();
|
||||||
@ -190,62 +189,65 @@ int main(int argc, char* argv[]) {
|
|||||||
|
|
||||||
if (cmd == "create-template") {
|
if (cmd == "create-template") {
|
||||||
if (argc < 3) return die("Error: create-template requires a template name");
|
if (argc < 3) return die("Error: create-template requires a template name");
|
||||||
gTemplateManager().create_template(argv[2]);
|
return (gTemplateManager().create_template(argv[2])) ? 0 : 1;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd == "create-server") {
|
if (cmd == "create-server") {
|
||||||
if (argc < 3) return die("Error: create-server requires a server name");
|
if (argc < 3) return die("Error: create-server requires a server name");
|
||||||
create_server(argv[2]);
|
return (create_server(argv[2])) ? 0 : 1;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd == "create-service") {
|
if (cmd == "create-service") {
|
||||||
if (argc < 5) return die("Error: not enough arguments.\ndropshell create-service server template service");
|
if (argc < 5) return die("Error: not enough arguments.\ndropshell create-service server template service");
|
||||||
create_service(argv[2], argv[3], argv[4]);
|
return (create_service(argv[2], argv[3], argv[4])) ? 0 : 1;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd == "ssh" && argc < 4) {
|
if (cmd == "ssh" && argc < 4) {
|
||||||
if (argc < 3) return die("Error: ssh requires a server name and optionally service name");
|
if (argc < 3) return die("Error: ssh requires a server name and optionally service name");
|
||||||
interactive_ssh(argv[2], "bash");
|
service_runner::interactive_ssh(argv[2], "bash");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd == "edit" && argc < 4) {
|
if (cmd == "edit" && argc < 4) {
|
||||||
ASSERT_MSG(argc>=3, "Error: logic error!");
|
ASSERT_MSG(argc>=3, "Error: logic error!");
|
||||||
edit_server(safearg(argc,argv,2));
|
service_runner::edit_server(safearg(argc,argv,2));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle running a command.
|
// handle running a command.
|
||||||
std::set<std::string> commands;
|
std::set<std::string> commands;
|
||||||
get_all_used_commands(commands);
|
get_all_used_commands(commands);
|
||||||
commands.merge(std::set<std::string>{"ssh","edit","_allservicesstatus"}); // handled by service_runner, but not in template_shell_commands.
|
commands.merge(std::set<std::string>{"ssh","edit","_allservicesstatus","fullnuke"}); // handled by service_runner, but not in template_shell_commands.
|
||||||
for (const auto& command : commands) {
|
|
||||||
if (cmd == command) {
|
|
||||||
ServerAndServices server_and_services;
|
|
||||||
if (!getCLIServices(safearg(argc, argv, 2), safearg(argc, argv, 3), server_and_services)) {
|
|
||||||
std::cerr << "Error: " << command << " command requires server name and optionally service name" << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& service_info : server_and_services.servicelist) {
|
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);
|
service_runner runner(server_and_services.server_name, service_info.service_name);
|
||||||
if (!runner.isValid()) {
|
if (!runner.isValid())
|
||||||
std::cerr << "Error: Failed to initialize service" << std::endl;
|
return die("Error: Failed to initialize service");
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
std::vector<std::string> additional_args;
|
std::vector<std::string> additional_args;
|
||||||
for (int i=4; i<argc; i++)
|
for (int i=4; i<argc; i++)
|
||||||
additional_args.push_back(argv[i]);
|
additional_args.push_back(argv[i]);
|
||||||
if (!runner.run_command(command, additional_args)) {
|
if (!runner.run_command(cmd, additional_args))
|
||||||
std::cerr << command +" failed." << std::endl;
|
return die(cmd+" failed on service "+service_info.service_name);
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// success!
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unknown command
|
// Unknown command
|
||||||
|
@ -112,13 +112,19 @@ std::string server_env_manager::construct_ssh_cmd(bool allocateTTY) const {
|
|||||||
|
|
||||||
sCommand server_env_manager::construct_standard_command_run_cmd(const std::string &service_name, const std::string &command, std::vector<std::string> args, bool silent) const
|
sCommand server_env_manager::construct_standard_command_run_cmd(const std::string &service_name, const std::string &command, std::vector<std::string> args, bool silent) const
|
||||||
{
|
{
|
||||||
|
if (command.empty())
|
||||||
|
return sCommand();
|
||||||
|
|
||||||
std::string remote_service_template_path = remotepath::service_template(mServerName,service_name);
|
std::string remote_service_template_path = remotepath::service_template(mServerName,service_name);
|
||||||
std::string remote_service_config_path = remotepath::service_config(mServerName,service_name);
|
std::string remote_service_config_path = remotepath::service_config(mServerName,service_name);
|
||||||
|
|
||||||
std::string script_path = remote_service_template_path + "/" + command + ".sh";
|
std::string script_path = remote_service_template_path + "/" + command + ".sh";
|
||||||
|
|
||||||
std::map<std::string, std::string> env_vars;
|
std::map<std::string, std::string> env_vars;
|
||||||
get_all_service_env_vars(mServerName, service_name, env_vars);
|
if (!get_all_service_env_vars(mServerName, service_name, env_vars)) {
|
||||||
|
std::cerr << "Error: Failed to get all service env vars for " << service_name << std::endl;
|
||||||
|
return sCommand();
|
||||||
|
}
|
||||||
|
|
||||||
std::string argstr = "";
|
std::string argstr = "";
|
||||||
for (const auto& arg : args) {
|
for (const auto& arg : args) {
|
||||||
@ -126,6 +132,10 @@ sCommand server_env_manager::construct_standard_command_run_cmd(const std::strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
sCommand scommand(remote_service_template_path, "bash " + quote(script_path) + argstr + (silent ? " > /dev/null 2>&1" : ""), env_vars);
|
sCommand scommand(remote_service_template_path, "bash " + quote(script_path) + argstr + (silent ? " > /dev/null 2>&1" : ""), env_vars);
|
||||||
|
|
||||||
|
if (scommand.empty())
|
||||||
|
std::cerr << "Error: Failed to construct command for " << service_name << " " << command << std::endl;
|
||||||
|
|
||||||
return scommand;
|
return scommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,9 +171,21 @@ bool server_env_manager::check_remote_items_exist(const std::vector<std::string>
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EXITSTATUSCHECK(int ret) {
|
||||||
|
return (ret != -1 && WIFEXITED(ret) && (WEXITSTATUS(ret) == 0)); // ret is -1 if the command failed to execute.
|
||||||
|
}
|
||||||
|
|
||||||
bool server_env_manager::execute_ssh_command(const sCommand& command, bool allocateTTY) const {
|
bool server_env_manager::execute_ssh_command(const sCommand& command, bool allocateTTY) const {
|
||||||
std::string full_cmd = construct_ssh_cmd(allocateTTY) + " " + (allocateTTY ? halfquote(command.construct_rawcmd()) : quote(command.construct_safecmd()));
|
std::string full_cmd = construct_ssh_cmd(allocateTTY) + " " + (allocateTTY ? halfquote(command.construct_rawcmd()) : quote(command.construct_safecmd()));
|
||||||
return (system(full_cmd.c_str()) == 0);
|
int ret = system(full_cmd.c_str());
|
||||||
|
if (ret == -1) {
|
||||||
|
std::cerr << "Error: Failed to execute command: " << full_cmd << std::endl;
|
||||||
|
// system() failed to execute
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
// Check if the command exited normally and with status 0
|
||||||
|
return EXITSTATUSCHECK(ret);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool server_env_manager::execute_ssh_command_and_capture_output(const sCommand& command, std::string &output, bool allocateTTY) const
|
bool server_env_manager::execute_ssh_command_and_capture_output(const sCommand& command, std::string &output, bool allocateTTY) const
|
||||||
@ -175,6 +197,8 @@ bool server_env_manager::execute_ssh_command_and_capture_output(const sCommand&
|
|||||||
bool server_env_manager::run_remote_template_command(const std::string &service_name, const std::string &command, std::vector<std::string> args, bool silent) const
|
bool server_env_manager::run_remote_template_command(const std::string &service_name, const std::string &command, std::vector<std::string> args, bool silent) const
|
||||||
{
|
{
|
||||||
sCommand scommand = construct_standard_command_run_cmd(service_name, command, args, silent);
|
sCommand scommand = construct_standard_command_run_cmd(service_name, command, args, silent);
|
||||||
|
if (scommand.get_command_to_run().empty())
|
||||||
|
return false;
|
||||||
bool allocateTTY = (command=="ssh");
|
bool allocateTTY = (command=="ssh");
|
||||||
return execute_ssh_command(scommand, allocateTTY);
|
return execute_ssh_command(scommand, allocateTTY);
|
||||||
}
|
}
|
||||||
@ -182,21 +206,23 @@ bool server_env_manager::run_remote_template_command(const std::string &service_
|
|||||||
bool server_env_manager::run_remote_template_command_and_capture_output(const std::string &service_name, const std::string &command, std::vector<std::string> args, std::string &output, bool silent) const
|
bool server_env_manager::run_remote_template_command_and_capture_output(const std::string &service_name, const std::string &command, std::vector<std::string> args, std::string &output, bool silent) const
|
||||||
{
|
{
|
||||||
sCommand scommand = construct_standard_command_run_cmd(service_name, command, args, silent);
|
sCommand scommand = construct_standard_command_run_cmd(service_name, command, args, silent);
|
||||||
|
if (scommand.get_command_to_run().empty())
|
||||||
|
return false;
|
||||||
bool allocateTTY = (command=="ssh");
|
bool allocateTTY = (command=="ssh");
|
||||||
return execute_ssh_command_and_capture_output(scommand, output, allocateTTY);
|
return execute_ssh_command_and_capture_output(scommand, output, allocateTTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool server_env_manager::execute_local_command(const sCommand& command) {
|
bool server_env_manager::execute_local_command(const sCommand& command) {
|
||||||
auto status = system(command.construct_safecmd().c_str());
|
if (command.get_command_to_run().empty())
|
||||||
if ( WIFEXITED(status) ) {
|
return false;
|
||||||
int returned = WEXITSTATUS(status);
|
int ret = system(command.construct_safecmd().c_str());
|
||||||
return (returned == 0);
|
return EXITSTATUSCHECK(ret);
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool server_env_manager::execute_local_command_interactive(const sCommand &command)
|
bool server_env_manager::execute_local_command_interactive(const sCommand &command)
|
||||||
{
|
{
|
||||||
|
if (command.get_command_to_run().empty())
|
||||||
|
return false;
|
||||||
std::string full_command = command.construct_rawcmd(); // Get the command string
|
std::string full_command = command.construct_rawcmd(); // Get the command string
|
||||||
|
|
||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
@ -217,23 +243,20 @@ bool server_env_manager::execute_local_command_interactive(const sCommand &comma
|
|||||||
// If execvp returns, it means an error occurred
|
// If execvp returns, it means an error occurred
|
||||||
perror("execvp failed");
|
perror("execvp failed");
|
||||||
exit(EXIT_FAILURE); // Exit child process on error
|
exit(EXIT_FAILURE); // Exit child process on error
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Parent process
|
// Parent process
|
||||||
int status;
|
int ret;
|
||||||
// Wait for the child process to complete
|
// Wait for the child process to complete
|
||||||
waitpid(pid, &status, 0);
|
waitpid(pid, &ret, 0);
|
||||||
|
|
||||||
if (WIFEXITED(status)) {
|
return EXITSTATUSCHECK(ret);
|
||||||
return (WEXITSTATUS(status) == 0);
|
|
||||||
}
|
|
||||||
return false; // Child terminated abnormally
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool server_env_manager::execute_local_command_and_capture_output(const sCommand& command, std::string &output)
|
bool server_env_manager::execute_local_command_and_capture_output(const sCommand& command, std::string &output)
|
||||||
{
|
{
|
||||||
|
if (command.get_command_to_run().empty())
|
||||||
|
return false;
|
||||||
std::string full_cmd = command.construct_safecmd() + " 2>&1";
|
std::string full_cmd = command.construct_safecmd() + " 2>&1";
|
||||||
FILE *pipe = popen(full_cmd.c_str(), "r");
|
FILE *pipe = popen(full_cmd.c_str(), "r");
|
||||||
if (!pipe) {
|
if (!pipe) {
|
||||||
@ -243,17 +266,15 @@ bool server_env_manager::execute_local_command_and_capture_output(const sCommand
|
|||||||
while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
|
while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
|
||||||
output += buffer;
|
output += buffer;
|
||||||
}
|
}
|
||||||
int status = pclose(pipe);
|
int ret = pclose(pipe);
|
||||||
if ( WIFEXITED(status) ) {
|
return EXITSTATUSCHECK(ret);
|
||||||
int returned = WEXITSTATUS(status);
|
|
||||||
return (returned == 0);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string sCommand::construct_safecmd() const
|
std::string sCommand::construct_safecmd() const
|
||||||
{
|
{
|
||||||
|
if (mCmd.empty())
|
||||||
|
return "";
|
||||||
std::string to_encode;
|
std::string to_encode;
|
||||||
|
|
||||||
for (const auto& env_var : mVars) {
|
for (const auto& env_var : mVars) {
|
||||||
@ -272,6 +293,9 @@ std::string sCommand::construct_safecmd() const
|
|||||||
|
|
||||||
std::string sCommand::construct_rawcmd() const
|
std::string sCommand::construct_rawcmd() const
|
||||||
{
|
{
|
||||||
|
if (mCmd.empty())
|
||||||
|
return "";
|
||||||
|
|
||||||
std::string rawcmd;
|
std::string rawcmd;
|
||||||
if (!mDir.empty())
|
if (!mDir.empty())
|
||||||
rawcmd = "cd " + quote(mDir) + " && ";
|
rawcmd = "cd " + quote(mDir) + " && ";
|
||||||
@ -288,6 +312,8 @@ std::string sCommand::construct_rawcmd() const
|
|||||||
|
|
||||||
std::string makesafecmd(const std::string &command)
|
std::string makesafecmd(const std::string &command)
|
||||||
{
|
{
|
||||||
|
if (command.empty())
|
||||||
|
return "";
|
||||||
std::string encoded = base64_encode(dequote(trim(command)));
|
std::string encoded = base64_encode(dequote(trim(command)));
|
||||||
std::string commandstr = "echo " + encoded + " | base64 -d | bash";
|
std::string commandstr = "echo " + encoded + " | base64 -d | bash";
|
||||||
return commandstr;
|
return commandstr;
|
||||||
|
@ -18,7 +18,7 @@ class sCommand {
|
|||||||
mDir(directory_to_run_in), mCmd(command_to_run), mVars(env_vars) {}
|
mDir(directory_to_run_in), mCmd(command_to_run), mVars(env_vars) {}
|
||||||
sCommand(std::string command_to_run) :
|
sCommand(std::string command_to_run) :
|
||||||
mDir(""), mCmd(command_to_run), mVars({}) {}
|
mDir(""), mCmd(command_to_run), mVars({}) {}
|
||||||
|
sCommand() : mDir(""), mCmd(""), mVars({}) {}
|
||||||
|
|
||||||
std::string get_directory_to_run_in() const { return mDir; }
|
std::string get_directory_to_run_in() const { return mDir; }
|
||||||
std::string get_command_to_run() const { return mCmd; }
|
std::string get_command_to_run() const { return mCmd; }
|
||||||
@ -29,6 +29,8 @@ class sCommand {
|
|||||||
std::string construct_safecmd() const;
|
std::string construct_safecmd() const;
|
||||||
std::string construct_rawcmd() const;
|
std::string construct_rawcmd() const;
|
||||||
|
|
||||||
|
bool empty() const { return mCmd.empty(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string mDir;
|
std::string mDir;
|
||||||
std::string mCmd;
|
std::string mCmd;
|
||||||
@ -64,6 +66,7 @@ class server_env_manager {
|
|||||||
std::string get_SSH_PORT() const { return get_variable("SSH_PORT"); }
|
std::string get_SSH_PORT() const { return get_variable("SSH_PORT"); }
|
||||||
std::string get_DROPSHELL_DIR() const { return get_variable("DROPSHELL_DIR"); }
|
std::string get_DROPSHELL_DIR() const { return get_variable("DROPSHELL_DIR"); }
|
||||||
bool is_valid() const { return mValid; }
|
bool is_valid() const { return mValid; }
|
||||||
|
std::string get_server_name() const { return mServerName; }
|
||||||
|
|
||||||
// helper functions
|
// helper functions
|
||||||
public:
|
public:
|
||||||
|
@ -174,14 +174,14 @@ void show_server_details(const std::string& server_name) {
|
|||||||
} // end of list services
|
} // end of list services
|
||||||
} // end of show_server_details
|
} // end of show_server_details
|
||||||
|
|
||||||
void create_server(const std::string &server_name)
|
bool create_server(const std::string &server_name)
|
||||||
{
|
{
|
||||||
// 1. check if server name already exists
|
// 1. check if server name already exists
|
||||||
std::string server_existing_dir = localpath::server(server_name);
|
std::string server_existing_dir = localpath::server(server_name);
|
||||||
if (!server_existing_dir.empty()) {
|
if (!server_existing_dir.empty()) {
|
||||||
std::cerr << "Error: Server name already exists: " << server_name << std::endl;
|
std::cerr << "Error: Server name already exists: " << server_name << std::endl;
|
||||||
std::cerr << "Current server path: " << server_existing_dir << std::endl;
|
std::cerr << "Current server path: " << server_existing_dir << std::endl;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. create a new directory in the user config directory
|
// 2. create a new directory in the user config directory
|
||||||
@ -189,7 +189,7 @@ void create_server(const std::string &server_name)
|
|||||||
if (lsdp.empty() || lsdp[0].empty()) {
|
if (lsdp.empty() || lsdp[0].empty()) {
|
||||||
std::cerr << "Error: Local server definition path not found" << std::endl;
|
std::cerr << "Error: Local server definition path not found" << std::endl;
|
||||||
std::cerr << "Run 'dropshell edit' to configure DropShell" << std::endl;
|
std::cerr << "Run 'dropshell edit' to configure DropShell" << std::endl;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
std::string server_dir = lsdp[0] + "/" + server_name;
|
std::string server_dir = lsdp[0] + "/" + server_name;
|
||||||
std::filesystem::create_directory(server_dir);
|
std::filesystem::create_directory(server_dir);
|
||||||
@ -214,6 +214,7 @@ void create_server(const std::string &server_name)
|
|||||||
std::cout << "2) test ssh is working: dropshell ssh " << server_name << std::endl;
|
std::cout << "2) test ssh is working: dropshell ssh " << server_name << std::endl;
|
||||||
std::cout << "3) install dropshell-agent: dropshell install " << server_name << " dropshell-agent" << std::endl;
|
std::cout << "3) install dropshell-agent: dropshell install " << server_name << " dropshell-agent" << std::endl;
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void get_all_used_commands(std::set<std::string> &commands)
|
void get_all_used_commands(std::set<std::string> &commands)
|
||||||
|
@ -24,7 +24,7 @@ namespace dropshell {
|
|||||||
void list_servers();
|
void list_servers();
|
||||||
void show_server_details(const std::string& server_name);
|
void show_server_details(const std::string& server_name);
|
||||||
|
|
||||||
void create_server(const std::string& server_name);
|
bool create_server(const std::string& server_name);
|
||||||
|
|
||||||
void get_all_used_commands(std::set<std::string> &commands);
|
void get_all_used_commands(std::set<std::string> &commands);
|
||||||
|
|
||||||
|
@ -31,6 +31,9 @@ service_runner::service_runner(const std::string& server_name, const std::string
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
mServiceInfo = get_service_info(server_name, service_name);
|
mServiceInfo = get_service_info(server_name, service_name);
|
||||||
|
if (mServiceInfo.service_name.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
mService = mServiceInfo.service_name;
|
mService = mServiceInfo.service_name;
|
||||||
|
|
||||||
mValid = !mServiceInfo.local_template_path.empty();
|
mValid = !mServiceInfo.local_template_path.empty();
|
||||||
@ -144,7 +147,7 @@ bool service_runner::uninstall() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool service_runner::nuke()
|
bool service_runner::nuke(bool silent)
|
||||||
{
|
{
|
||||||
maketitle("Nuking " + mService + " (" + mServiceInfo.template_name + ") on " + mServer);
|
maketitle("Nuking " + mService + " (" + mServiceInfo.template_name + ") on " + mServer);
|
||||||
|
|
||||||
@ -156,10 +159,22 @@ bool service_runner::nuke()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// try uninstalling.
|
// try uninstalling.
|
||||||
mServerEnv.run_remote_template_command(mService, "uninstall", {});
|
if (gTemplateManager().template_command_exists(mServiceInfo.template_name, "uninstall"))
|
||||||
mServerEnv.run_remote_template_command(mService, "nuke", {});
|
if (!mServerEnv.run_remote_template_command(mService, "uninstall", {}))
|
||||||
|
std::cerr << "Warning: Uninstall script failed, but continuing with directory removal" << std::endl;
|
||||||
|
|
||||||
std::string rm_cmd = "rm -rf " + quote(remotepath::service(mServer, mService));
|
// try nuke script.
|
||||||
|
if (gTemplateManager().template_command_exists(mServiceInfo.template_name, "nuke"))
|
||||||
|
if (!mServerEnv.run_remote_template_command(mService, "nuke", {}))
|
||||||
|
std::cerr << "Warning: Nuke script failed, but continuing with directory removal" << std::endl;
|
||||||
|
|
||||||
|
// try rm -rf.
|
||||||
|
std::string remotedir = remotepath::service(mServer, mService);
|
||||||
|
std::string remoteparentdir = get_parent(remotedir);
|
||||||
|
std::string remotechilddir = get_child(remotedir);
|
||||||
|
|
||||||
|
std::string cmd = quote("rm -rf /parent/"+remotechilddir);
|
||||||
|
std::string rm_cmd = "docker run --rm -v "+quote(remoteparentdir)+":/parent debian bash -c "+cmd;
|
||||||
if (!mServerEnv.execute_ssh_command(rm_cmd)) {
|
if (!mServerEnv.execute_ssh_command(rm_cmd)) {
|
||||||
std::cerr << "Failed to remove service directory" << std::endl;
|
std::cerr << "Failed to remove service directory" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
@ -167,24 +182,37 @@ bool service_runner::nuke()
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Service " << mService << " successfully nuked from " << mServer << std::endl;
|
std::cout << "Service " << mService << " successfully nuked from " << mServer << std::endl;
|
||||||
std::cout << "Now deleteing local files..." << std::endl;
|
|
||||||
std::string local_service_path = localpath::service(mServer,mService);
|
|
||||||
if (local_service_path.empty() || !fs::exists(local_service_path)) {
|
|
||||||
std::cerr << "Error: Service directory not found: " << local_service_path << std::endl;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::string rm_cmd = "rm -rf " + quote(local_service_path);
|
|
||||||
if (!mServerEnv.execute_local_command(rm_cmd)) {
|
|
||||||
std::cerr << "Failed to remove service directory" << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "Service " << mService << " successfully nuked from " << mServer << std::endl;
|
if (!silent) {
|
||||||
|
std::cout << "There's nothing left on the remote server." << std::endl;
|
||||||
|
std::cout << "You can remove the local files with:" << std::endl;
|
||||||
|
std::cout << " rm -rf " << localpath::service(mServer,mService) << std::endl;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool service_runner::fullnuke()
|
||||||
|
{
|
||||||
|
if (!nuke(true))
|
||||||
|
{
|
||||||
|
std::cerr << "Warning: Nuke script failed, aborting fullnuke!" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string local_service_path = mServiceInfo.local_service_path;
|
||||||
|
if (local_service_path.empty() || !fs::exists(local_service_path)) {
|
||||||
|
std::cerr << "Error: Service directory not found: " << local_service_path << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string rm_cmd = "rm -rf " + quote(local_service_path);
|
||||||
|
if (!mServerEnv.execute_local_command(rm_cmd)) {
|
||||||
|
std::cerr << "Failed to remove service directory" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
@ -207,6 +235,15 @@ bool service_runner::run_command(const std::string& command, std::vector<std::st
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)) {
|
if (!gTemplateManager().template_command_exists(mServiceInfo.template_name, command)) {
|
||||||
std::cout << "No command script for " << mServiceInfo.template_name << " : " << command << std::endl;
|
std::cout << "No command script for " << mServiceInfo.template_name << " : " << command << std::endl;
|
||||||
return true; // nothing to run.
|
return true; // nothing to run.
|
||||||
@ -220,25 +257,25 @@ bool service_runner::run_command(const std::string& command, std::vector<std::st
|
|||||||
|
|
||||||
// Check if service directory exists
|
// Check if service directory exists
|
||||||
if (!mServerEnv.check_remote_dir_exists(remotepath::service(mServer, mService))) {
|
if (!mServerEnv.check_remote_dir_exists(remotepath::service(mServer, mService))) {
|
||||||
|
std::cerr << "Error: Service is not installed: " << mService << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if command script exists
|
// Check if command script exists
|
||||||
if (!mServerEnv.check_remote_file_exists(script_path)) {
|
if (!mServerEnv.check_remote_file_exists(script_path)) {
|
||||||
|
std::cerr << "Error: Remote command script not found: " << script_path << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if env file exists
|
// Check if env file exists
|
||||||
if (!mServerEnv.check_remote_file_exists(remotefile::service_env(mServer, mService))) {
|
if (!mServerEnv.check_remote_file_exists(remotefile::service_env(mServer, mService))) {
|
||||||
|
std::cerr << "Error: Service config file not found: " << remotefile::service_env(mServer, mService) << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command == "uninstall")
|
if (command == "uninstall")
|
||||||
return uninstall();
|
return uninstall();
|
||||||
|
|
||||||
if (command == "nuke")
|
|
||||||
return nuke();
|
|
||||||
|
|
||||||
if (command == "ssh") {
|
if (command == "ssh") {
|
||||||
interactive_ssh_service();
|
interactive_ssh_service();
|
||||||
return true;
|
return true;
|
||||||
@ -379,7 +416,7 @@ std::string service_runner::healthmark()
|
|||||||
return HealthStatus2String(status);
|
return HealthStatus2String(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
void interactive_ssh(const std::string & server_name, const std::string & command) {
|
void service_runner::interactive_ssh(const std::string & server_name, const std::string & command) {
|
||||||
std::string serverpath = localpath::server(server_name);
|
std::string serverpath = localpath::server(server_name);
|
||||||
if (serverpath.empty()) {
|
if (serverpath.empty()) {
|
||||||
std::cerr << "Error: Server not found: " << server_name << std::endl;
|
std::cerr << "Error: Server not found: " << server_name << std::endl;
|
||||||
@ -409,7 +446,7 @@ void interactive_ssh(const std::string & server_name, const std::string & comman
|
|||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void edit_server(const std::string &server_name)
|
void service_runner::edit_server(const std::string &server_name)
|
||||||
{
|
{
|
||||||
std::string serverpath = localpath::server(server_name);
|
std::string serverpath = localpath::server(server_name);
|
||||||
if (serverpath.empty()) {
|
if (serverpath.empty()) {
|
||||||
@ -432,7 +469,7 @@ void edit_server(const std::string &server_name)
|
|||||||
std::cout << aftertext.str() << std::endl;
|
std::cout << aftertext.str() << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool edit_file(const std::string &file_path)
|
bool service_runner::edit_file(const std::string &file_path)
|
||||||
{
|
{
|
||||||
// make sure parent directory exists.
|
// make sure parent directory exists.
|
||||||
std::string parent_dir = get_parent(file_path);
|
std::string parent_dir = get_parent(file_path);
|
||||||
@ -491,6 +528,12 @@ bool service_runner::restore(std::string backup_file, bool silent)
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string local_backups_dir = gConfig().get_local_backup_path();
|
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();
|
std::string local_backup_file_path = (std::filesystem::path(local_backups_dir) / backup_file).string();
|
||||||
|
|
||||||
if (! std::filesystem::exists(local_backup_file_path)) {
|
if (! std::filesystem::exists(local_backup_file_path)) {
|
||||||
@ -520,14 +563,6 @@ bool service_runner::restore(std::string backup_file, bool silent)
|
|||||||
std::cout << "Restoring " << nicedate << " backup of " << backup_template_name << " taken from "<<backup_server_name<<", onto "<<mServer<<"/"<<mService<<std::endl;
|
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 << std::endl;
|
||||||
std::cout << "*** ALL DATA FOR "<<mServer<<"/"<<mService<<" WILL BE OVERWRITTEN! ***"<<std::endl;
|
std::cout << "*** ALL DATA FOR "<<mServer<<"/"<<mService<<" WILL BE OVERWRITTEN! ***"<<std::endl;
|
||||||
std::cout << std::endl;
|
|
||||||
std::cout << "Are you sure you want to continue? (y/n)" << std::endl;
|
|
||||||
char confirm;
|
|
||||||
std::cin >> confirm;
|
|
||||||
if (confirm != 'y') {
|
|
||||||
std::cout << "Restore cancelled." << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// run the restore script
|
// run the restore script
|
||||||
std::cout << "OK, here goes..." << std::endl;
|
std::cout << "OK, here goes..." << std::endl;
|
||||||
@ -556,8 +591,10 @@ bool service_runner::restore(std::string backup_file, bool silent)
|
|||||||
std::cerr << "Failed to copy backup file from server" << std::endl;
|
std::cerr << "Failed to copy backup file from server" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
mServerEnv.run_remote_template_command(mService, "restore", {remote_backup_file_path}, silent);
|
|
||||||
}
|
cRemoteTempFolder remote_temp_folder(mServerEnv);
|
||||||
|
mServerEnv.run_remote_template_command(mService, "restore", {remote_backup_file_path, remote_temp_folder.path()}, silent);
|
||||||
|
} // dtor of remote_temp_folder will clean up the temp folder on the server
|
||||||
|
|
||||||
// healthcheck the service
|
// healthcheck the service
|
||||||
std::cout << "3) Healthchecking service..." << std::endl;
|
std::cout << "3) Healthchecking service..." << std::endl;
|
||||||
@ -652,20 +689,22 @@ bool service_runner::backup(bool silent) {
|
|||||||
// assert that the backup filename is valid - -_- appears exactly 3 times in local_backup_file_path.
|
// assert that the backup filename is valid - -_- appears exactly 3 times in local_backup_file_path.
|
||||||
ASSERT(3 == count_substring(magic_string, local_backup_file_path));
|
ASSERT(3 == count_substring(magic_string, local_backup_file_path));
|
||||||
|
|
||||||
// Run backup script
|
{ // Run backup script
|
||||||
if (!mServerEnv.run_remote_template_command(mService, command, {remote_backup_file_path}, silent)) {
|
cRemoteTempFolder remote_temp_folder(mServerEnv);
|
||||||
std::cerr << "Backup script failed on remote server: " << remote_backup_file_path << std::endl;
|
if (!mServerEnv.run_remote_template_command(mService, command, {remote_backup_file_path, remote_temp_folder.path()}, silent)) {
|
||||||
return false;
|
std::cerr << "Backup script failed on remote server: " << remote_backup_file_path << std::endl;
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Copy backup file from server to local
|
// Copy backup file from server to local
|
||||||
std::string scp_cmd = "scp -P " + mServerEnv.get_SSH_PORT() + " " +
|
std::string scp_cmd = "scp -P " + mServerEnv.get_SSH_PORT() + " " +
|
||||||
mServerEnv.get_SSH_USER() + "@" + mServerEnv.get_SSH_HOST() + ":" +
|
mServerEnv.get_SSH_USER() + "@" + mServerEnv.get_SSH_HOST() + ":" +
|
||||||
quote(remote_backup_file_path) + " " + quote(local_backup_file_path) + (silent ? " > /dev/null 2>&1" : "");
|
quote(remote_backup_file_path) + " " + quote(local_backup_file_path) + (silent ? " > /dev/null 2>&1" : "");
|
||||||
if (!mServerEnv.execute_local_command(scp_cmd)) {
|
if (!mServerEnv.execute_local_command(scp_cmd)) {
|
||||||
std::cerr << "Failed to copy backup file from server" << std::endl;
|
std::cerr << "Failed to copy backup file from server" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
} // dtor of remote_temp_folder will clean up the temp folder on the server
|
||||||
|
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
std::cout << "Backup created successfully. Restore with:"<<std::endl;
|
std::cout << "Backup created successfully. Restore with:"<<std::endl;
|
||||||
@ -674,6 +713,73 @@ bool service_runner::backup(bool silent) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cRemoteTempFolder::cRemoteTempFolder(const server_env_manager &server_env) : mServerEnv(server_env)
|
||||||
|
{
|
||||||
|
std::string p = remotepath::temp_files(server_env.get_server_name()) + "/" + random_alphanumeric_string(10);
|
||||||
|
std::string mkdir_cmd = "mkdir -p " + quote(p);
|
||||||
|
if (!server_env.execute_ssh_command(mkdir_cmd))
|
||||||
|
std::cerr << "Failed to create temp directory on server" << std::endl;
|
||||||
|
else
|
||||||
|
mPath = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace dropshell
|
cRemoteTempFolder::~cRemoteTempFolder()
|
||||||
|
{
|
||||||
|
std::string rm_cmd = "rm -rf " + quote(mPath);
|
||||||
|
mServerEnv.execute_ssh_command(rm_cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string cRemoteTempFolder::path() const
|
||||||
|
{
|
||||||
|
return mPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 + magic_string + info.template_name + magic_string + service + magic_string;
|
||||||
|
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
|
@ -56,10 +56,6 @@ class service_runner {
|
|||||||
std::string healthtick();
|
std::string healthtick();
|
||||||
std::string healthmark();
|
std::string healthmark();
|
||||||
|
|
||||||
// get the status of all services on the server
|
|
||||||
static std::map<std::string, ServiceStatus> get_all_services_status(std::string server_name);
|
|
||||||
static std::string HealthStatus2String(HealthStatus status);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// install the service over ssh, using the credentials from server.env (via server_env.hpp), by:
|
// install the service over ssh, using the credentials from server.env (via server_env.hpp), by:
|
||||||
// 1. check if the server_name exists, and the service_name refers to a valid template
|
// 1. check if the server_name exists, and the service_name refers to a valid template
|
||||||
@ -82,7 +78,8 @@ class service_runner {
|
|||||||
bool restore(std::string backup_file, bool silent=false);
|
bool restore(std::string backup_file, bool silent=false);
|
||||||
|
|
||||||
// nuke the service
|
// nuke the service
|
||||||
bool nuke();
|
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
|
// launch an interactive ssh session on a server or service
|
||||||
// replaces the current dropshell process with the ssh process
|
// replaces the current dropshell process with the ssh process
|
||||||
@ -91,6 +88,16 @@ class service_runner {
|
|||||||
// edit the service configuration file
|
// edit the service configuration file
|
||||||
void edit_service_config();
|
void edit_service_config();
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
// utility functions
|
||||||
|
static std::string get_latest_backup_file(const std::string& server, const std::string& service);
|
||||||
|
static void interactive_ssh(const std::string & server_name, const std::string & command);
|
||||||
|
static void edit_server(const std::string & server_name);
|
||||||
|
static bool edit_file(const std::string & file_path);
|
||||||
|
static std::map<std::string, ServiceStatus> get_all_services_status(std::string server_name);
|
||||||
|
static std::string HealthStatus2String(HealthStatus status);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string mServer;
|
std::string mServer;
|
||||||
server_env_manager mServerEnv;
|
server_env_manager mServerEnv;
|
||||||
@ -102,11 +109,15 @@ class service_runner {
|
|||||||
public:
|
public:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class cRemoteTempFolder {
|
||||||
// other utility routines (not specific to service_runner)
|
public:
|
||||||
void interactive_ssh(const std::string & server_name, const std::string & command);
|
cRemoteTempFolder(const server_env_manager & server_env); // create a temp folder on the remote server
|
||||||
void edit_server(const std::string & server_name);
|
~cRemoteTempFolder(); // delete the temp folder on the remote server
|
||||||
bool edit_file(const std::string & file_path);
|
std::string path() const; // get the path to the temp folder on the remote server
|
||||||
|
private:
|
||||||
|
std::string mPath;
|
||||||
|
const server_env_manager & mServerEnv;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace dropshell
|
} // namespace dropshell
|
||||||
|
|
||||||
|
@ -14,6 +14,13 @@ namespace fs = std::filesystem;
|
|||||||
|
|
||||||
namespace dropshell {
|
namespace dropshell {
|
||||||
|
|
||||||
|
bool SIvalid(const LocalServiceInfo& service_info) {
|
||||||
|
return !service_info.service_name.empty() &&
|
||||||
|
!service_info.template_name.empty() &&
|
||||||
|
!service_info.local_service_path.empty() &&
|
||||||
|
!service_info.local_template_path.empty();
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<LocalServiceInfo> get_server_services_info(const std::string& server_name) {
|
std::vector<LocalServiceInfo> get_server_services_info(const std::string& server_name) {
|
||||||
std::vector<LocalServiceInfo> services;
|
std::vector<LocalServiceInfo> services;
|
||||||
|
|
||||||
@ -64,7 +71,8 @@ LocalServiceInfo get_service_info(const std::string &server_name, const std::str
|
|||||||
|
|
||||||
// now set the template name and path.
|
// now set the template name and path.
|
||||||
std::map<std::string, std::string> variables;
|
std::map<std::string, std::string> variables;
|
||||||
get_all_service_env_vars(server_name, service_name, variables);
|
if (!get_all_service_env_vars(server_name, service_name, variables))
|
||||||
|
return LocalServiceInfo();
|
||||||
|
|
||||||
// confirm TEMPLATE is defined.
|
// confirm TEMPLATE is defined.
|
||||||
auto it = variables.find("TEMPLATE");
|
auto it = variables.find("TEMPLATE");
|
||||||
@ -207,10 +215,16 @@ bool create_service(const std::string &server_name, const std::string &template_
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void get_all_service_env_vars(const std::string &server_name, const std::string &service_name, std::map<std::string, std::string> & all_env_vars)
|
bool get_all_service_env_vars(const std::string &server_name, const std::string &service_name, std::map<std::string, std::string> & all_env_vars)
|
||||||
{
|
{
|
||||||
all_env_vars.clear();
|
all_env_vars.clear();
|
||||||
|
|
||||||
|
if (localpath::service(server_name, service_name).empty() || !fs::exists(localpath::service(server_name, service_name)))
|
||||||
|
{
|
||||||
|
std::cerr << "Error: Service not found: " << service_name << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// add in some handy variables.
|
// add in some handy variables.
|
||||||
all_env_vars["CONFIG_PATH"] = remotepath::service_config(server_name,service_name);
|
all_env_vars["CONFIG_PATH"] = remotepath::service_config(server_name,service_name);
|
||||||
all_env_vars["SERVER"] = server_name;
|
all_env_vars["SERVER"] = server_name;
|
||||||
@ -247,13 +261,22 @@ void get_all_service_env_vars(const std::string &server_name, const std::string
|
|||||||
std::cerr << "The TEMPLATE variable is required to determine the template name." << std::endl;
|
std::cerr << "The TEMPLATE variable is required to determine the template name." << std::endl;
|
||||||
std::cerr << "Please check the service.env file and the .template_info.env file in:" << std::endl;
|
std::cerr << "Please check the service.env file and the .template_info.env file in:" << std::endl;
|
||||||
std::cerr << " " << localpath::service(server_name, service_name) << std::endl << std::endl;
|
std::cerr << " " << localpath::service(server_name, service_name) << std::endl << std::endl;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
template_info tinfo = gTemplateManager().get_template_info(it->second);
|
template_info tinfo = gTemplateManager().get_template_info(it->second);
|
||||||
if (tinfo.is_set())
|
if (!tinfo.is_set()) {
|
||||||
load_env_file(tinfo.local_template_path()/"_default.env");
|
|
||||||
else
|
|
||||||
std::cerr << "Error: Template '" << it->second << "' not found" << std::endl;
|
std::cerr << "Error: Template '" << it->second << "' not found" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string default_env_file = tinfo.local_template_path()/"_default.env";
|
||||||
|
if (!fs::exists(default_env_file)) {
|
||||||
|
std::cerr << "Error: Template default env file '" << default_env_file << "' not found" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
load_env_file(default_env_file);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace dropshell
|
} // namespace dropshell
|
||||||
|
@ -15,13 +15,15 @@ namespace dropshell {
|
|||||||
std::string local_template_path;
|
std::string local_template_path;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool SIvalid(const LocalServiceInfo& service_info);
|
||||||
|
|
||||||
std::vector<LocalServiceInfo> get_server_services_info(const std::string& server_name);
|
std::vector<LocalServiceInfo> get_server_services_info(const std::string& server_name);
|
||||||
|
|
||||||
LocalServiceInfo get_service_info(const std::string& server_name, const std::string& service_name);
|
LocalServiceInfo get_service_info(const std::string& server_name, const std::string& service_name);
|
||||||
std::set<std::string> get_used_commands(const std::string& server_name, const std::string& service_name);
|
std::set<std::string> get_used_commands(const std::string& server_name, const std::string& service_name);
|
||||||
|
|
||||||
// get all env vars for a given service
|
// get all env vars for a given service
|
||||||
void get_all_service_env_vars(const std::string& server_name, const std::string& service_name, std::map<std::string, std::string> & all_env_vars);
|
bool get_all_service_env_vars(const std::string& server_name, const std::string& service_name, std::map<std::string, std::string> & all_env_vars);
|
||||||
|
|
||||||
// list all backups for a given service (across all servers)
|
// list all backups for a given service (across all servers)
|
||||||
std::set<std::string> list_backups(const std::string& server_name, const std::string& service_name);
|
std::set<std::string> list_backups(const std::string& server_name, const std::string& service_name);
|
||||||
|
@ -168,7 +168,7 @@
|
|||||||
return source->template_command_exists(template_name, command);
|
return source->template_command_exists(template_name, command);
|
||||||
}
|
}
|
||||||
|
|
||||||
void template_manager::create_template(const std::string &template_name) const
|
bool template_manager::create_template(const std::string &template_name) const
|
||||||
{
|
{
|
||||||
// 1. Create a new directory in the user templates directory
|
// 1. Create a new directory in the user templates directory
|
||||||
std::vector<std::string> local_server_definition_paths = gConfig().get_local_server_definition_paths();
|
std::vector<std::string> local_server_definition_paths = gConfig().get_local_server_definition_paths();
|
||||||
@ -176,20 +176,20 @@
|
|||||||
if (local_server_definition_paths.empty()) {
|
if (local_server_definition_paths.empty()) {
|
||||||
std::cerr << "Error: No local server definition paths found" << std::endl;
|
std::cerr << "Error: No local server definition paths found" << std::endl;
|
||||||
std::cerr << "Run 'dropshell edit' to configure DropShell" << std::endl;
|
std::cerr << "Run 'dropshell edit' to configure DropShell" << std::endl;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto info = get_template_info(template_name);
|
auto info = get_template_info(template_name);
|
||||||
if (info.is_set()) {
|
if (info.is_set()) {
|
||||||
std::cerr << "Error: Template '" << template_name << "' already exists at " << info.locationID() << std::endl;
|
std::cerr << "Error: Template '" << template_name << "' already exists at " << info.locationID() << std::endl;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto local_template_paths = gConfig().get_template_local_paths();
|
auto local_template_paths = gConfig().get_template_local_paths();
|
||||||
if (local_template_paths.empty()) {
|
if (local_template_paths.empty()) {
|
||||||
std::cerr << "Error: No local template paths found" << std::endl;
|
std::cerr << "Error: No local template paths found" << std::endl;
|
||||||
std::cerr << "Run 'dropshell edit' to add one to the DropShell config" << std::endl;
|
std::cerr << "Run 'dropshell edit' to add one to the DropShell config" << std::endl;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
std::string new_template_path = local_template_paths[0] + "/" + template_name;
|
std::string new_template_path = local_template_paths[0] + "/" + template_name;
|
||||||
|
|
||||||
@ -200,7 +200,7 @@
|
|||||||
auto example_info = gTemplateManager().get_template_info("example-nginx");
|
auto example_info = gTemplateManager().get_template_info("example-nginx");
|
||||||
if (!example_info.is_set()) {
|
if (!example_info.is_set()) {
|
||||||
std::cerr << "Error: Example template not found" << std::endl;
|
std::cerr << "Error: Example template not found" << std::endl;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
std::string example_template_path = example_info.local_template_path();
|
std::string example_template_path = example_info.local_template_path();
|
||||||
|
|
||||||
@ -222,7 +222,7 @@
|
|||||||
std::string service_env_path = new_template_path + "/config/.template_info.env";
|
std::string service_env_path = new_template_path + "/config/.template_info.env";
|
||||||
if (!replace_line_in_file(service_env_path, search_string, replacement_line)) {
|
if (!replace_line_in_file(service_env_path, search_string, replacement_line)) {
|
||||||
std::cerr << "Error: Failed to replace TEMPLATE= line in the .template_info.env file" << std::endl;
|
std::cerr << "Error: Failed to replace TEMPLATE= line in the .template_info.env file" << std::endl;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Print out the README.txt file and the path
|
// 3. Print out the README.txt file and the path
|
||||||
@ -244,7 +244,7 @@
|
|||||||
|
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
std::cout << "Template '" << template_name << "' created at " << new_template_path << std::endl;
|
std::cout << "Template '" << template_name << "' created at " << new_template_path << std::endl;
|
||||||
test_template(new_template_path);
|
return test_template(new_template_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void template_manager::load_sources()
|
void template_manager::load_sources()
|
||||||
|
@ -84,7 +84,7 @@ class template_manager {
|
|||||||
template_info get_template_info(const std::string& template_name) const;
|
template_info get_template_info(const std::string& template_name) const;
|
||||||
|
|
||||||
bool template_command_exists(const std::string& template_name,const std::string& command) const;
|
bool template_command_exists(const std::string& template_name,const std::string& command) const;
|
||||||
void create_template(const std::string& template_name) const;
|
bool create_template(const std::string& template_name) const;
|
||||||
static bool test_template(const std::string& template_path);
|
static bool test_template(const std::string& template_path);
|
||||||
|
|
||||||
void list_templates() const;
|
void list_templates() const;
|
||||||
|
@ -136,6 +136,12 @@ namespace remotepath {
|
|||||||
return (dsp.empty() ? "" : (dsp + "/backups"));
|
return (dsp.empty() ? "" : (dsp + "/backups"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string temp_files(const std::string &server_name)
|
||||||
|
{
|
||||||
|
std::string dsp = DROPSHELL_DIR(server_name);
|
||||||
|
return (dsp.empty() ? "" : (dsp + "/temp_files"));
|
||||||
|
}
|
||||||
|
|
||||||
std::string service_env(const std::string &server_name, const std::string &service_name)
|
std::string service_env(const std::string &server_name, const std::string &service_name)
|
||||||
{
|
{
|
||||||
std::string service_path = service_config(server_name, service_name);
|
std::string service_path = service_config(server_name, service_name);
|
||||||
@ -147,11 +153,18 @@ namespace remotepath {
|
|||||||
// ------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------
|
||||||
// Utility functions
|
// Utility functions
|
||||||
|
|
||||||
std::string get_parent(const std::string &path)
|
std::string get_parent(const std::filesystem::path path)
|
||||||
{
|
{
|
||||||
if (path.empty())
|
if (path.empty())
|
||||||
return std::string();
|
return std::string();
|
||||||
return fs::path(path).parent_path().string();
|
return path.parent_path().string();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_child(const std::filesystem::path path)
|
||||||
|
{
|
||||||
|
if (path.empty())
|
||||||
|
return std::string();
|
||||||
|
return path.filename().string();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace dropshell
|
} // namespace dropshell
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#define DIRECTORIES_HPP
|
#define DIRECTORIES_HPP
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
namespace dropshell {
|
namespace dropshell {
|
||||||
|
|
||||||
@ -62,6 +63,7 @@ namespace dropshell {
|
|||||||
// remote paths
|
// remote paths
|
||||||
// DROPSHELL_DIR
|
// DROPSHELL_DIR
|
||||||
// |-- backups
|
// |-- backups
|
||||||
|
// |-- temp_files
|
||||||
// |-- services
|
// |-- services
|
||||||
// |-- service name
|
// |-- service name
|
||||||
// |-- config
|
// |-- config
|
||||||
@ -83,12 +85,14 @@ namespace dropshell {
|
|||||||
std::string service_config(const std::string &server_name, const std::string &service_name);
|
std::string service_config(const std::string &server_name, const std::string &service_name);
|
||||||
std::string service_template(const std::string &server_name, const std::string &service_name);
|
std::string service_template(const std::string &server_name, const std::string &service_name);
|
||||||
std::string backups(const std::string &server_name);
|
std::string backups(const std::string &server_name);
|
||||||
|
std::string temp_files(const std::string &server_name);
|
||||||
} // namespace remotepath
|
} // namespace remotepath
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------------
|
||||||
// utility functions
|
// utility functions
|
||||||
std::string get_parent(const std::string &path);
|
std::string get_parent(const std::filesystem::path path);
|
||||||
|
std::string get_child(const std::filesystem::path path);
|
||||||
} // namespace dropshell
|
} // namespace dropshell
|
||||||
|
|
||||||
|
|
||||||
#endif // DIRECTORIES_HPP
|
#endif // DIRECTORIES_HPP
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
namespace dropshell {
|
namespace dropshell {
|
||||||
|
|
||||||
void maketitle(const std::string& title) {
|
void maketitle(const std::string& title) {
|
||||||
@ -292,6 +294,18 @@ std::string replace_with_environment_variables_like_bash(std::string str) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string random_alphanumeric_string(int length)
|
||||||
|
{
|
||||||
|
static std::mt19937 generator(std::random_device{}());
|
||||||
|
static const std::string chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||||
|
std::uniform_int_distribution<> distribution(0, chars.size() - 1);
|
||||||
|
std::string random_string;
|
||||||
|
for (int i = 0; i < length; ++i) {
|
||||||
|
random_string += chars[distribution(generator)];
|
||||||
|
}
|
||||||
|
|
||||||
|
return random_string;
|
||||||
|
}
|
||||||
|
|
||||||
std::string requote(std::string str) {
|
std::string requote(std::string str) {
|
||||||
return quote(trim(dequote(trim(str))));
|
return quote(trim(dequote(trim(str))));
|
||||||
|
@ -38,4 +38,6 @@ int count_substring(const std::string &substring, const std::string &text);
|
|||||||
|
|
||||||
std::string replace_with_environment_variables_like_bash(std::string str);
|
std::string replace_with_environment_variables_like_bash(std::string str);
|
||||||
|
|
||||||
|
std::string random_alphanumeric_string(int length);
|
||||||
|
|
||||||
} // namespace dropshell
|
} // namespace dropshell
|
@ -4,9 +4,7 @@ _check_required_env_vars
|
|||||||
|
|
||||||
_stop_container "$CONTAINER_NAME"
|
_stop_container "$CONTAINER_NAME"
|
||||||
|
|
||||||
if ! autobackup volume=$DATA_VOLUME volume=$CONFIG_VOLUME $1 $2; then
|
autobackup "$1" "$2" "volume=$DATA_VOLUME" "volume=$CONFIG_VOLUME" || _die "Failed to create backup"
|
||||||
_die "Failed to create backup"
|
|
||||||
fi
|
|
||||||
|
|
||||||
_start_container "$CONTAINER_NAME"
|
_start_container "$CONTAINER_NAME"
|
||||||
|
|
||||||
|
@ -2,9 +2,7 @@
|
|||||||
source "${AGENT_PATH}/_common.sh"
|
source "${AGENT_PATH}/_common.sh"
|
||||||
_check_required_env_vars "CONTAINER_NAME" "IMAGE_REGISTRY" "IMAGE_REPO" "IMAGE_TAG" "DATA_VOLUME" "CONFIG_VOLUME" "CONFIG_PATH"
|
_check_required_env_vars "CONTAINER_NAME" "IMAGE_REGISTRY" "IMAGE_REPO" "IMAGE_TAG" "DATA_VOLUME" "CONFIG_VOLUME" "CONFIG_PATH"
|
||||||
|
|
||||||
if ! autocreate volume=$DATA_VOLUME volume=$CONFIG_VOLUME; then
|
autocreate "volume=$DATA_VOLUME" "volume=$CONFIG_VOLUME" || _die "Failed to autocreate volumes $DATA_VOLUME and $CONFIG_VOLUME"
|
||||||
_die "Failed to autocreate volumes and paths"
|
|
||||||
fi
|
|
||||||
|
|
||||||
_check_docker_installed || _die "Docker test failed, aborting installation..."
|
_check_docker_installed || _die "Docker test failed, aborting installation..."
|
||||||
|
|
||||||
|
@ -8,8 +8,6 @@ _check_required_env_vars
|
|||||||
# any docker volumes and any custom local data folders.
|
# any docker volumes and any custom local data folders.
|
||||||
|
|
||||||
|
|
||||||
if ! autonuke volume=$DATA_VOLUME volume=$CONFIG_VOLUME; then
|
autonuke "volume=$DATA_VOLUME" "volume=$CONFIG_VOLUME" || _die "Failed to nuke"
|
||||||
_die "Failed to nuke"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Nuking of ${CONTAINER_NAME} complete."
|
echo "Nuking of ${CONTAINER_NAME} complete."
|
||||||
|
@ -9,9 +9,7 @@ _check_required_env_vars
|
|||||||
bash ./uninstall.sh || _die "Failed to uninstall service before restore"
|
bash ./uninstall.sh || _die "Failed to uninstall service before restore"
|
||||||
|
|
||||||
# restore data from backup file
|
# restore data from backup file
|
||||||
if ! autorestore volume=$DATA_VOLUME volume=$CONFIG_VOLUME "$1" "$2"; then
|
autorestore "$1" "$2" "volume=$DATA_VOLUME" "volume=$CONFIG_VOLUME" || _die "Failed to restore data from backup file"
|
||||||
_die "Failed to restore data from backup file"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# reinstall service
|
# reinstall service
|
||||||
bash ./install.sh || _die "Failed to reinstall service after restore"
|
bash ./install.sh || _die "Failed to reinstall service after restore"
|
||||||
|
@ -1,27 +1,99 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
MYID=$(id -u)
|
||||||
|
MYGRP=$(id -g)
|
||||||
|
|
||||||
_autocommandrun() {
|
_autocommandrun_volume() {
|
||||||
command="$1"
|
command="$1"
|
||||||
key="$2"
|
volume_name="$2"
|
||||||
value="$3"
|
|
||||||
|
|
||||||
# only passed through if command is backup or restore.
|
case "$command" in
|
||||||
backup_file="$4"
|
create)
|
||||||
temp_path="$5"
|
echo "Creating volume ${volume_name}"
|
||||||
|
docker volume create ${volume_name}
|
||||||
case "$key" in
|
|
||||||
volume)
|
|
||||||
echo "Volume: $value"
|
|
||||||
;;
|
;;
|
||||||
path)
|
nuke)
|
||||||
echo "Path: $value"
|
echo "Nuking volume ${volume_name}"
|
||||||
|
docker volume rm ${volume_name}
|
||||||
;;
|
;;
|
||||||
file)
|
backup)
|
||||||
echo "File: $value"
|
local backup_folder="$3"
|
||||||
|
echo "Backing up volume ${volume_name}"
|
||||||
|
docker run --rm -v ${volume_name}:/volume -v ${backup_folder}:/backup debian bash -c "tar -czvf /backup/backup.tgz -C /volume . && chown -R $MYID:$MYGRP /backup"
|
||||||
;;
|
;;
|
||||||
*)
|
restore)
|
||||||
_die "Unknown key $key passed to auto${command}. We only support volume, path and file."
|
local backup_folder="$3"
|
||||||
|
echo "Restoring volume ${volume_name}"
|
||||||
|
docker volume rm ${volume_name}
|
||||||
|
docker volume create ${volume_name}
|
||||||
|
docker run --rm -v ${volume_name}:/volume -v ${backup_folder}:/backup debian bash -c "tar -xzvf /backup/backup.tgz -C /volume --strip-components=1"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
_autocommandrun_path() {
|
||||||
|
command="$1"
|
||||||
|
path="$2"
|
||||||
|
|
||||||
|
case "$command" in
|
||||||
|
create)
|
||||||
|
echo "Creating path ${path}"
|
||||||
|
mkdir -p ${path}
|
||||||
|
;;
|
||||||
|
nuke)
|
||||||
|
echo "Nuking path ${path}"
|
||||||
|
PATHPARENT=$(dirname ${path})
|
||||||
|
PATHCHILD=$(basename ${path})
|
||||||
|
if [ -d "${PATHPARENT}/${PATHCHILD}" ]; then
|
||||||
|
docker run --rm -v ${PATHPARENT}:/volume debian bash -c "rm -rf /volume/${PATHCHILD}" || echo "Failed to nuke path ${path}"
|
||||||
|
else
|
||||||
|
echo "Path ${path} does not exist - nothing to nuke"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
backup)
|
||||||
|
local backup_folder="$3"
|
||||||
|
echo "Backing up path ${path}"
|
||||||
|
if [ -d "${path}" ]; then
|
||||||
|
docker run --rm -v ${path}:/path -v ${backup_folder}:/backup debian bash -c "tar -czvf /backup/backup.tgz -C /path . && chown -R $MYID:$MYGRP /backup"
|
||||||
|
else
|
||||||
|
echo "Path ${path} does not exist - nothing to backup"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
restore)
|
||||||
|
local backup_folder="$3"
|
||||||
|
echo "Restoring path ${path}"
|
||||||
|
tar -xzvf ${backup_folder}/backup.tgz -C ${path} --strip-components=1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
_autocommandrun_file() {
|
||||||
|
command="$1"
|
||||||
|
value="$2"
|
||||||
|
|
||||||
|
case "$command" in
|
||||||
|
create)
|
||||||
|
;;
|
||||||
|
nuke)
|
||||||
|
rm -f ${value}
|
||||||
|
;;
|
||||||
|
backup)
|
||||||
|
local backup_folder="$3"
|
||||||
|
echo "Backing up file ${value}"
|
||||||
|
|
||||||
|
FILEPARENT=$(dirname ${value})
|
||||||
|
FILENAME=$(basename ${value})
|
||||||
|
if [ -f "${FILEPARENT}/${FILENAME}" ]; then
|
||||||
|
docker run --rm-v ${FILEPARENT}:/volume -v ${backup_folder}:/backup debian bash -c "cp /volume/${FILENAME} /backup/${FILENAME} && chown -R $MYID:$MYGRP /backup"
|
||||||
|
else
|
||||||
|
echo "File ${value} does not exist - nothing to backup"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
restore)
|
||||||
|
local backup_folder="$3"
|
||||||
|
echo "Restoring file ${value}"
|
||||||
|
local filename=$(basename ${value})
|
||||||
|
cp ${backup_folder}/${filename} ${value}
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
@ -35,19 +107,22 @@ _autocommandparse() {
|
|||||||
# value is the path or volume name.
|
# value is the path or volume name.
|
||||||
|
|
||||||
# we iterate over the key=value arguments, and for each we call:
|
# we iterate over the key=value arguments, and for each we call:
|
||||||
# autorun <command> <key> <value> <backup_file> <temp_path>
|
# autorun <command> <backupfile> <key> <value>
|
||||||
|
|
||||||
local command="$1"
|
local command="$1"
|
||||||
shift
|
shift
|
||||||
|
|
||||||
|
local temp_path="$1"
|
||||||
|
shift
|
||||||
|
|
||||||
|
echo "autocommandparse: command=$command temp_path=$temp_path"
|
||||||
|
|
||||||
# Extract the backup file and temp path (last two arguments)
|
# Extract the backup file and temp path (last two arguments)
|
||||||
local args=("$@")
|
local args=("$@")
|
||||||
local arg_count=${#args[@]}
|
local arg_count=${#args[@]}
|
||||||
local backup_file="${args[$arg_count-2]}"
|
|
||||||
local temp_path="${args[$arg_count-1]}"
|
|
||||||
|
|
||||||
# Process all key=value pairs
|
# Process all key=value pairs
|
||||||
for ((i=0; i<$arg_count-2; i++)); do
|
for ((i=0; i<$arg_count; i++)); do
|
||||||
local pair="${args[$i]}"
|
local pair="${args[$i]}"
|
||||||
|
|
||||||
# Skip if not in key=value format
|
# Skip if not in key=value format
|
||||||
@ -57,30 +132,81 @@ _autocommandparse() {
|
|||||||
|
|
||||||
local key="${pair%%=*}"
|
local key="${pair%%=*}"
|
||||||
local value="${pair#*=}"
|
local value="${pair#*=}"
|
||||||
|
|
||||||
|
# create backup folder unique to key/value.
|
||||||
|
local bfolder="${key}_${value}"
|
||||||
|
|
||||||
|
# remove any non-alphanumeric characters, that aren't dash or underscore from the bfile
|
||||||
|
targetpath=""
|
||||||
|
if [ ! "$temp_path" == "-" ]; then
|
||||||
|
bfolder=$(echo "$bfolder" | tr -cd '[:alnum:]_-')
|
||||||
|
mkdir -p ${temp_path}/${bfolder}
|
||||||
|
targetpath="${temp_path}/${bfolder}"
|
||||||
|
fi
|
||||||
|
|
||||||
# Key must be one of volume, path or file
|
# Key must be one of volume, path or file
|
||||||
_autocommandrun "$command" "$key" "$value" "$backup_file" "$temp_path"
|
case "$key" in
|
||||||
|
volume)
|
||||||
|
_autocommandrun_volume "$command" "$value" "$targetpath"
|
||||||
|
;;
|
||||||
|
path)
|
||||||
|
_autocommandrun_path "$command" "$value" "$targetpath"
|
||||||
|
;;
|
||||||
|
file)
|
||||||
|
_autocommandrun_file "$command" "$value" "$targetpath"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
_die "Unknown key $key passed to auto${command}. We only support volume, path and file."
|
||||||
|
;;
|
||||||
|
esac
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
autocreate() {
|
autocreate() {
|
||||||
_autocommandparse create "$@" "-" "-"
|
_autocommandparse create "-" "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
autonuke() {
|
autonuke() {
|
||||||
_autocommandparse nuke "$@" "-" "-"
|
_autocommandparse nuke "-" "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
autobackup() {
|
autobackup() {
|
||||||
_autocommandparse backup "$@"
|
local backup_file="$1"
|
||||||
|
shift
|
||||||
|
local temp_path="$1"
|
||||||
|
shift
|
||||||
|
|
||||||
|
[ -f "$backup_file" ] && _die "Backup file $backup_file already exists"
|
||||||
|
[ -d "$temp_path" ] || _die "Temp path $temp_path does not exist"
|
||||||
|
|
||||||
|
local backup_temp_path="$temp_path/backup"
|
||||||
|
|
||||||
|
mkdir -p "$backup_temp_path"
|
||||||
|
echo "_autocommandparse [backup] [$backup_temp_path] [$@]"
|
||||||
|
_autocommandparse backup "$backup_temp_path" "$@"
|
||||||
|
|
||||||
|
tar zcvf "$backup_file" -C "$backup_temp_path" .
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
autorestore() {
|
autorestore() {
|
||||||
_autocommandparse restore "$@"
|
local backup_file="$1"
|
||||||
|
shift
|
||||||
|
local temp_path="$1"
|
||||||
|
shift
|
||||||
|
|
||||||
|
[ -f "$backup_file" ] || _die "Backup file $backup_file does not exist"
|
||||||
|
[ -d "$temp_path" ] || _die "Temp path $temp_path does not exist"
|
||||||
|
|
||||||
|
local restore_temp_path="$temp_path/restore"
|
||||||
|
|
||||||
|
mkdir -p "$restore_temp_path"
|
||||||
|
tar zxvf "$backup_file" -C "$restore_temp_path" --strip-components=1
|
||||||
|
|
||||||
|
_autocommandparse restore "$restore_temp_path" "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,6 +5,6 @@ _check_required_env_vars "LOCAL_DATA_FOLDER"
|
|||||||
# Nginx Example Backup Script
|
# Nginx Example Backup Script
|
||||||
# hot backup is fine for nginx website content.
|
# hot backup is fine for nginx website content.
|
||||||
|
|
||||||
autobackup "path=${LOCAL_DATA_FOLDER}" $1 $2 || _die "Failed to create backup"
|
autobackup "$1" "$2" "path=${LOCAL_DATA_FOLDER}" || _die "Failed to create backup"
|
||||||
|
|
||||||
echo "Backup complete"
|
echo "Backup complete"
|
||||||
|
@ -5,11 +5,7 @@ _check_required_env_vars "LOCAL_DATA_FOLDER" "IMAGE_REGISTRY" "IMAGE_REPO" "IMAG
|
|||||||
# Nginx Example Install Script
|
# Nginx Example Install Script
|
||||||
|
|
||||||
# Ensure local data folder exists
|
# Ensure local data folder exists
|
||||||
if [ ! -d "${LOCAL_DATA_FOLDER}" ]; then
|
autocreate "path=${LOCAL_DATA_FOLDER}"
|
||||||
echo "Creating local data folder ${LOCAL_DATA_FOLDER}..."
|
|
||||||
mkdir -p "${LOCAL_DATA_FOLDER}"
|
|
||||||
chmod 777 "${LOCAL_DATA_FOLDER}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Checking Docker installation..."
|
echo "Checking Docker installation..."
|
||||||
_check_docker_installed || _die "Docker test failed, aborting installation..."
|
_check_docker_installed || _die "Docker test failed, aborting installation..."
|
||||||
|
@ -8,7 +8,6 @@ _check_required_env_vars "LOCAL_DATA_FOLDER" "CONTAINER_NAME"
|
|||||||
# Call uninstall script first
|
# Call uninstall script first
|
||||||
./uninstall.sh
|
./uninstall.sh
|
||||||
|
|
||||||
echo "Removing local data folder ${LOCAL_DATA_FOLDER}..."
|
autonuke "path=${LOCAL_DATA_FOLDER}" || _die "Failed to nuke ${LOCAL_DATA_FOLDER}"
|
||||||
rm -rf $LOCAL_DATA_FOLDER || _die "Failed to remove local data folder ${LOCAL_DATA_FOLDER}"
|
|
||||||
|
|
||||||
echo "Nuke complete for service ${CONTAINER_NAME}."
|
echo "Nuke complete for service ${CONTAINER_NAME}."
|
||||||
|
@ -9,7 +9,7 @@ BACKUP_FILE="$1"
|
|||||||
echo "Uninstalling service before restore..."
|
echo "Uninstalling service before restore..."
|
||||||
bash ./uninstall.sh || _die "Failed to uninstall service before restore"
|
bash ./uninstall.sh || _die "Failed to uninstall service before restore"
|
||||||
|
|
||||||
autorestore "path=${LOCAL_DATA_FOLDER}" $1 $2 || _die "Failed to restore data folder from backup"
|
autorestore "$1" "$2" "path=${LOCAL_DATA_FOLDER}" || _die "Failed to restore data folder from backup"
|
||||||
|
|
||||||
echo "Restore complete. Reinstalling service..."
|
echo "Restore complete. Reinstalling service..."
|
||||||
bash ./install.sh || _die "Failed to reinstall service after restore"
|
bash ./install.sh || _die "Failed to reinstall service after restore"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
source "${AGENT_PATH}/_common.sh"
|
source "${AGENT_PATH}/_common.sh"
|
||||||
_check_required_env_vars
|
_check_required_env_vars "VOLUME_NAME"
|
||||||
|
|
||||||
# Simple Object Storage Backup Script
|
# Simple Object Storage Backup Script
|
||||||
# Creates a backup tarball of the volume contents.
|
# Creates a backup tarball of the volume contents.
|
||||||
@ -8,6 +8,6 @@ _check_required_env_vars
|
|||||||
|
|
||||||
|
|
||||||
# HOT backup is fine for simple-object-storage
|
# HOT backup is fine for simple-object-storage
|
||||||
autobackup "volume=${VOLUME_NAME}" $1 $2 || _die "Failed to create backup"
|
autobackup "$1" "$2" "volume=${VOLUME_NAME}" || _die "Failed to create backup"
|
||||||
|
|
||||||
echo "Backup complete: ${BACKUP_FILE}"
|
echo "Backup complete: ${BACKUP_FILE}"
|
||||||
|
@ -5,8 +5,6 @@ _check_required_env_vars
|
|||||||
# Simple Object Storage Install Script
|
# Simple Object Storage Install Script
|
||||||
# Pulls image, creates volume/config, starts container.
|
# Pulls image, creates volume/config, starts container.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
autocreate "volume=${VOLUME_NAME}"
|
autocreate "volume=${VOLUME_NAME}"
|
||||||
|
|
||||||
# check can pull image on remote host and exit if fails
|
# check can pull image on remote host and exit if fails
|
||||||
|
@ -6,4 +6,6 @@ _check_required_env_vars
|
|||||||
# Removes container AND volume.
|
# Removes container AND volume.
|
||||||
|
|
||||||
|
|
||||||
autonuke "volume=${VOLUME_NAME}"
|
autonuke "volume=${VOLUME_NAME}" || _die "Failed to nuke volume ${VOLUME_NAME}"
|
||||||
|
|
||||||
|
echo "Nuke complete for service ${CONTAINER_NAME}."
|
||||||
|
@ -12,9 +12,7 @@ bash ./uninstall.sh || _die "Failed to uninstall service before restore"
|
|||||||
|
|
||||||
echo "Restoring data for ${CONTAINER_NAME} from ${BACKUP_FILE}..."
|
echo "Restoring data for ${CONTAINER_NAME} from ${BACKUP_FILE}..."
|
||||||
|
|
||||||
if ! autorestore "volume=${VOLUME_NAME}" "$1" "$2"; then
|
autorestore "$1" "$2" "volume=${VOLUME_NAME}" || _die "Failed to restore data from backup file"
|
||||||
_die "Failed to restore data from backup file"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Restore complete. Reinstalling service..."
|
echo "Restore complete. Reinstalling service..."
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ _check_required_env_vars "CONTAINER_NAME" "LOCAL_DATA_FOLDER"
|
|||||||
# Stop container before backup
|
# Stop container before backup
|
||||||
_stop_container "$CONTAINER_NAME"
|
_stop_container "$CONTAINER_NAME"
|
||||||
|
|
||||||
autobackup "path=${LOCAL_DATA_FOLDER}" $1 $2 || _die "Failed to create backup"
|
autobackup "$1" "$2" "path=${LOCAL_DATA_FOLDER}" || _die "Failed to create backup"
|
||||||
|
|
||||||
# Start container after backup
|
# Start container after backup
|
||||||
_start_container "$CONTAINER_NAME"
|
_start_container "$CONTAINER_NAME"
|
||||||
|
@ -2,5 +2,5 @@
|
|||||||
# (can also override anything in the _basic.env file in the template to make it specific to this server)
|
# (can also override anything in the _basic.env file in the template to make it specific to this server)
|
||||||
|
|
||||||
HOST_PORT=80
|
HOST_PORT=80
|
||||||
LOCAL_DATA_FOLDER="${HOME}/.sk"
|
LOCAL_DATA_FOLDER="/home/dropshell/example-squashkiwi"
|
||||||
IMAGE_TAG="latest"
|
IMAGE_TAG="latest"
|
||||||
|
8
templates/squashkiwi/nuke.sh
Normal file
8
templates/squashkiwi/nuke.sh
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
source "${AGENT_PATH}/_common.sh"
|
||||||
|
_check_required_env_vars "LOCAL_DATA_FOLDER"
|
||||||
|
|
||||||
|
autonuke "path=${LOCAL_DATA_FOLDER}" || _die "Failed to nuke ${LOCAL_DATA_FOLDER}"
|
||||||
|
|
||||||
|
echo "Nuke of ${CONTAINER_NAME} complete"
|
||||||
|
|
@ -10,7 +10,7 @@ _check_required_env_vars "CONTAINER_NAME" "LOCAL_DATA_FOLDER"
|
|||||||
# # Stop container before backup
|
# # Stop container before backup
|
||||||
bash ./uninstall.sh || _die "Failed to uninstall service before restore"
|
bash ./uninstall.sh || _die "Failed to uninstall service before restore"
|
||||||
|
|
||||||
autorestore "path=${LOCAL_DATA_FOLDER}" $1 $2 || _die "Failed to restore data folder from backup"
|
autorestore "$1" "$2" "path=${LOCAL_DATA_FOLDER}" || _die "Failed to restore data folder from backup"
|
||||||
|
|
||||||
# reinstall service
|
# reinstall service
|
||||||
bash ./install.sh || _die "Failed to reinstall service after restore"
|
bash ./install.sh || _die "Failed to reinstall service after restore"
|
||||||
|
@ -12,8 +12,5 @@ _remove_container $CONTAINER_NAME || _die "Failed to remove container ${CONTAINE
|
|||||||
_is_container_running && _die "Couldn't stop existing container"
|
_is_container_running && _die "Couldn't stop existing container"
|
||||||
_is_container_exists && _die "Couldn't remove existing container"
|
_is_container_exists && _die "Couldn't remove existing container"
|
||||||
|
|
||||||
# remove the image
|
|
||||||
docker rmi "$IMAGE_REGISTRY/$IMAGE_REPO:$IMAGE_TAG" || echo "Failed to remove image $IMAGE_REGISTRY/$IMAGE_REPO:$IMAGE_TAG"
|
|
||||||
|
|
||||||
echo "Uninstallation of ${CONTAINER_NAME} complete."
|
echo "Uninstallation of ${CONTAINER_NAME} complete."
|
||||||
echo "Local data folder ${LOCAL_DATA_FOLDER} still in place."
|
echo "Local data folder ${LOCAL_DATA_FOLDER} still in place."
|
||||||
|
@ -1,23 +1,49 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# default config should always work for localhost
|
|
||||||
|
|
||||||
SCRIPT_DIR=$(dirname "$0")
|
SCRIPT_DIR=$(dirname "$0")
|
||||||
TEMPLATE="$1"
|
|
||||||
|
|
||||||
# make sure TEMPLATE doesn't end with a /
|
# default config should always work for localhost
|
||||||
TEMPLATE=$(basename "$TEMPLATE")
|
|
||||||
|
|
||||||
function die() {
|
function die() {
|
||||||
echo "$1"
|
echo "$1"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
function title() {
|
function dashes() {
|
||||||
echo "----------------------------------------"
|
for ((i=0; i<$1; i++)); do
|
||||||
echo "$1"
|
echo -n "-"
|
||||||
echo "----------------------------------------"
|
done
|
||||||
|
echo ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function centerprint() {
|
||||||
|
# print $1 centered
|
||||||
|
local width=$2
|
||||||
|
local padding=$(( (width - ${#1}) / 2 ))
|
||||||
|
for ((i=0; i<$padding; i++)); do
|
||||||
|
echo -n " "
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
function title() {
|
||||||
|
# determine terminal width
|
||||||
|
TERMINAL_WIDTH=$(tput cols)
|
||||||
|
|
||||||
|
echo " "
|
||||||
|
dashes $TERMINAL_WIDTH
|
||||||
|
centerprint "$1" $TERMINAL_WIDTH
|
||||||
|
dashes $TERMINAL_WIDTH
|
||||||
|
}
|
||||||
|
|
||||||
|
# do we have the first argument?
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
echo "Usage: $0 <template>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
TEMPLATE=$(basename "$1")
|
||||||
|
|
||||||
if [ ! -d "$SCRIPT_DIR/$TEMPLATE" ]; then
|
if [ ! -d "$SCRIPT_DIR/$TEMPLATE" ]; then
|
||||||
echo "Local Template $TEMPLATE does not exist"
|
echo "Local Template $TEMPLATE does not exist"
|
||||||
exit 1
|
exit 1
|
||||||
@ -27,16 +53,6 @@ fi
|
|||||||
|
|
||||||
title "Checking template $TEMPLATE"
|
title "Checking template $TEMPLATE"
|
||||||
|
|
||||||
|
|
||||||
HASH1=$(ds hash "$SCRIPT_DIR/$TEMPLATE")
|
|
||||||
HASH2=$(ds hash "/opt/dropshell/templates/$TEMPLATE")
|
|
||||||
|
|
||||||
if [ "$HASH1" != "$HASH2" ]; then
|
|
||||||
echo "Template $TEMPLATE is out of date"
|
|
||||||
echo "Need to run build.sh, and install locally."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
SERVICE_NAME="test-$TEMPLATE"
|
SERVICE_NAME="test-$TEMPLATE"
|
||||||
|
|
||||||
title "Creating service"
|
title "Creating service"
|
||||||
@ -58,7 +74,7 @@ title "Backing up service"
|
|||||||
ds backup localhost $SERVICE_NAME || die "Failed to backup service"
|
ds backup localhost $SERVICE_NAME || die "Failed to backup service"
|
||||||
|
|
||||||
title "Restoring service"
|
title "Restoring service"
|
||||||
ds restore localhost $SERVICE_NAME || die "Failed to restore service"
|
ds restore localhost $SERVICE_NAME latest || die "Failed to restore service"
|
||||||
|
|
||||||
title "Checking status"
|
title "Checking status"
|
||||||
ds status localhost $SERVICE_NAME || die "Failed to check status"
|
ds status localhost $SERVICE_NAME || die "Failed to check status"
|
||||||
@ -70,4 +86,8 @@ title "Listing services"
|
|||||||
ds list localhost || die "Failed to list services"
|
ds list localhost || die "Failed to list services"
|
||||||
|
|
||||||
|
|
||||||
|
# change to green font
|
||||||
|
echo -e "\033[32m"
|
||||||
|
title "ALL TESTS PASSED FOR $TEMPLATE"
|
||||||
|
echo -e "\033[0m"
|
||||||
|
echo " "
|
||||||
|
Loading…
x
Reference in New Issue
Block a user