feat: Update 5 and remove 1 files
This commit is contained in:
@@ -1,170 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# This script checks ALL services on the server and returns a status for each.
|
|
||||||
|
|
||||||
# Return format is simple ENV with the following format:
|
|
||||||
# SERVICE_NAME_HEALTH=healthy|unhealthy|unknown
|
|
||||||
# SERVICE_NAME_PORTS=port1,port2,port3
|
|
||||||
|
|
||||||
# Get all services on the server
|
|
||||||
SCRIPT_DIR="$(dirname "$0")"
|
|
||||||
|
|
||||||
# export the passed variables from C++, so subshell can use them.
|
|
||||||
export AGENT_PATH="${AGENT_PATH}"
|
|
||||||
export HOST_NAME="${HOST_NAME}"
|
|
||||||
export SERVER="${SERVER}"
|
|
||||||
|
|
||||||
# //------------------------------------------------------------------------------------------------
|
|
||||||
# // remote paths
|
|
||||||
# // DROPSHELL_DIR
|
|
||||||
# // |-- backups
|
|
||||||
# // |-- temp_files
|
|
||||||
# // |-- agent
|
|
||||||
# // | |-- bb64
|
|
||||||
# // | |-- (other agent files, including _allservicesstatus.sh)
|
|
||||||
# // |-- services
|
|
||||||
# // |-- service name
|
|
||||||
# // |-- config
|
|
||||||
# // |-- service.env (actual service config)
|
|
||||||
# // |-- .template_info.env
|
|
||||||
# // |-- template
|
|
||||||
# // |-- (script files)
|
|
||||||
# // |-- config
|
|
||||||
# // |-- service.env (default service config)
|
|
||||||
# // |-- .template_info.env
|
|
||||||
# // |-- (other config files for specific server&service)
|
|
||||||
|
|
||||||
# Get all services on the server
|
|
||||||
SERVICES_PATH=$(realpath "${SCRIPT_DIR}/../services/")
|
|
||||||
|
|
||||||
CURRENT_OUTPUT=""
|
|
||||||
CURRENT_EXIT_CODE=0
|
|
||||||
|
|
||||||
load_dotenv(){
|
|
||||||
local file_path=$1
|
|
||||||
if [ -f "${file_path}" ]; then
|
|
||||||
# shellcheck source=/dev/null
|
|
||||||
source "${file_path}"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
_check_required_env_vars_allservicesstatus() {
|
|
||||||
local required_vars=("$@")
|
|
||||||
for var in "${required_vars[@]}"; do
|
|
||||||
if [ -z "${!var}" ]; then
|
|
||||||
_die "Required environment variable $var is not set"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
function run_command() {
|
|
||||||
local service_path=$1
|
|
||||||
local command=$2
|
|
||||||
local capture_output=${3:-false} # default to false if not specified
|
|
||||||
|
|
||||||
# check if the command is a file
|
|
||||||
if [ ! -f "${service_path}/template/${command}.sh" ]; then
|
|
||||||
return;
|
|
||||||
fi
|
|
||||||
|
|
||||||
# run the command in a subshell to prevent environment changes
|
|
||||||
# This subshell creates a completely isolated environment for each service
|
|
||||||
CURRENT_OUTPUT=$(
|
|
||||||
# Start with a clean environment for each service
|
|
||||||
set -a
|
|
||||||
|
|
||||||
# First set the core variables that other env vars might reference
|
|
||||||
export SERVER="${SERVER}"
|
|
||||||
export SERVICE_NAME="" # Will be set from .template_info.env
|
|
||||||
export SERVICE="" # Will be set after loading SERVICE_NAME
|
|
||||||
export DOCKER_CLI_HINTS=false
|
|
||||||
export AGENT_PATH="${AGENT_PATH}"
|
|
||||||
export HOST_NAME="${HOST_NAME}"
|
|
||||||
|
|
||||||
# Load the template info first to get SERVICE_NAME and TEMPLATE
|
|
||||||
load_dotenv "${service_path}/config/.template_info.env"
|
|
||||||
|
|
||||||
# Now set SERVICE from SERVICE_NAME (critical for CONTAINER_NAME expansion)
|
|
||||||
export SERVICE="${SERVICE_NAME}"
|
|
||||||
|
|
||||||
# Set CONFIG_PATH before loading service.env as it might be referenced
|
|
||||||
export CONFIG_PATH="${service_path}/config"
|
|
||||||
|
|
||||||
# Now load service.env which can properly expand variables like CONTAINER_NAME=${SERVICE}
|
|
||||||
load_dotenv "${service_path}/config/service.env"
|
|
||||||
|
|
||||||
set +a
|
|
||||||
|
|
||||||
_check_required_env_vars_allservicesstatus "CONFIG_PATH" "SERVER" "SERVICE" "AGENT_PATH" "HOST_NAME" "TEMPLATE"
|
|
||||||
|
|
||||||
if [ "$capture_output" = "true" ]; then
|
|
||||||
# Capture and return output
|
|
||||||
bash "${service_path}/template/${command}.sh" 2>&1
|
|
||||||
else
|
|
||||||
# Run silently and return exit code
|
|
||||||
bash "${service_path}/template/${command}.sh" > /dev/null 2>&1
|
|
||||||
fi
|
|
||||||
)
|
|
||||||
CURRENT_EXIT_CODE=$?
|
|
||||||
}
|
|
||||||
|
|
||||||
function command_exists() {
|
|
||||||
local service_path=$1
|
|
||||||
local command=$2
|
|
||||||
if [ ! -f "${service_path}/template/${command}.sh" ]; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if [ ! -d "${SERVICES_PATH}" ]; then
|
|
||||||
echo "Services path does not exist: ${SERVICES_PATH}"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Get all service names
|
|
||||||
SERVICE_NAMES=$(ls "${SERVICES_PATH}")
|
|
||||||
|
|
||||||
# Iterate over all service names
|
|
||||||
for SERVICE_NAME in ${SERVICE_NAMES}; do
|
|
||||||
|
|
||||||
SERVICE_PATH=$(realpath "${SERVICES_PATH}/${SERVICE_NAME}")
|
|
||||||
|
|
||||||
#--------------------------------
|
|
||||||
# Get the service health
|
|
||||||
if ! command_exists "${SERVICE_PATH}" "status"; then
|
|
||||||
SERVICE_HEALTH="unknown"
|
|
||||||
else
|
|
||||||
# Debug: capture output to see what's failing
|
|
||||||
run_command "${SERVICE_PATH}" "status" "true"
|
|
||||||
ERROR_OUTPUT="${CURRENT_OUTPUT}"
|
|
||||||
run_command "${SERVICE_PATH}" "status" "false"
|
|
||||||
if [ "${CURRENT_EXIT_CODE}" -eq 0 ]; then
|
|
||||||
SERVICE_HEALTH="healthy"
|
|
||||||
else
|
|
||||||
SERVICE_HEALTH="unhealthy"
|
|
||||||
# Debug: show error for simple-object-server services
|
|
||||||
if [ -f "${SERVICE_PATH}/config/.template_info.env" ]; then
|
|
||||||
source "${SERVICE_PATH}/config/.template_info.env"
|
|
||||||
if [ "${TEMPLATE}" = "simple-object-server" ]; then
|
|
||||||
>&2 echo "DEBUG ${SERVICE_NAME}: ${ERROR_OUTPUT}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
#--------------------------------
|
|
||||||
# Get the service ports
|
|
||||||
if ! command_exists "${SERVICE_PATH}" "ports"; then
|
|
||||||
SERVICE_PORTS=""
|
|
||||||
else
|
|
||||||
run_command "${SERVICE_PATH}" "ports" "true"
|
|
||||||
SERVICE_PORTS="${CURRENT_OUTPUT}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
#--------------------------------
|
|
||||||
# return the health and ports
|
|
||||||
echo "${SERVICE_NAME}_HEALTH=${SERVICE_HEALTH}"
|
|
||||||
echo "${SERVICE_NAME}_PORTS=${SERVICE_PORTS}"
|
|
||||||
done
|
|
@@ -73,7 +73,6 @@ install_bb64
|
|||||||
|
|
||||||
required_files=(
|
required_files=(
|
||||||
"$AGENT_PATH/bb64"
|
"$AGENT_PATH/bb64"
|
||||||
"$AGENT_PATH/_allservicesstatus.sh"
|
|
||||||
"$AGENT_PATH/common.sh"
|
"$AGENT_PATH/common.sh"
|
||||||
"$AGENT_PATH/datacommands.sh"
|
"$AGENT_PATH/datacommands.sh"
|
||||||
)
|
)
|
||||||
|
@@ -1,11 +1,15 @@
|
|||||||
#include "shared_commands.hpp"
|
#include "shared_commands.hpp"
|
||||||
#include <libassert/assert.hpp>
|
#include <libassert/assert.hpp>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <sstream>
|
||||||
|
#include <mutex>
|
||||||
#include "utils/utils.hpp"
|
#include "utils/utils.hpp"
|
||||||
#include "servers.hpp"
|
#include "servers.hpp"
|
||||||
#include "directories.hpp"
|
#include "directories.hpp"
|
||||||
#include "services.hpp"
|
#include "services.hpp"
|
||||||
#include "servers.hpp"
|
#include "servers.hpp"
|
||||||
#include "utils/output.hpp"
|
#include "utils/output.hpp"
|
||||||
|
#include "transwarp.hpp"
|
||||||
|
|
||||||
namespace dropshell
|
namespace dropshell
|
||||||
{
|
{
|
||||||
@@ -140,51 +144,42 @@ namespace dropshell
|
|||||||
std::map<std::string, ServiceStatus> status;
|
std::map<std::string, ServiceStatus> status;
|
||||||
std::string server_name = server_env.get_server_name();
|
std::string server_name = server_env.get_server_name();
|
||||||
|
|
||||||
std::string output;
|
// Get all services for this user on this server
|
||||||
std::string agentpath = remotepath(server_name,user).agent();
|
std::vector<LocalServiceInfo> services = get_server_services_info(server_name);
|
||||||
if (!execute_ssh_command(server_env.get_SSH_INFO(user),
|
|
||||||
sCommand(agentpath, "./_allservicesstatus.sh", {{"HOST_NAME", server_name}, {"SERVER", server_name}, {"AGENT_PATH", agentpath}}),
|
// Filter services for this specific user
|
||||||
cMode::Silent,
|
std::vector<LocalServiceInfo> user_services;
|
||||||
&output))
|
for (const auto& service : services) {
|
||||||
return status;
|
if (service.user == user) {
|
||||||
|
user_services.push_back(service);
|
||||||
std::stringstream ss(output);
|
|
||||||
std::string line;
|
|
||||||
while (std::getline(ss, line))
|
|
||||||
{
|
|
||||||
std::string key, value;
|
|
||||||
std::size_t pos = line.find("=");
|
|
||||||
if (pos != std::string::npos)
|
|
||||||
{
|
|
||||||
key = dequote(trim(line.substr(0, pos)));
|
|
||||||
value = dequote(trim(line.substr(pos + 1)));
|
|
||||||
|
|
||||||
// decode key, it's of format SERVICENAME_[HEALTH|PORTS]
|
|
||||||
std::string service_name = key.substr(0, key.find_last_of("_"));
|
|
||||||
std::string status_type = key.substr(key.find_last_of("_") + 1);
|
|
||||||
|
|
||||||
if (status_type == "HEALTH")
|
|
||||||
{ // healthy|unhealthy|unknown
|
|
||||||
if (value == "healthy")
|
|
||||||
status[service_name].health = HealthStatus::HEALTHY;
|
|
||||||
else if (value == "unhealthy")
|
|
||||||
status[service_name].health = HealthStatus::UNHEALTHY;
|
|
||||||
else if (value == "unknown")
|
|
||||||
status[service_name].health = HealthStatus::UNKNOWN;
|
|
||||||
else
|
|
||||||
status[service_name].health = HealthStatus::ERROR;
|
|
||||||
}
|
|
||||||
else if (status_type == "PORTS")
|
|
||||||
{ // port1,port2,port3
|
|
||||||
std::vector<std::string> ports = string2multi(value);
|
|
||||||
for (const auto &port : ports)
|
|
||||||
{
|
|
||||||
if (port != "unknown")
|
|
||||||
status[service_name].ports.push_back(str2int(port));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use parallel execution to check all services simultaneously
|
||||||
|
// This matches how the list command already works for multiple servers
|
||||||
|
if (user_services.size() > 0) {
|
||||||
|
std::mutex status_mutex;
|
||||||
|
|
||||||
|
// transwarp requires at least 1 thread
|
||||||
|
size_t num_threads = std::max(size_t(1), user_services.size());
|
||||||
|
transwarp::parallel exec{num_threads};
|
||||||
|
auto task = transwarp::for_each(exec, user_services.begin(), user_services.end(),
|
||||||
|
[&status, &status_mutex, &server_name](const LocalServiceInfo& service) {
|
||||||
|
ServiceStatus service_status;
|
||||||
|
|
||||||
|
// Get health status using the existing is_healthy function
|
||||||
|
service_status.health = is_healthy(server_name, service.service_name);
|
||||||
|
|
||||||
|
// Get ports using the new get_ports function
|
||||||
|
service_status.ports = get_ports(server_name, service.service_name);
|
||||||
|
|
||||||
|
// Thread-safe update of the status map
|
||||||
|
std::lock_guard<std::mutex> lock(status_mutex);
|
||||||
|
status[service.service_name] = service_status;
|
||||||
|
});
|
||||||
|
|
||||||
|
task->wait();
|
||||||
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@@ -258,6 +253,53 @@ namespace dropshell
|
|||||||
return HealthStatus::HEALTHY;
|
return HealthStatus::HEALTHY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// get_ports : SHARED COMMAND
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
std::vector<int> get_ports(const std::string &server, const std::string &service)
|
||||||
|
{
|
||||||
|
std::vector<int> ports;
|
||||||
|
|
||||||
|
ServerConfig env(server);
|
||||||
|
if (!env.is_valid())
|
||||||
|
{
|
||||||
|
error << "Server service not initialized" << std::endl;
|
||||||
|
return ports;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string user = env.get_user_for_service(service);
|
||||||
|
|
||||||
|
// Check if ports script exists
|
||||||
|
std::string script_path = remotepath(server,user).service_template(service) + "/ports.sh";
|
||||||
|
if (!env.check_remote_file_exists(script_path, user))
|
||||||
|
{
|
||||||
|
return ports; // No ports script, return empty
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run ports script and capture output
|
||||||
|
std::string output;
|
||||||
|
if (env.run_remote_template_command_and_capture_output(service, "ports", {}, output, true, {}))
|
||||||
|
{
|
||||||
|
// Parse the output - expecting comma-separated or newline-separated port numbers
|
||||||
|
std::stringstream ss(output);
|
||||||
|
std::string port_str;
|
||||||
|
while (ss >> port_str)
|
||||||
|
{
|
||||||
|
// Remove any commas
|
||||||
|
port_str.erase(std::remove(port_str.begin(), port_str.end(), ','), port_str.end());
|
||||||
|
try {
|
||||||
|
int port = std::stoi(port_str);
|
||||||
|
if (port > 0 && port <= 65535)
|
||||||
|
ports.push_back(port);
|
||||||
|
} catch (...) {
|
||||||
|
// Ignore non-numeric entries
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ports;
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// healthmark : SHARED COMMAND
|
// healthmark : SHARED COMMAND
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
@@ -67,6 +67,7 @@ namespace dropshell
|
|||||||
std::string healthtick(const std::string &server, const std::string &service);
|
std::string healthtick(const std::string &server, const std::string &service);
|
||||||
std::string HealthStatus2String(HealthStatus status);
|
std::string HealthStatus2String(HealthStatus status);
|
||||||
HealthStatus is_healthy(const std::string &server, const std::string &service);
|
HealthStatus is_healthy(const std::string &server, const std::string &service);
|
||||||
|
std::vector<int> get_ports(const std::string &server, const std::string &service);
|
||||||
std::string healthmark(const std::string &server, const std::string &service);
|
std::string healthmark(const std::string &server, const std::string &service);
|
||||||
|
|
||||||
void std_autocomplete(const CommandContext &ctx);
|
void std_autocomplete(const CommandContext &ctx);
|
||||||
|
@@ -323,7 +323,8 @@ namespace dropshell
|
|||||||
for (const auto &[key, value] : extra_env_vars)
|
for (const auto &[key, value] : extra_env_vars)
|
||||||
scommand->add_env_var(key, value);
|
scommand->add_env_var(key, value);
|
||||||
|
|
||||||
return execute_ssh_command(get_SSH_INFO(user), scommand.value(), cMode::Defaults, &output);
|
// Use Silent mode when capturing output to avoid printing to console
|
||||||
|
return execute_ssh_command(get_SSH_INFO(user), scommand.value(), cMode::Silent, &output);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ServerConfig::get_variable(const std::string &name) const
|
std::string ServerConfig::get_variable(const std::string &name) const
|
||||||
|
@@ -140,7 +140,7 @@ namespace dropshell
|
|||||||
{ // determine if the service template hash matches the template hash.
|
{ // determine if the service template hash matches the template hash.
|
||||||
auto it = variables.find("TEMPLATE_HASH");
|
auto it = variables.find("TEMPLATE_HASH");
|
||||||
if (it == variables.end())
|
if (it == variables.end())
|
||||||
error << "Variable TEMPLATE_HASH not found in the service " << filenames::template_info_env << std::endl;
|
error << "Variable TEMPLATE_HASH not found in the service " << filenames::template_info_env << " for "<<server_name << " - "<< service.template_name << std::endl;
|
||||||
else if (tinfo.is_set())
|
else if (tinfo.is_set())
|
||||||
{
|
{
|
||||||
uint64_t service_template_hash = std::stoull(it->second);
|
uint64_t service_template_hash = std::stoull(it->second);
|
||||||
|
Reference in New Issue
Block a user