diff --git a/src/main.cpp b/src/main.cpp index 68bb70a..ed8f816 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -30,6 +30,7 @@ void print_help() { std::cout << std::endl; std::cout << " install SERVER [SERVICE] Install/Update service(s)." << std::endl; std::cout << " backup SERVER [SERVICE] Backup service(s)." << std::endl; + std::cout << " uninstall SERVER [SERVICE] Uninstall service(s)." << std::endl; std::cout << " COMMAND SERVER [SERVICE] Run a custom command on service(s)." << std::endl; std::cout << std::endl; } @@ -120,7 +121,7 @@ int main(int argc, char* argv[]) { commands.insert("init"); if (cfg->is_config_set()) commands.merge(std::set{ - "servers","templates","install","backup" + "servers","templates","install","uninstall","backup" }); for (const auto& command : commands) { @@ -181,11 +182,11 @@ int main(int argc, char* argv[]) { return 0; } - if (cmd == "install") { + if (cmd == "install" || cmd == "uninstall") { std::string server_name; std::vector servicelist; if (!parseargs(safearg(argc, argv, 2), safearg(argc, argv, 3), server_name, servicelist)) { - std::cerr << "Error: install command requires server name and optionally service name" << std::endl; + std::cerr << "Error: " << cmd << " command requires server name and optionally service name" << std::endl; return 1; } for (const auto& service_info : servicelist) { @@ -194,9 +195,9 @@ int main(int argc, char* argv[]) { std::cerr << "Error: Failed to initialize service" << std::endl; return 1; } - - if (!service.install()) { - std::cerr << "Error: Failed to install service" << std::endl; + bool success = ((cmd=="install") ? service.install() : service.uninstall()); + if (!success) { + std::cerr << "Error: Failed to " << cmd << " service" << std::endl; return 1; } } diff --git a/src/service_runner.cpp b/src/service_runner.cpp index 21c3a3a..01338e4 100644 --- a/src/service_runner.cpp +++ b/src/service_runner.cpp @@ -175,6 +175,52 @@ bool service_runner::install() { return true; } +bool service_runner::uninstall() { + maketitle("Uninstalling " + m_service_info.service_name + " (" + m_service_info.template_name + ") on " + m_server_name); + + if (!m_server_env) { + std::cerr << "Error: Server service not initialized" << std::endl; + return false; + } + + // 1. Check if template exists + template_info tinfo; + if (!get_template_info(m_service_info.template_name, tinfo)) { + std::cerr << "Error: Template '" << m_service_info.template_name << "' not found" << std::endl; + return false; + } + + // 2. Check if service directory exists on server + if (!check_remote_dir_exists(mRemote_service_path)) { + std::cerr << "Service is not installed: " << m_service_info.service_name << std::endl; + return true; // Nothing to uninstall + } + + // 3. Run uninstall script if it exists + std::string uninstall_script = mRemote_service_template_path + "/_uninstall.sh"; + bool script_exists = check_remote_file_exists(uninstall_script); + + if (script_exists) { + std::string uninstall_cmd = "'cd " + mRemote_service_template_path + + " && /bin/bash _uninstall.sh " + mRemote_service_env_file + "'"; + if (!execute_ssh_command(uninstall_cmd, "Failed to run uninstall script")) { + std::cerr << "Warning: Uninstall script failed, but continuing with directory removal" << std::endl; + } + } else { + std::cerr << "Warning: No uninstall script found. Unable to uninstall service." << std::endl; + return false; + } + + // 4. Remove the service directory from the server + std::string rm_cmd = "'rm -rf " + mRemote_service_path + "'"; + if (!execute_ssh_command(rm_cmd, "Failed to remove service directory")) { + return false; + } + + std::cout << "Service " << m_service_info.service_name << " successfully uninstalled from " << m_server_name << std::endl; + return true; +} + bool service_runner::run_command(const std::string& command) { if (!m_server_env) { std::cerr << "Error: Server service not initialized" << std::endl; diff --git a/src/service_runner.hpp b/src/service_runner.hpp index 39b2b3d..328075e 100644 --- a/src/service_runner.hpp +++ b/src/service_runner.hpp @@ -20,11 +20,19 @@ class service_runner { // 1. check if the server_name exists, and the service_name refers to a valid template // 2. check if service_name is valid for the server_name // 3. create the service directory on the server at {DROPSHELL_DIR}/{service_name} - // 3. copy the template files into that service directory/template (from the templates directory for the specified server, using templates.hpp to identify the path) - // 4. copying the {service_name}.env file to the service directory (from the server directory for the specified server) + // 3. copy the template files into {DROPSHELL_DIR}/{service_name}/template (from the templates directory for the specified server, using templates.hpp to identify the path) + // 4. copying the local service directory into {DROPSHELL_DIR}/{service_name}/config (from the server directory for the specified server) // 5. running the install.sh script on the server, passing the {service_name}.env file as an argument bool install(); + // uninstall the service over ssh, using the credentials from server.env (via server_env.hpp) + // 1. check if the server_name exists, and the service_name refers to a valid template + // 2. check if service_name is valid for the server_name + // 3. run the uninstall.sh script on the server, passing the {service_name}.env file as an argument + // 4. + // 1. run the uninstall.sh script on the server, passing the {service_name}.env file as an argument + bool uninstall(); + // run a command over ssh, using the credentials from server.env (via server_env.hpp) // first check that the command corresponds to a valid .sh file in the service directory // then run the command, passing the {service_name}.env file as an argument diff --git a/templates/example/_install.sh b/templates/example/_install.sh index 2798d32..19237e7 100755 --- a/templates/example/_install.sh +++ b/templates/example/_install.sh @@ -3,11 +3,19 @@ source "$(dirname "$0")/_common.sh" load_env "$1" || die "Failed to load environment variables" # Required environment variables -check_required_env_vars "CONTAINER_NAME" "IMAGE_REGISTRY" "IMAGE_REPO" "IMAGE_TAG" +check_required_env_vars "CONTAINER_NAME" "IMAGE_REGISTRY" "IMAGE_REPO" "IMAGE_TAG" "LOCAL_DATA_FOLDER" # Test Docker _check_docker_installed || die "Docker test failed, aborting installation..." +# Create local data folder if it doesn't exist +if [ -d "${LOCAL_DATA_FOLDER}" ]; then + echo "Local data folder ${LOCAL_DATA_FOLDER} exists, using existing data." +else + echo "Local data folder ${LOCAL_DATA_FOLDER} does not exist, creating..." + mkdir -p "${LOCAL_DATA_FOLDER}" +fi + # check can pull image on remote host and exit if fails docker pull "$IMAGE_REGISTRY/$IMAGE_REPO:$IMAGE_TAG" || die "Failed to pull image $IMAGE_REGISTRY/$IMAGE_REPO:$IMAGE_TAG" diff --git a/templates/example/_uninstall.sh b/templates/example/_uninstall.sh new file mode 100644 index 0000000..cbfe0dd --- /dev/null +++ b/templates/example/_uninstall.sh @@ -0,0 +1,14 @@ +#!/bin/bash +source "$(dirname "$0")/_common.sh" +load_env "$1" || die "Failed to load environment variables" + +# Required environment variables +check_required_env_vars "CONTAINER_NAME" "IMAGE_REGISTRY" "IMAGE_REPO" "IMAGE_TAG" + +_remove_container $CONTAINER_NAME || die "Failed to remove container ${CONTAINER_NAME}" + +# 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 "Leaving local data folder ${LOCAL_DATA_FOLDER} in place." diff --git a/templates/watchtower/_uninstall.sh b/templates/watchtower/_uninstall.sh new file mode 100644 index 0000000..8dab330 --- /dev/null +++ b/templates/watchtower/_uninstall.sh @@ -0,0 +1,14 @@ +#!/bin/bash +source "$(dirname "$0")/_common.sh" +load_env "$1" || die "Failed to load environment variables" + +# Required environment variables +check_required_env_vars "CONTAINER_NAME" "IMAGE_REGISTRY" "IMAGE_REPO" "IMAGE_TAG" + +_remove_container $CONTAINER_NAME || die "Failed to remove container ${CONTAINER_NAME}" + +# 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." +