This commit is contained in:
Your Name 2025-04-25 12:53:03 +12:00
parent 7e499195e5
commit 2f3135053f
16 changed files with 162 additions and 119 deletions

View File

@ -128,7 +128,7 @@ int main(int argc, char* argv[]) {
commands.insert("init");
if (cfg->is_config_set())
commands.merge(std::set<std::string>{
"servers","templates","install","uninstall","backup"
"servers","templates"
});
for (const auto& command : commands) {
@ -189,51 +189,6 @@ int main(int argc, char* argv[]) {
return 0;
}
if (cmd == "install" || cmd == "uninstall") {
std::string server_name;
std::vector<dropshell::ServiceInfo> servicelist;
if (!parseargs(safearg(argc, argv, 2), safearg(argc, argv, 3), server_name, servicelist)) {
std::cerr << "Error: " << cmd << " command requires server name and optionally service name" << std::endl;
return 1;
}
for (const auto& service_info : servicelist) {
dropshell::service_runner service;
if (!service.init(server_name, service_info.service_name)) {
std::cerr << "Error: Failed to initialize service" << std::endl;
return 1;
}
bool success = ((cmd=="install") ? service.install() : service.uninstall());
if (!success) {
std::cerr << "Error: Failed to " << cmd << " service" << std::endl;
return 1;
}
}
return 0;
}
if (cmd == "backup") {
std::string server_name;
std::vector<dropshell::ServiceInfo> servicelist;
if (!parseargs(safearg(argc, argv, 2), safearg(argc, argv, 3), server_name, servicelist)) {
std::cerr << "Error: backup command requires server name and optionally service name" << std::endl;
return 1;
}
for (const auto& service_info : servicelist) {
dropshell::service_runner service;
if (!service.init(server_name, service_info.service_name)) {
std::cerr << "Error: Failed to initialize service" << std::endl;
return 1;
}
if (!service.backup()) {
std::cerr << "Backup failed." << std::endl;
return 1;
}
}
return 0;
}
// handle running a command.
for (const auto& command : commands) {
if (cmd == command) {

View File

@ -112,29 +112,23 @@ void service_runner::maketitle(const std::string& title) const {
bool service_runner::install() {
maketitle("Installing " + 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;
}
if (!m_server_env) return false; // should never hit this.
// 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;
if (!get_template_info(m_service_info.template_name, tinfo))
return false;
}
// Create service directory
std::string mkdir_cmd = "'mkdir -p " + mRemote_service_path + "'";
if (!execute_ssh_command(mkdir_cmd, "Failed to create service directory")) {
if (!execute_ssh_command(mkdir_cmd, "Failed to create service directory"))
return false;
}
// Check if rsync is installed on remote host
std::string check_rsync_cmd = "'which rsync > /dev/null 2>&1'";
if (!execute_ssh_command(check_rsync_cmd, "rsync is not installed on the remote host")) {
if (!execute_ssh_command(check_rsync_cmd, "rsync is not installed on the remote host"))
return false;
}
// Copy template files
{
@ -178,17 +172,7 @@ bool service_runner::install() {
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;
}
if (!m_server_env) return false; // should never hit this.
// 2. Check if service directory exists on server
if (!check_remote_dir_exists(mRemote_service_path)) {
@ -226,12 +210,20 @@ bool service_runner::run_command(const std::string& command) {
std::cerr << "Error: Server service not initialized" << std::endl;
return false;
}
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;
}
if (!template_command_exists(m_service_info.template_name, command)) {
std::cout << "No command script for " << m_service_info.template_name << " : " << command << std::endl;
return true; // nothing to run.
}
// install doesn't require anything on the server yet.
if (command == "install")
return install();
std::string script_path = mRemote_service_template_path + "/" + command + ".sh";
// Check if service directory exists
@ -249,7 +241,12 @@ bool service_runner::run_command(const std::string& command) {
return false;
}
// Run the command
if (command == "uninstall")
return uninstall();
if (command == "backup")
return backup();
// Run the generic command
std::string run_cmd = "'cd " + mRemote_service_template_path +
" && /bin/bash " + script_path + " " + mRemote_service_env_file + "'";
return execute_ssh_command(run_cmd, "Command returned error code: " + script_path);
@ -258,10 +255,7 @@ bool service_runner::run_command(const std::string& command) {
bool service_runner::backup() {
maketitle("Backing up " + 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;
}
if (!m_server_env) return false; // should never hit this.
std::string command = "_backup";

View File

@ -16,23 +16,6 @@ class service_runner {
service_runner();
bool init(const std::string& server_name, const std::string& service_name);
// 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
// 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 {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
@ -44,14 +27,6 @@ class service_runner {
// checking that the {service_name}.env file exists in the service directory.
bool run_command(const std::string& command);
// backup the service over ssh, using the credentials from server.env (via server_env.hpp)
// 1. run backup.sh on the server
// 2. create a backup file with format server-service-datetime.tgz
// 3. store it in the server's DROPSHELL_DIR/backups folder
// 4. copy it to the local user_dir/backups folder
bool backup();
// check health of service. Silent.
// 1. run status.sh on the server
// 2. return the output of the status.sh script
@ -71,6 +46,33 @@ class service_runner {
std::string healthtick();
std::string healthmark();
private:
// 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
// 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 {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. remove the service directory from the server
bool uninstall();
// backup the service over ssh, using the credentials from server.env (via server_env.hpp)
// 1. run backup.sh on the server
// 2. create a backup file with format server-service-datetime.tgz
// 3. store it in the server's DROPSHELL_DIR/backups folder
// 4. copy it to the local user_dir/backups folder
bool backup();
private:
std::string m_server_name;
ServiceInfo m_service_info;

View File

View File

@ -0,0 +1,9 @@
# Service settings specific to this server
TEMPLATE=example
# Application settings
CONTAINER_PORT=8181
# Image settings
IMAGE_REGISTRY="gitea.jde.nz"
IMAGE_REPO="example/example"

View File

@ -1,5 +1,12 @@
#!/bin/bash
# COMMON FUNCTIONS
# JDE
# 2025-04-25
# This file is not required if you write your own template.
# Print error message and exit with code 1
# Usage: die "error message"
die() {
@ -14,6 +21,17 @@ load_env() {
local script_dir="$(dirname "${BASH_SOURCE[0]}")"
local env_file
# first load basic.env for the template defaults
if [ -f "$script_dir/_basic.env" ]; then
set -a
source "$script_dir/_basic.env"
set +a
else
echo "Warning: basic.env file not found at $script_dir/_basic.env. Broken template?"
return 1
fi
# now load the server specific env file
if [ -z "$1" ]; then
echo "Usage: $0 [path_to_env_file]"
return 1

View File

@ -1,4 +1,13 @@
#!/bin/bash
# BACKUP SCRIPT
# The backup script is OPTIONAL.
# It is used to backup the service on the server.
# It is called with:
# 1) the path to the server specific env file as the frist argument.
# 2) the path to the destination backup file as the second argument.
# If the backup file already exists, the script should exit with a message.
source "$(dirname "$0")/_common.sh"
load_env "$1" || die "Failed to load environment variables"

View File

@ -1,15 +1,7 @@
# Service settings
TEMPLATE=example
# Service settings specific to this server
# (can also override anything in the basic.env file in the template to make it specific to this server)
# Application settings
CONTAINER_PORT=8181
HOST_PORT=80
# Deployment settings
LOCAL_DATA_FOLDER="${HOME}/.example"
CONTAINER_NAME="example"
# Image settings
IMAGE_REGISTRY="gitea.jde.nz"
IMAGE_REPO="example/example"
IMAGE_TAG="latest"

View File

@ -1,4 +1,10 @@
#!/bin/bash
# INSTALL SCRIPT
# The install script is required for all templates.
# It is used to install the service on the server.
# It is called with the path to the server specific env file as an argument.
source "$(dirname "$0")/_common.sh"
load_env "$1" || die "Failed to load environment variables"

View File

@ -1,4 +1,10 @@
#!/bin/bash
# LOGS SCRIPT
# The logs script is OPTIONAL.
# It is used to return the logs of the service.
# It is called with the path to the server specific env file as an argument.
source "$(dirname "$0")/_common.sh"
load_env "$1" || die "Failed to load environment variables"

View File

@ -1,4 +1,10 @@
#!/bin/bash
# PORT SCRIPT
# The port script is OPTIONAL.
# It is used to return the ports used by the service.
# It is called with the path to the server specific env file as an argument.
source "$(dirname "$0")/_common.sh"
load_env "$1" || die "Failed to load environment variables"

View File

@ -1,10 +1,23 @@
#!/bin/bash
# START SCRIPT
# The start script is required for all templates.
# It is used to start the service on the server.
# It is called with the path to the server specific env file as an argument.
source "$(dirname "$0")/_common.sh"
load_env "$1" || die "Failed to load environment variables"
# Required environment variables
check_required_env_vars "CONTAINER_NAME" "HOST_PORT" "CONTAINER_PORT" "LOCAL_DATA_FOLDER"
if [ -d "${LOCAL_DATA_FOLDER}" ]; then
echo "Local data folder ${LOCAL_DATA_FOLDER} exists, skipping data folder creation"
else
echo "Local data folder ${LOCAL_DATA_FOLDER} does not exist, creating..."
mkdir -p "${LOCAL_DATA_FOLDER}"
fi
DOCKER_RUN_CMD="docker run -d \
--restart unless-stopped \
--name ${CONTAINER_NAME} \

View File

@ -1,4 +1,12 @@
#!/bin/bash
# STATUS SCRIPT
# The status script is OPTIONAL.
# It is used to return the status of the service (0 is healthy, 1 is unhealthy).
# It is called with the path to the server specific env file as an argument.
# This is an example of a status script that checks if the service is running.
source "$(dirname "$0")/_common.sh"
load_env "$1" || die "Failed to load environment variables"

View File

@ -1,4 +1,10 @@
#!/bin/bash
# STOP SCRIPT
# The stop script is required for all templates.
# It is used to stop the service on the server.
# It is called with the path to the server specific env file as an argument.
source "$(dirname "$0")/_common.sh"
load_env "$1" || die "Failed to load environment variables"

View File

@ -1,4 +1,10 @@
#!/bin/bash
# UNINSTALL SCRIPT
# The uninstall script is required for all templates.
# It is used to uninstall the service from the server.
# It is called with the path to the server specific env file as an argument.
source "$(dirname "$0")/_common.sh"
load_env "$1" || die "Failed to load environment variables"

View File

@ -1,5 +1,9 @@
#!/bin/bash
# COMMON FUNCTIONS
# JDE
# 2025-04-25
# Print error message and exit with code 1
# Usage: die "error message"
die() {
@ -14,6 +18,17 @@ load_env() {
local script_dir="$(dirname "${BASH_SOURCE[0]}")"
local env_file
# first load basic.env for the template defaults
if [ -f "$script_dir/basic.env" ]; then
set -a
source "$script_dir/basic.env"
set +a
else
echo "Warning: basic.env file not found at $script_dir/basic.env. Broken template?"
return 1
fi
# now load the server specific env file
if [ -z "$1" ]; then
echo "Usage: $0 [path_to_env_file]"
return 1
@ -49,9 +64,7 @@ create_and_start_container() {
local container_name="$2"
if _is_container_exists $container_name; then
if _is_container_running $container_name; then
return 0
fi
_is_container_running $container_name && return 0
_start_container $container_name
else
grey_start