360 lines
13 KiB
C++
360 lines
13 KiB
C++
#include <filesystem>
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <vector>
|
|
#include <string>
|
|
#include <algorithm>
|
|
#include <iomanip>
|
|
#include <map>
|
|
|
|
#include "utils/envmanager.hpp"
|
|
#include "utils/directories.hpp"
|
|
#include "utils/utils.hpp"
|
|
#include "templates.hpp"
|
|
#include "config.hpp"
|
|
#include "utils/assert.hpp"
|
|
|
|
namespace dropshell {
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// template_source_local
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
std::set<std::string> template_source_local::get_template_list() {
|
|
std::set<std::string> templates;
|
|
|
|
// Helper function to add templates from a directory
|
|
auto add_templates_from_dir = [&templates](const std::string& dir_path) {
|
|
if (!std::filesystem::exists(dir_path))
|
|
return;
|
|
|
|
for (const auto& entry : std::filesystem::directory_iterator(dir_path))
|
|
if (entry.is_directory())
|
|
templates.insert(entry.path().filename().string());
|
|
};
|
|
|
|
add_templates_from_dir(mLocalPath);
|
|
return templates;
|
|
}
|
|
|
|
bool template_source_local::has_template(const std::string& template_name) {
|
|
std::filesystem::path path = mLocalPath / template_name;
|
|
return (std::filesystem::exists(path));
|
|
}
|
|
|
|
bool template_source_local::template_command_exists(const std::string& template_name, const std::string& command) {
|
|
std::filesystem::path path = mLocalPath / template_name / (command+".sh");
|
|
return std::filesystem::exists(path);
|
|
}
|
|
|
|
template_info template_source_local::get_template_info(const std::string& template_name) {
|
|
std::filesystem::path path = mLocalPath / template_name;
|
|
|
|
if (!std::filesystem::exists(path))
|
|
return template_info();
|
|
|
|
return template_info(
|
|
template_name,
|
|
mLocalPath.string(),
|
|
path
|
|
);
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// template_source_registry
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
std::set<std::string> template_source_registry::get_template_list()
|
|
{
|
|
#pragma message("TODO: Implement template_source_registry::get_template_list")
|
|
return std::set<std::string>();
|
|
}
|
|
|
|
bool template_source_registry::has_template(const std::string& template_name)
|
|
{
|
|
#pragma message("TODO: Implement template_source_registry::has_template")
|
|
return false;
|
|
}
|
|
|
|
template_info template_source_registry::get_template_info(const std::string& template_name)
|
|
{
|
|
#pragma message("TODO: Implement template_source_registry::get_template_info")
|
|
return template_info();
|
|
}
|
|
|
|
bool template_source_registry::template_command_exists(const std::string& template_name, const std::string& command)
|
|
{
|
|
#pragma message("TODO: Implement template_source_registry::template_command_exists")
|
|
return false;
|
|
}
|
|
|
|
std::filesystem::path template_source_registry::get_cache_dir()
|
|
{
|
|
#pragma message("TODO: Implement template_source_registry::get_cache_dir")
|
|
return std::filesystem::path();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// template_manager
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
void template_manager::list_templates() const {
|
|
ASSERT(mLoaded && mSources.size() > 0);
|
|
auto templates = get_template_list();
|
|
|
|
if (templates.empty()) {
|
|
std::cout << "No templates found." << std::endl;
|
|
return;
|
|
}
|
|
|
|
std::cout << "Available templates:" << std::endl;
|
|
|
|
// print templates.
|
|
std::cout << std::string(60, '-') << std::endl;
|
|
bool first = true;
|
|
for (const auto& t : templates) {
|
|
std::cout << (first ? "" : ", ") << t;
|
|
first = false;
|
|
}
|
|
std::cout << std::endl;
|
|
std::cout << std::string(60, '-') << std::endl;
|
|
}
|
|
|
|
std::set<std::string> template_manager::get_template_list() const
|
|
{
|
|
ASSERT(mLoaded && mSources.size() > 0);
|
|
std::set<std::string> templates;
|
|
for (const auto& source : mSources) {
|
|
auto source_templates = source->get_template_list();
|
|
templates.insert(source_templates.begin(), source_templates.end());
|
|
}
|
|
return templates;
|
|
}
|
|
|
|
bool template_manager::has_template(const std::string &template_name) const
|
|
{
|
|
ASSERT(mLoaded && mSources.size() > 0);
|
|
template_source_interface* source = get_source(template_name);
|
|
if (!source)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
template_info template_manager::get_template_info(const std::string &template_name) const
|
|
{
|
|
ASSERT(mLoaded && mSources.size() > 0);
|
|
template_source_interface* source = get_source(template_name);
|
|
if (source)
|
|
return source->get_template_info(template_name);
|
|
|
|
// fail
|
|
return template_info();
|
|
}
|
|
|
|
bool template_manager::template_command_exists(const std::string &template_name, const std::string &command) const
|
|
{
|
|
ASSERT(mLoaded && mSources.size() > 0);
|
|
template_source_interface* source = get_source(template_name);
|
|
if (!source) {
|
|
std::cerr << "Error: Template '" << template_name << "' not found" << std::endl;
|
|
return false;
|
|
}
|
|
return source->template_command_exists(template_name, command);
|
|
}
|
|
|
|
bool template_manager::create_template(const std::string &template_name) const
|
|
{
|
|
// 1. Create a new directory in the user templates directory
|
|
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 false;
|
|
}
|
|
|
|
auto info = get_template_info(template_name);
|
|
if (info.is_set()) {
|
|
std::cerr << "Error: Template '" << template_name << "' already exists at " << info.locationID() << std::endl;
|
|
return false;
|
|
}
|
|
|
|
auto local_template_paths = gConfig().get_template_local_paths();
|
|
if (local_template_paths.empty()) {
|
|
std::cerr << "Error: No local template paths found" << std::endl;
|
|
std::cerr << "Run 'dropshell edit' to add one to the DropShell config" << std::endl;
|
|
return false;
|
|
}
|
|
std::string new_template_path = local_template_paths[0] + "/" + template_name;
|
|
|
|
// Create the new template directory
|
|
std::filesystem::create_directories(new_template_path);
|
|
|
|
// 2. Copy the example template from the system templates directory
|
|
auto example_info = gTemplateManager().get_template_info("example-nginx");
|
|
if (!example_info.is_set()) {
|
|
std::cerr << "Error: Example template not found" << std::endl;
|
|
return false;
|
|
}
|
|
std::string example_template_path = example_info.local_template_path();
|
|
|
|
// Copy all files from example template to new template
|
|
for (const auto& entry : std::filesystem::recursive_directory_iterator(example_template_path)) {
|
|
std::string relative_path = entry.path().string().substr(example_template_path.length());
|
|
std::string target_path = new_template_path + relative_path;
|
|
|
|
if (entry.is_directory()) {
|
|
std::filesystem::create_directory(target_path);
|
|
} else {
|
|
std::filesystem::copy_file(entry.path(), target_path);
|
|
}
|
|
}
|
|
|
|
// modify the TEMPLATE=example line in the .template_info.env file to TEMPLATE=<template_name>
|
|
std::string search_string = "TEMPLATE=";
|
|
std::string replacement_line = "TEMPLATE=" + template_name;
|
|
std::string service_env_path = new_template_path + "/config/.template_info.env";
|
|
if (!replace_line_in_file(service_env_path, search_string, replacement_line)) {
|
|
std::cerr << "Error: Failed to replace TEMPLATE= line in the .template_info.env file" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
// 3. Print out the README.txt file and the path
|
|
std::string readme_path = new_template_path + "/README.txt";
|
|
if (std::filesystem::exists(readme_path)) {
|
|
std::cout << "\nREADME contents:" << std::endl;
|
|
std::cout << std::string(60, '-') << std::endl;
|
|
|
|
std::ifstream readme_file(readme_path);
|
|
if (readme_file.is_open()) {
|
|
std::string line;
|
|
while (std::getline(readme_file, line)) {
|
|
std::cout << line << std::endl;
|
|
}
|
|
readme_file.close();
|
|
}
|
|
std::cout << std::string(60, '-') << std::endl;
|
|
}
|
|
|
|
std::cout << std::endl;
|
|
std::cout << "Template '" << template_name << "' created at " << new_template_path << std::endl;
|
|
return test_template(new_template_path);
|
|
}
|
|
|
|
void template_manager::load_sources()
|
|
{
|
|
ASSERT(mSources.empty());
|
|
ASSERT(gConfig().is_config_set());
|
|
ASSERT(!mLoaded);
|
|
auto local_template_paths = gConfig().get_template_local_paths();
|
|
if (local_template_paths.empty())
|
|
return;
|
|
for (const auto& path : local_template_paths)
|
|
mSources.push_back(std::make_unique<template_source_local>(path));
|
|
|
|
auto registry_urls = gConfig().get_template_registry_urls();
|
|
for (const auto& url : registry_urls)
|
|
mSources.push_back(std::make_unique<template_source_registry>(url));
|
|
|
|
mLoaded = true;
|
|
}
|
|
|
|
void template_manager::print_sources() const
|
|
{
|
|
std::cout << "Template sources: ";
|
|
for (const auto& source : mSources) {
|
|
std::cout << "[" << source->get_description() << "] ";
|
|
}
|
|
std::cout << std::endl;
|
|
}
|
|
|
|
bool template_manager::required_file(std::string path, std::string template_name)
|
|
{
|
|
if (!std::filesystem::exists(path)) {
|
|
std::cerr << "Error: " << path << " file not found in template - REQUIRED." << template_name << std::endl;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template_source_interface *template_manager::get_source(const std::string &template_name) const
|
|
{
|
|
ASSERT(mLoaded && mSources.size() > 0);
|
|
for (const auto& source : mSources) {
|
|
if (source->has_template(template_name)) {
|
|
return source.get();
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool template_manager::test_template(const std::string &template_path)
|
|
{
|
|
std::string template_name = std::filesystem::path(template_path).filename().string();
|
|
|
|
std::vector<std::string> required_files = {
|
|
"config/service.env",
|
|
"config/.template_info.env",
|
|
"_default.env",
|
|
"install.sh",
|
|
"uninstall.sh",
|
|
"nuke.sh"
|
|
};
|
|
|
|
for (const auto& file : required_files) {
|
|
if (!required_file(template_path + "/" + file, template_name))
|
|
return false;
|
|
}
|
|
|
|
// ------------------------------------------------------------
|
|
// check TEMPLATE= line.
|
|
std::map<std::string, std::string> all_env_vars;
|
|
std::vector<std::string> env_files = {
|
|
"config/service.env",
|
|
"config/.template_info.env"
|
|
};
|
|
for (const auto& file : env_files) {
|
|
{ // load service.env from the service on this machine.
|
|
std::map<std::string, std::string> env_vars;
|
|
envmanager env_manager(template_path + "/" + file);
|
|
env_manager.load();
|
|
env_manager.get_all_variables(env_vars);
|
|
all_env_vars.merge(env_vars);
|
|
}
|
|
}
|
|
|
|
// determine template name.
|
|
auto it = all_env_vars.find("TEMPLATE");
|
|
if (it == all_env_vars.end()) {
|
|
std::cerr << "Error: TEMPLATE variable not found in " << template_path << std::endl;
|
|
return false;
|
|
}
|
|
|
|
std::string env_template_name = it->second;
|
|
if (env_template_name.empty()) {
|
|
std::cerr << "Error: TEMPLATE variable is empty in " << template_path << std::endl;
|
|
return false;
|
|
}
|
|
|
|
if (env_template_name != template_name) {
|
|
std::cerr << "Error: TEMPLATE variable is wrong in " << template_path << std::endl;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
template_manager & gTemplateManager()
|
|
{
|
|
static template_manager instance;
|
|
return instance;
|
|
}
|
|
|
|
} // namespace dropshell
|