template tidying
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
#include "directories.hpp"
|
#include "directories.hpp"
|
||||||
#include "shared_commands.hpp"
|
#include "shared_commands.hpp"
|
||||||
#include "templates.hpp"
|
#include "templates.hpp"
|
||||||
|
#include "envmanager.hpp"
|
||||||
|
|
||||||
#include <libassert/assert.hpp>
|
#include <libassert/assert.hpp>
|
||||||
#include "utils/utils.hpp"
|
#include "utils/utils.hpp"
|
||||||
@@ -85,7 +86,9 @@ namespace dropshell
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
ordered_env_vars all_env_vars;
|
ordered_env_vars all_env_vars;
|
||||||
get_all_service_env_vars(server, service, all_env_vars);
|
envmanager env_manager(localfile::service_env(server,service));
|
||||||
|
env_manager.load();
|
||||||
|
env_manager.get_all_variables(all_env_vars);
|
||||||
set_var(all_env_vars, "LOCAL_CONFIG_PATH", localpath::service(server, service));
|
set_var(all_env_vars, "LOCAL_CONFIG_PATH", localpath::service(server, service));
|
||||||
set_var(all_env_vars, "LOCAL_TEMPLATE_PATH", tinfo.local_template_path().string());
|
set_var(all_env_vars, "LOCAL_TEMPLATE_PATH", tinfo.local_template_path().string());
|
||||||
|
|
||||||
|
|||||||
@@ -1,137 +0,0 @@
|
|||||||
#include "command_registry.hpp"
|
|
||||||
#include "config.hpp"
|
|
||||||
#include "utils/utils.hpp"
|
|
||||||
#include "utils/directories.hpp"
|
|
||||||
#include "utils/execute.hpp"
|
|
||||||
#include "shared_commands.hpp"
|
|
||||||
#include "servers.hpp"
|
|
||||||
#include "services.hpp"
|
|
||||||
#include "templates.hpp"
|
|
||||||
#include "utils/output.hpp"
|
|
||||||
#include <libassert/assert.hpp>
|
|
||||||
|
|
||||||
namespace dropshell
|
|
||||||
{
|
|
||||||
|
|
||||||
int exec_handler(const CommandContext &ctx);
|
|
||||||
|
|
||||||
static std::vector<std::string> exec_name_list = {"exec"};
|
|
||||||
|
|
||||||
// Static registration
|
|
||||||
struct ExecCommandRegister
|
|
||||||
{
|
|
||||||
ExecCommandRegister()
|
|
||||||
{
|
|
||||||
CommandRegistry::instance().register_command({exec_name_list,
|
|
||||||
exec_handler,
|
|
||||||
shared_commands::std_autocomplete,
|
|
||||||
false, // hidden
|
|
||||||
true, // requires_config
|
|
||||||
true, // requires_install
|
|
||||||
3, // min_args (server, service, command)
|
|
||||||
-1, // max_args (unlimited for command with args)
|
|
||||||
"exec SERVER SERVICE COMMAND [ARGS...]",
|
|
||||||
"Execute a command on a server within a service environment.",
|
|
||||||
R"(
|
|
||||||
|
|
||||||
exec SERVER SERVICE COMMAND [ARGS...] Execute a command in the service's environment.
|
|
||||||
|
|
||||||
This command runs a command on the remote server with the service's environment
|
|
||||||
variables loaded (including those from service.env and the usual variables).
|
|
||||||
The command is executed in the service's template directory context.
|
|
||||||
)"});
|
|
||||||
}
|
|
||||||
} exec_command_register;
|
|
||||||
|
|
||||||
int exec_handler(const CommandContext &ctx)
|
|
||||||
{
|
|
||||||
if (ctx.args.size() < 3)
|
|
||||||
{
|
|
||||||
error << "Server name, service name, and command are required" << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string server = safearg(ctx.args, 0);
|
|
||||||
std::string service = safearg(ctx.args, 1);
|
|
||||||
std::string command = safearg(ctx.args, 2);
|
|
||||||
|
|
||||||
// Collect any additional arguments for the command
|
|
||||||
std::vector<std::string> command_args;
|
|
||||||
for (size_t i = 3; i < ctx.args.size(); ++i)
|
|
||||||
{
|
|
||||||
command_args.push_back(ctx.args[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate server
|
|
||||||
ServerConfig server_env(server);
|
|
||||||
if (!server_env.is_valid())
|
|
||||||
{
|
|
||||||
error << "Server " << server << " is not valid" << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate service name
|
|
||||||
if (!legal_service_name(service))
|
|
||||||
{
|
|
||||||
error << "Service name contains illegal characters: " << service << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get service info to validate it exists
|
|
||||||
LocalServiceInfo sinfo = get_service_info(server, service);
|
|
||||||
if (!SIvalid(sinfo))
|
|
||||||
{
|
|
||||||
error << "Service " << service << " is not valid on server " << server << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the user for this service
|
|
||||||
std::string user = server_env.get_user_for_service(service);
|
|
||||||
|
|
||||||
// Get all service environment variables
|
|
||||||
ordered_env_vars env_vars;
|
|
||||||
if (!get_all_service_env_vars(server, service, env_vars))
|
|
||||||
{
|
|
||||||
error << "Failed to get environment variables for service " << service << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add HOST_NAME like other commands do
|
|
||||||
set_var(env_vars, "HOST_NAME", server_env.get_SSH_HOST());
|
|
||||||
|
|
||||||
// Get the remote service template path for working directory
|
|
||||||
std::string remote_service_template_path = remotepath(server, user).service_template(service);
|
|
||||||
|
|
||||||
// Build the command string with arguments
|
|
||||||
std::string full_command = command;
|
|
||||||
for (const auto &arg : command_args)
|
|
||||||
{
|
|
||||||
full_command += " " + quote(dequote(trim(arg)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the command structure with environment variables
|
|
||||||
// Note: execute_ssh_command will automatically use bb64 to encode and execute this safely
|
|
||||||
sCommand scommand(remote_service_template_path, full_command, env_vars);
|
|
||||||
|
|
||||||
// Execute the command on the remote server
|
|
||||||
info << "Executing command on " << server << "/" << service << ": " << command;
|
|
||||||
if (!command_args.empty())
|
|
||||||
{
|
|
||||||
rawout << " with args:";
|
|
||||||
for (const auto &arg : command_args)
|
|
||||||
rawout << " " << arg;
|
|
||||||
}
|
|
||||||
rawout << std::endl;
|
|
||||||
|
|
||||||
bool success = execute_ssh_command(server_env.get_SSH_INFO(user), scommand, cMode::Interactive);
|
|
||||||
|
|
||||||
if (!success)
|
|
||||||
{
|
|
||||||
error << "Command execution failed" << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace dropshell
|
|
||||||
@@ -143,8 +143,8 @@ namespace dropshell
|
|||||||
|
|
||||||
// Validate service.env matches template service.env
|
// Validate service.env matches template service.env
|
||||||
{
|
{
|
||||||
std::filesystem::path template_service_env = tinfo.local_template_path() / "config" / "service.env";
|
std::filesystem::path template_service_env = tinfo.local_template_service_env_path(); //tinfo.local_template_path() / "config" / "service.env";
|
||||||
std::filesystem::path template_info_env = tinfo.local_template_path() / "config" / ".template_info.env";
|
std::filesystem::path template_info_env = tinfo.local_template_info_env_path(); //tinfo.local_template_path() / "config" / ".template_info.env";
|
||||||
std::string service_env_file = localfile::service_env(server, service);
|
std::string service_env_file = localfile::service_env(server, service);
|
||||||
|
|
||||||
std::vector<std::string> missing_vars;
|
std::vector<std::string> missing_vars;
|
||||||
|
|||||||
@@ -91,17 +91,7 @@ namespace dropshell
|
|||||||
return server_env.run_remote_template_command(service, "ssh", {}, false, {}, NULL); // explicitly supports interactive ssh!
|
return server_env.run_remote_template_command(service, "ssh", {}, false, {}, NULL); // explicitly supports interactive ssh!
|
||||||
}
|
}
|
||||||
|
|
||||||
// ssh in without as ssh.sh script.
|
error << "No ssh script provided for the service "<<service<<"." << std::endl;
|
||||||
ordered_env_vars env_vars;
|
|
||||||
if (!get_all_service_env_vars(server, service, env_vars))
|
|
||||||
{
|
|
||||||
error << "Failed to get all service env vars for " << service << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
set_var(env_vars, "HOST_NAME", server_env.get_SSH_HOST());
|
|
||||||
std::string service_dir = remotepath(server, sinfo.user).service_template(service);
|
|
||||||
|
|
||||||
return ssh_into_server(server, sinfo.user, env_vars, service_dir);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int ssh_handler(const CommandContext &ctx)
|
int ssh_handler(const CommandContext &ctx)
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ namespace dropshell
|
|||||||
auto it = find_var(variables, variable_name);
|
auto it = find_var(variables, variable_name);
|
||||||
if (it == variables.end())
|
if (it == variables.end())
|
||||||
{
|
{
|
||||||
error << "Variable " << variable_name << " not found in the service " << filenames::template_info_env << std::endl;
|
error << "Variable " << variable_name << " not found." << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return it->second == "true";
|
return it->second == "true";
|
||||||
@@ -74,7 +74,6 @@ namespace dropshell
|
|||||||
|
|
||||||
LocalServiceInfo get_service_info(const std::string &server_name, const std::string &service_name, bool skip_update)
|
LocalServiceInfo get_service_info(const std::string &server_name, const std::string &service_name, bool skip_update)
|
||||||
{
|
{
|
||||||
LocalServiceInfo service;
|
|
||||||
|
|
||||||
if (server_name.empty() || service_name.empty())
|
if (server_name.empty() || service_name.empty())
|
||||||
return LocalServiceInfo();
|
return LocalServiceInfo();
|
||||||
@@ -82,6 +81,10 @@ namespace dropshell
|
|||||||
if (!legal_service_name(service_name))
|
if (!legal_service_name(service_name))
|
||||||
return LocalServiceInfo();
|
return LocalServiceInfo();
|
||||||
|
|
||||||
|
|
||||||
|
LocalServiceInfo service;
|
||||||
|
template_info tinfo;
|
||||||
|
|
||||||
service.service_name = service_name;
|
service.service_name = service_name;
|
||||||
|
|
||||||
service.local_service_path = localpath::service(server_name, service_name);
|
service.local_service_path = localpath::service(server_name, service_name);
|
||||||
@@ -96,45 +99,54 @@ namespace dropshell
|
|||||||
}
|
}
|
||||||
|
|
||||||
// now set the template name and path.
|
// now set the template name and path.
|
||||||
ordered_env_vars variables;
|
{ // service.env variables.
|
||||||
if (!get_all_service_env_vars(server_name, service_name, variables))
|
ordered_env_vars variables;
|
||||||
return LocalServiceInfo();
|
envmanager env_manager(localfile::service_env(server_name, service_name));
|
||||||
|
bool ok = env_manager.load();
|
||||||
{ // confirm TEMPLATE is defined.
|
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");
|
auto it = find_var(variables, "TEMPLATE");
|
||||||
if (it == variables.end())
|
if (it == variables.end())
|
||||||
{
|
{
|
||||||
error << "TEMPLATE variable not defined in service " << service_name << " on server " << server_name << std::endl;
|
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();
|
return LocalServiceInfo();
|
||||||
}
|
}
|
||||||
service.template_name = it->second;
|
service.template_name = it->second;
|
||||||
}
|
|
||||||
|
|
||||||
template_info tinfo = gTemplateManager().get_template_info(service.template_name,skip_update);
|
tinfo = gTemplateManager().get_template_info(service.template_name,skip_update);
|
||||||
if (!tinfo.is_set())
|
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;
|
// 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();
|
return LocalServiceInfo();
|
||||||
}
|
}
|
||||||
service.user = it->second;
|
|
||||||
|
// 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.
|
// set the host root and docker requirements from the template_info.env
|
||||||
service.requires_host_root = get_bool_variable(variables, "REQUIRES_HOST_ROOT");
|
{ // template_info.env variables.
|
||||||
service.requires_docker = get_bool_variable(variables, "REQUIRES_DOCKER");
|
ordered_env_vars template_variables;
|
||||||
service.requires_docker_root = get_bool_variable(variables, "REQUIRES_DOCKER_ROOT");
|
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;
|
return service;
|
||||||
}
|
}
|
||||||
@@ -196,70 +208,70 @@ namespace dropshell
|
|||||||
return backups;
|
return backups;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get_all_service_env_vars(const std::string &server_name, const std::string &service_name, ordered_env_vars &all_env_vars)
|
// 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);
|
// clear_vars(all_env_vars);
|
||||||
|
|
||||||
if (localpath::service(server_name, service_name).empty() || !fs::exists(localpath::service(server_name, service_name)))
|
// 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;
|
// error << "Service not found: " << service_name << " on server " << server_name << std::endl;
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Lambda function to load environment variables from a file
|
// // Lambda function to load environment variables from a file
|
||||||
auto load_env_file = [&all_env_vars](const std::string &file)
|
// auto load_env_file = [&all_env_vars](const std::string &file)
|
||||||
{
|
// {
|
||||||
if (!file.empty() && std::filesystem::exists(file))
|
// if (!file.empty() && std::filesystem::exists(file))
|
||||||
{
|
// {
|
||||||
ordered_env_vars env_vars;
|
// ordered_env_vars env_vars;
|
||||||
envmanager env_manager(file);
|
// envmanager env_manager(file);
|
||||||
env_manager.load();
|
// env_manager.load();
|
||||||
env_manager.get_all_variables(env_vars);
|
// env_manager.get_all_variables(env_vars);
|
||||||
merge_vars(all_env_vars, env_vars);
|
// merge_vars(all_env_vars, env_vars);
|
||||||
}
|
// }
|
||||||
else
|
// else
|
||||||
warning << "Expected environment file not found: " << file << std::endl;
|
// 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.
|
// // 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, "SERVER", server_name);
|
||||||
set_var(all_env_vars, "SERVICE", service_name);
|
// set_var(all_env_vars, "SERVICE", service_name);
|
||||||
set_var(all_env_vars, "DOCKER_CLI_HINTS", "false"); // turn off docker junk.
|
// set_var(all_env_vars, "DOCKER_CLI_HINTS", "false"); // turn off docker junk.
|
||||||
|
|
||||||
// Load environment files
|
// // Load environment files
|
||||||
load_env_file(localfile::service_env(server_name, service_name));
|
// load_env_file(localfile::service_env(server_name, service_name));
|
||||||
|
|
||||||
std::string template_name = get_var(all_env_vars, "TEMPLATE");
|
// std::string template_name = get_var(all_env_vars, "TEMPLATE");
|
||||||
if (template_name.empty())
|
// if (template_name.empty())
|
||||||
{
|
// {
|
||||||
error << "TEMPLATE variable not defined in service " << service_name << " on server " << server_name << std::endl;
|
// error << "TEMPLATE variable not defined in service " << service_name << " on server " << server_name << std::endl;
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
auto tinfo = gTemplateManager().get_template_info(template_name, true); // skip updates.
|
// auto tinfo = gTemplateManager().get_template_info(template_name, true); // skip updates.
|
||||||
if (!tinfo.is_set())
|
// if (!tinfo.is_set())
|
||||||
{
|
// {
|
||||||
// Template is not available locally or from registry
|
// // Template is not available locally or from registry
|
||||||
error << "Template '" << template_name << "' not found locally or in registry" << std::endl;
|
// error << "Template '" << template_name << "' not found locally or in registry" << std::endl;
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
ASSERT(std::filesystem::exists(tinfo.local_template_info_env_path()));
|
// ASSERT(std::filesystem::exists(tinfo.local_template_info_env_path()));
|
||||||
load_env_file(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");
|
// std::string user = get_var(all_env_vars, "SSH_USER");
|
||||||
if (user.empty())
|
// if (user.empty())
|
||||||
{
|
// {
|
||||||
error << "SSH_USER variable not defined in service " << service_name << " on server " << server_name << std::endl;
|
// 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 << "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;
|
// info << "Please check " << localfile::service_env(server_name, service_name) << std::endl;
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// more additional, these depend on others above.
|
// // more additional, these depend on others above.
|
||||||
set_var(all_env_vars, "CONFIG_PATH", remotepath(server_name, user).service_config(service_name));
|
// set_var(all_env_vars, "CONFIG_PATH", remotepath(server_name, user).service_config(service_name));
|
||||||
set_var(all_env_vars, "AGENT_PATH", remotepath(server_name, user).agent());
|
// set_var(all_env_vars, "AGENT_PATH", remotepath(server_name, user).agent());
|
||||||
|
|
||||||
return true;
|
// return true;
|
||||||
}
|
// }
|
||||||
|
|
||||||
} // namespace dropshell
|
} // namespace dropshell
|
||||||
|
|||||||
@@ -29,9 +29,6 @@ namespace dropshell {
|
|||||||
LocalServiceInfo get_service_info(const std::string& server_name, const std::string& service_name, bool skip_update=false);
|
LocalServiceInfo get_service_info(const std::string& server_name, const std::string& service_name, bool skip_update=false);
|
||||||
std::set<std::string> get_used_commands(const std::string& server_name, const std::string& service_name);
|
std::set<std::string> get_used_commands(const std::string& server_name, const std::string& service_name);
|
||||||
|
|
||||||
// get all env vars for a given service
|
|
||||||
bool get_all_service_env_vars(const std::string& server_name, const std::string& service_name, ordered_env_vars& all_env_vars);
|
|
||||||
|
|
||||||
// list all backups for a given service (across all servers)
|
// list all backups for a given service (across all servers)
|
||||||
std::set<std::string> list_backups(const std::string& server_name, const std::string& service_name);
|
std::set<std::string> list_backups(const std::string& server_name, const std::string& service_name);
|
||||||
|
|
||||||
|
|||||||
@@ -891,6 +891,11 @@ For full documentation, see: dropshell help templates
|
|||||||
// mHash = hash_directory_recursive(local_template_path);
|
// mHash = hash_directory_recursive(local_template_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::filesystem::path template_info::local_template_service_env_path()
|
||||||
|
{
|
||||||
|
return mTemplateLocalPath / "config" / filenames::service_env ;
|
||||||
|
}
|
||||||
|
|
||||||
std::filesystem::path template_info::local_template_info_env_path()
|
std::filesystem::path template_info::local_template_info_env_path()
|
||||||
{
|
{
|
||||||
return mTemplateLocalPath / filenames::template_info_env ;
|
return mTemplateLocalPath / filenames::template_info_env ;
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ class template_info {
|
|||||||
std::string name() const { return mTemplateName; }
|
std::string name() const { return mTemplateName; }
|
||||||
std::string locationID() const { return mLocationID; }
|
std::string locationID() const { return mLocationID; }
|
||||||
std::filesystem::path local_template_path() const { return mTemplateLocalPath; }
|
std::filesystem::path local_template_path() const { return mTemplateLocalPath; }
|
||||||
|
std::filesystem::path local_template_service_env_path();
|
||||||
std::filesystem::path local_template_info_env_path();
|
std::filesystem::path local_template_info_env_path();
|
||||||
bool template_valid() const { return mTemplateValid; }
|
bool template_valid() const { return mTemplateValid; }
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user