From 61fabac1f0c71aa03c411abe5e4c10e3bda7c28a Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 25 Apr 2025 11:44:32 +1200 Subject: [PATCH] Watchtower! --- src/servers.cpp | 4 ++- src/service_runner.cpp | 31 +++++++++++++++++++++--- src/service_runner.hpp | 1 + templates/watchtower/example/service.env | 4 +++ templates/watchtower/start.sh | 5 ++-- 5 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/servers.cpp b/src/servers.cpp index b0e6713..7845cff 100644 --- a/src/servers.cpp +++ b/src/servers.cpp @@ -94,12 +94,14 @@ void show_server_details(const std::string& server_name) { //--------------------- // Check if server is reachable via SSH std::string ssh_address = env.get_SSH_HOST(); + std::string ssh_user = env.get_SSH_USER(); + std::string ssh_port = env.get_SSH_PORT(); if (!ssh_address.empty()) { std::cout << std::endl << "Server Status:" << std::endl; std::cout << std::string(40, '-') << std::endl; // Try to connect to the server - std::string cmd = "ssh -o ConnectTimeout=5 " + ssh_address + " 'echo connected' 2>/dev/null"; + std::string cmd = "ssh -o ConnectTimeout=5 " + ssh_user + "@" + ssh_address + " -p " + ssh_port + " 'echo connected' 2>/dev/null"; int result = system(cmd.c_str()); if (result == 0) { std::cout << "Status: Online" << std::endl; diff --git a/src/service_runner.cpp b/src/service_runner.cpp index b4aba3f..21c3a3a 100644 --- a/src/service_runner.cpp +++ b/src/service_runner.cpp @@ -59,7 +59,7 @@ std::string service_runner::construct_ssh_cmd() const { bool service_runner::check_remote_dir_exists(const std::string& dir_path) const { std::string check_dir_cmd = construct_ssh_cmd() + "'test -d " + dir_path + "'"; if (system(check_dir_cmd.c_str()) != 0) { - std::cerr << "Error: Directory not found on remote server:" << dir_path << std::endl; + std::cerr << "Error: Directory not found on remote server:" << fs::path(dir_path).filename().string() << std::endl; return false; } return true; @@ -68,7 +68,25 @@ bool service_runner::check_remote_dir_exists(const std::string& dir_path) const bool service_runner::check_remote_file_exists(const std::string& file_path) const { std::string check_cmd = construct_ssh_cmd() + "'test -f " + file_path + "'"; if (system(check_cmd.c_str()) != 0) { - std::cerr << "Error: File not found on remote server: " << file_path << std::endl; + std::cerr << "Error: File not found on remote server: " << fs::path(file_path).filename().string() << std::endl; + return false; + } + return true; +} + +bool service_runner::check_remote_items_exist(const std::vector &file_paths) const +{ + // convert file_paths to a single string, separated by spaces + std::string file_paths_str; + std::string file_names_str; + for (const auto& file_path : file_paths) { + file_paths_str += file_path + " "; + file_names_str += fs::path(file_path).filename().string() + " "; + } + // check if all items in the vector exist on the remote server, in a single command. + std::string check_cmd = construct_ssh_cmd() + "'for item in " + file_paths_str + "; do test -f $item; done'"; + if (system(check_cmd.c_str()) != 0) { + std::cerr << "Error: Required items not found on remote server: " << file_names_str << std::endl; return false; } return true; @@ -208,8 +226,12 @@ bool service_runner::backup() { } // Check if basic installed stuff is in place. - if (!check_remote_dir_exists(mRemote_service_path) || !check_remote_file_exists(script_path) || !check_remote_file_exists(mRemote_service_env_file)) + if (!check_remote_items_exist({mRemote_service_path,script_path,mRemote_service_env_file})) + { + std::cerr << "Error: Required service directories not found on remote server" << std::endl; + std::cerr << "Is the service installed?" << std::endl; return false; + } // Create backups directory on server if it doesn't exist std::string server_backups_dir = m_server_env->get_DROPSHELL_DIR() + "/backups"; @@ -270,11 +292,12 @@ service_runner::HealthStatus service_runner::is_healthy() std::string script_path = mRemote_service_template_path + "/" + command + ".sh"; if (!check_remote_file_exists(script_path)) { + std::cerr << "Service is not installed: " << m_service_info.service_name << std::endl; return HealthStatus::NOTINSTALLED; } // Run status script, does not display output. - bool ok = execute_ssh_command("'cd " + mRemote_service_template_path + " && /bin/bash _status.sh " + mRemote_service_env_file + " > /dev/null 2>&1'", ""); + bool ok = execute_ssh_command("'cd " + mRemote_service_template_path + " && /bin/bash " + script_path + " " + mRemote_service_env_file + " > /dev/null 2>&1'", ""); if (!ok) return HealthStatus::UNHEALTHY; diff --git a/src/service_runner.hpp b/src/service_runner.hpp index 0ba2218..39b2b3d 100644 --- a/src/service_runner.hpp +++ b/src/service_runner.hpp @@ -77,6 +77,7 @@ class service_runner { std::string construct_ssh_cmd() const; bool check_remote_dir_exists(const std::string& dir_path) const; bool check_remote_file_exists(const std::string& file_path) const; + bool check_remote_items_exist(const std::vector& file_paths) const; bool execute_ssh_command(const std::string& command, const std::string& error_msg) const; bool execute_local_command(const std::string& command, const std::string& error_msg) const; void maketitle(const std::string& title) const; diff --git a/templates/watchtower/example/service.env b/templates/watchtower/example/service.env index a458a44..3577c8b 100644 --- a/templates/watchtower/example/service.env +++ b/templates/watchtower/example/service.env @@ -3,6 +3,10 @@ TEMPLATE=watchtower CONTAINER_NAME=watchtower +# Interval in seconds between checks. +# Default is 1800 seconds (30 minutes). +INTERVAL=1800 + # Image settings IMAGE_REGISTRY="docker.io" IMAGE_REPO="containrrr/watchtower" diff --git a/templates/watchtower/start.sh b/templates/watchtower/start.sh index 40dd08d..f456de1 100755 --- a/templates/watchtower/start.sh +++ b/templates/watchtower/start.sh @@ -3,13 +3,14 @@ source "$(dirname "$0")/_common.sh" load_env "$1" || die "Failed to load environment variables" # Required environment variables -check_required_env_vars "CONTAINER_NAME" +check_required_env_vars "CONTAINER_NAME" "INTERVAL" DOCKER_RUN_CMD="docker run -d \ --restart unless-stopped \ --name ${CONTAINER_NAME} \ -v /var/run/docker.sock:/var/run/docker.sock \ - ${IMAGE_REGISTRY}/${IMAGE_REPO}:${IMAGE_TAG}" + ${IMAGE_REGISTRY}/${IMAGE_REPO}:${IMAGE_TAG} \ + --interval ${INTERVAL}" if ! create_and_start_container "$DOCKER_RUN_CMD" "$CONTAINER_NAME"; then echo "RUN_CMD failed:"