#include "services.hpp" #include "utils/envmanager.hpp" #include "utils/directories.hpp" #include "templates.hpp" #include "config.hpp" #include "utils/utils.hpp" #include #include #include 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() && !service_info.user.empty(); } std::vector get_server_services_info(const std::string &server_name, bool skip_update) { std::vector services; if (server_name.empty()) return services; std::vector 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, skip_update); 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 ordered_env_vars &variables, const std::string &variable_name) { auto it = find_var(variables, variable_name); if (it == variables.end()) { error << "Variable " << variable_name << " not found." << std::endl; return false; } return it->second == "true"; } LocalServiceInfo get_service_info(const std::string &server_name, const std::string &service_name, bool skip_update) { if (server_name.empty() || service_name.empty()) return LocalServiceInfo(); if (!legal_service_name(service_name)) return LocalServiceInfo(); LocalServiceInfo service; template_info tinfo; 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. { // service.env variables. ordered_env_vars variables; envmanager env_manager(localfile::service_env(server_name, service_name)); bool ok = env_manager.load(); env_manager.get_all_variables(variables); ASSERT(ok, "Failed to load env variables from "+localfile::service_env(server_name, service_name)); auto it = find_var(variables, "TEMPLATE"); if (it == variables.end()) { error << "TEMPLATE variable not defined in service " << service_name << " on server " << server_name << std::endl; info << "Path is " << localfile::service_env(server_name, service_name) << std::endl; return LocalServiceInfo(); } service.template_name = it->second; tinfo = gTemplateManager().get_template_info(service.template_name,skip_update); if (!tinfo.is_set()) { // Template not found - this means it's not available locally OR from registry error << "Template '" << service.template_name << "' not found locally or in registry" << std::endl; return LocalServiceInfo(); } // Template is available (either locally or downloaded from registry) service.local_template_path = tinfo.local_template_path(); { // set the user. auto it = find_var(variables, "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 from the template_info.env { // template_info.env variables. ordered_env_vars template_variables; envmanager template_env_manager(tinfo.local_template_info_env_path()); template_env_manager.load(); template_env_manager.get_all_variables(template_variables); service.requires_host_root = get_bool_variable(template_variables, "REQUIRES_HOST_ROOT"); service.requires_docker = get_bool_variable(template_variables, "REQUIRES_DOCKER"); service.requires_docker_root = get_bool_variable(template_variables, "REQUIRES_DOCKER_ROOT"); } return service; } std::set get_used_commands(const std::string &server_name, const std::string &service_name) { std::set 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 list_backups(const std::string &server_name, const std::string &service_name) { std::set 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, ordered_env_vars &all_env_vars) // { // clear_vars(all_env_vars); // 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)) // { // ordered_env_vars env_vars; // envmanager env_manager(file); // env_manager.load(); // env_manager.get_all_variables(env_vars); // merge_vars(all_env_vars, env_vars); // } // else // warning << "Expected environment file not found: " << file << std::endl; // }; // // add in some simple variables first, as others below may depend on/use these in bash. // set_var(all_env_vars, "SERVER", server_name); // set_var(all_env_vars, "SERVICE", service_name); // set_var(all_env_vars, "DOCKER_CLI_HINTS", "false"); // turn off docker junk. // // Load environment files // load_env_file(localfile::service_env(server_name, service_name)); // std::string template_name = get_var(all_env_vars, "TEMPLATE"); // if (template_name.empty()) // { // error << "TEMPLATE variable not defined in service " << service_name << " on server " << server_name << std::endl; // return false; // } // auto tinfo = gTemplateManager().get_template_info(template_name, true); // skip updates. // if (!tinfo.is_set()) // { // // Template is not available locally or from registry // error << "Template '" << template_name << "' not found locally or in registry" << std::endl; // return false; // } // ASSERT(std::filesystem::exists(tinfo.local_template_info_env_path())); // load_env_file(tinfo.local_template_info_env_path()); // std::string user = get_var(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 "<