276 lines
11 KiB
C++
276 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 <libassert/assert.hpp>
|
|
|
|
#include <iostream>
|
|
#include <filesystem>
|
|
|
|
namespace fs = std::filesystem;
|
|
|
|
namespace dropshell
|
|
{
|
|
|
|
#pragma message("TODO : Smart test that the service is fully valid.")
|
|
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() &&
|
|
!service_info.user.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())
|
|
{
|
|
error << "No local server definition paths found" << std::endl;
|
|
info << "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
|
|
warning << "Failed to get service info for " << dirname << " on server " << server_name << std::endl;
|
|
}
|
|
} // end of for
|
|
}
|
|
|
|
return services;
|
|
}
|
|
|
|
bool get_bool_variable(const std::map<std::string, std::string> &variables, const std::string &variable_name)
|
|
{
|
|
auto it = variables.find(variable_name);
|
|
if (it == variables.end())
|
|
{
|
|
error << "Variable " << variable_name << " not found in the service " << filenames::template_info_env << std::endl;
|
|
return false;
|
|
}
|
|
return it->second == "true";
|
|
}
|
|
|
|
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();
|
|
|
|
// check the service directory exists.
|
|
if (!fs::exists(service.local_service_path))
|
|
{
|
|
warning << "Service directory not found: " << service.local_service_path << std::endl;
|
|
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())
|
|
{
|
|
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())
|
|
{
|
|
error << "Template specified '" << service.template_name << "' could not be found" << std::endl;
|
|
return LocalServiceInfo();
|
|
}
|
|
|
|
// find the template path
|
|
service.local_template_path = tinfo.local_template_path();
|
|
|
|
{ // set the user.
|
|
auto it = variables.find("SSH_USER");
|
|
if (it == variables.end())
|
|
{
|
|
error << "SSH_USER variable not defined in service " << service_name << " on server " << server_name << std::endl;
|
|
return LocalServiceInfo();
|
|
}
|
|
service.user = it->second;
|
|
}
|
|
|
|
// set the host root and docker requirements.
|
|
service.requires_host_root = get_bool_variable(variables, "REQUIRES_HOST_ROOT");
|
|
service.requires_docker = get_bool_variable(variables, "REQUIRES_DOCKER");
|
|
service.requires_docker_root = get_bool_variable(variables, "REQUIRES_DOCKER_ROOT");
|
|
|
|
{ // determine if the service template hash matches the template hash.
|
|
auto it = variables.find("TEMPLATE_HASH");
|
|
if (it == variables.end())
|
|
error << "Variable TEMPLATE_HASH not found in the service " << filenames::template_info_env << std::endl;
|
|
else
|
|
{
|
|
uint64_t service_template_hash = std::stoull(it->second);
|
|
service.service_template_hash_match = (service_template_hash == tinfo.hash());
|
|
//debug << "Service template hash: " << service_template_hash << " == " << tinfo.hash() << std::endl;
|
|
}
|
|
}
|
|
|
|
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())
|
|
{
|
|
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())
|
|
{
|
|
error << "Service not found: " << service_name << std::endl;
|
|
return backups;
|
|
}
|
|
|
|
std::string backups_dir = localpath::backups();
|
|
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 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)))
|
|
{
|
|
error << "Service not found: " << service_name << " on server " << server_name << std::endl;
|
|
return false;
|
|
}
|
|
|
|
// 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
|
|
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));
|
|
|
|
std::string user = all_env_vars["SSH_USER"];
|
|
if (user.empty())
|
|
{
|
|
error << "SSH_USER variable not defined in service " << service_name << " on server " << server_name << std::endl;
|
|
info << "This variable definition is always required, and usually set in the "<<filenames::service_env << " file." << std::endl;
|
|
info << "Please check " << localfile::service_env(server_name, service_name) << std::endl;
|
|
return false;
|
|
}
|
|
|
|
// add in some handy variables.
|
|
// if we change these, we also need to update agent/_allservicesstatus.sh
|
|
all_env_vars["CONFIG_PATH"] = remotepath(server_name, user).service_config(service_name);
|
|
all_env_vars["SERVER"] = server_name;
|
|
all_env_vars["SERVICE"] = service_name;
|
|
all_env_vars["AGENT_PATH"] = remotepath(server_name, user).agent();
|
|
all_env_vars["DOCKER_CLI_HINTS"] = "false"; // turn off docker junk.
|
|
|
|
// determine template name.
|
|
auto it = all_env_vars.find("TEMPLATE");
|
|
if (it == all_env_vars.end())
|
|
{
|
|
error << "TEMPLATE variable not defined in service " << service_name << " on server " << server_name << std::endl;
|
|
info << "The TEMPLATE variable is required to determine the template name." << std::endl;
|
|
info << "Please check the " << filenames::service_env << " file and the "<< filenames::template_info_env << " file in:" << std::endl;
|
|
info << " " << 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())
|
|
{
|
|
error << "Template '" << it->second << "' not found" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
} // namespace dropshell
|