dropshell/src/services.cpp
Your Name a9b1758503
Some checks failed
Dropshell Test / Build_and_Test (push) Has been cancelled
./
2025-05-13 20:55:06 +12:00

283 lines
11 KiB
C++

#include "services.hpp"
#include "utils/envmanager.hpp"
#include "utils/directories.hpp"
#include "templates.hpp"
#include "config.hpp"
#include "utils/utils.hpp"
#include "server_env_manager.hpp"
#include "servers.hpp"
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
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> services;
if (server_name.empty())
return services;
std::vector<std::string> local_server_definition_paths = gConfig().get_local_server_definition_paths();
if (local_server_definition_paths.empty()) {
std::cerr << "Error: No local server definition paths found" << std::endl;
std::cerr << "Run 'dropshell edit' to configure DropShell" << std::endl;
return services;
}
for (const auto& server_definition_path : local_server_definition_paths) {
fs::path serverpath = server_definition_path + "/" + server_name;
if (fs::exists(serverpath)) // service is on that server...
for (const auto& entry : fs::directory_iterator(serverpath)) {
if (fs::is_directory(entry)) {
std::string dirname = entry.path().filename().string();
if (dirname.empty() || dirname[0] == '.' || dirname[0] == '_')
continue;
auto service = get_service_info(server_name, dirname);
if (!service.local_service_path.empty())
services.push_back(service);
else
std::cerr << "Warning: Failed to get service info for " << dirname << " on server " << server_name << std::endl;
}
} // end of for
}
return services;
}
LocalServiceInfo get_service_info(const std::string &server_name, const std::string &service_name)
{
LocalServiceInfo service;
if (server_name.empty() || service_name.empty())
return LocalServiceInfo();
service.service_name = service_name;
service.local_service_path = localpath::service(server_name, service_name);
if (service.local_service_path.empty())
return LocalServiceInfo();
// now set the template name and path.
std::map<std::string, std::string> variables;
if (!get_all_service_env_vars(server_name, service_name, variables))
return LocalServiceInfo();
// confirm TEMPLATE is defined.
auto it = variables.find("TEMPLATE");
if (it == variables.end()) {
std::cerr << "Error: TEMPLATE variable not defined in service " << service_name << " on server " << server_name << std::endl;
return LocalServiceInfo();
}
service.template_name = it->second;
template_info tinfo = gTemplateManager().get_template_info(service.template_name);
if (!tinfo.is_set()) {
std::cerr << "Error: Template '" << service.template_name << "' not found" << std::endl;
return LocalServiceInfo();
}
// find the template path
service.local_template_path = tinfo.local_template_path();
return service;
}
std::set<std::string> get_used_commands(const std::string &server_name, const std::string &service_name)
{
std::set<std::string> commands;
if (server_name.empty() || service_name.empty())
return commands;
auto service_info = get_service_info(server_name, service_name);
if (service_info.local_template_path.empty()) {
std::cerr << "Error: Service not found: " << service_name << std::endl;
return commands;
}
// iterate over all files in the template path, and add the command name to the set.
// commands are .sh files that don't begin with _
for (const auto& entry : fs::directory_iterator(service_info.local_template_path)) {
if (fs::is_regular_file(entry) && entry.path().extension() == ".sh" && (entry.path().filename().string().rfind("_", 0) != 0))
commands.insert(entry.path().stem().string());
}
return commands;
}
std::set<std::string> list_backups(const std::string &server_name, const std::string &service_name)
{
std::set<std::string> backups;
if (server_name.empty() || service_name.empty())
return backups;
// need to find the template for the service.
auto service_info = get_service_info(server_name, service_name);
if (service_info.local_template_path.empty()) {
std::cerr << "Error: Service not found: " << service_name << std::endl;
return backups;
}
std::string backups_dir = gConfig().get_local_backup_path();
if (backups_dir.empty())
return backups;
if (fs::exists(backups_dir)) {
for (const auto& entry : fs::directory_iterator(backups_dir)) {
if (fs::is_regular_file(entry) && entry.path().extension() == ".tgz")
if (entry.path().filename().string().find(service_info.template_name) != std::string::npos)
{
backups.insert(entry.path().filename().string());
}
}
}
return backups;
}
bool create_service(const std::string &server_name, const std::string &template_name, const std::string &service_name, bool silent)
{
if (server_name.empty() || template_name.empty() || service_name.empty())
return false;
std::string service_dir = localpath::service(server_name, service_name);
if (service_dir.empty())
{
if (!silent)
{
std::cerr << "Error: Couldn't locate server " << server_name << " in any config directory" << std::endl;
std::cerr << "Please check the server name is correct and try again" << std::endl;
std::cerr << "You can list all servers with 'dropshell servers'" << std::endl;
std::cerr << "You can create a new server with 'dropshell create-server " << server_name << "'" << std::endl;
}
return false;
}
if (fs::exists(service_dir))
{
if (!silent)
{
std::cerr << "Error: Service already exists: " << service_name << std::endl;
std::cerr << "Current service path: " << service_dir << std::endl;
}
return false;
}
template_info tinfo = gTemplateManager().get_template_info(template_name);
if (!tinfo.is_set())
{
if (!silent)
{
std::cerr << "Error: Template '" << template_name << "' not found" << std::endl;
std::cerr << "Please check the template name is correct and try again" << std::endl;
std::cerr << "You can list all templates with 'dropshell templates'" << std::endl;
std::cerr << "You can create a new template with 'dropshell create-template " << template_name << "'" << std::endl;
}
return false;
}
// check template is all good.
if (!gTemplateManager().test_template(tinfo.local_template_path()))
{
if (!silent)
std::cerr << "Error: Template '" << template_name << "' is not valid" << std::endl;
return false;
}
// create the service directory
fs::create_directory(service_dir);
// copy the template config files to the service directory
recursive_copy(tinfo.local_template_path()/"config", service_dir);
if (!silent)
{
std::cout << "Service " << service_name <<" created successfully"<<std::endl;
std::cout << std::endl;
std::cout << "To complete the installation, please:" << std::endl;
std::cout << "1. edit the service config file: dropshell edit " << server_name << " " << service_name << std::endl;
std::cout << "2. install the remote service: dropshell install " << server_name << " " << service_name << std::endl;
}
return true;
}
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();
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.
all_env_vars["CONFIG_PATH"] = remotepath::service_config(server_name,service_name);
all_env_vars["SERVER"] = server_name;
all_env_vars["SERVICE"] = service_name;
all_env_vars["AGENT_PATH"] = remotepath::agent(server_name);
ServerInfo server_info = get_server_info(server_name);
if (server_info.ssh_host.empty())
std::cerr << "Error: Server " << server_name << " not found - ssh_host empty, so HOST_NAME not set" << std::endl;
all_env_vars["HOST_NAME"] = server_info.ssh_host;
// Lambda function to load environment variables from a file
auto load_env_file = [&all_env_vars](const std::string& file) {
if (!file.empty() && std::filesystem::exists(file)) {
std::map<std::string, std::string> env_vars;
envmanager env_manager(file);
env_manager.load();
env_manager.get_all_variables(env_vars);
all_env_vars.merge(env_vars);
}
else
std::cout << "Warning: Expected environment file not found: " << file << std::endl;
};
// Load environment files
load_env_file(localfile::service_env(server_name, service_name));
load_env_file(localfile::template_info_env(server_name, service_name));
// determine template name.
auto it = all_env_vars.find("TEMPLATE");
if (it == all_env_vars.end()) {
std::cerr << std::endl << std::endl;
std::cerr << "Error: TEMPLATE variable not defined in service " << service_name << " on server " << server_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 << " " << localpath::service(server_name, service_name) << std::endl << std::endl;
return false;
}
template_info tinfo = gTemplateManager().get_template_info(it->second);
if (!tinfo.is_set()) {
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