Add overrides.env support for per-location service env overrides
This commit is contained in:
@@ -7,6 +7,7 @@
|
|||||||
#include <libassert/assert.hpp>
|
#include <libassert/assert.hpp>
|
||||||
#include "utils/utils.hpp"
|
#include "utils/utils.hpp"
|
||||||
#include "utils/service_env_validator.hpp"
|
#include "utils/service_env_validator.hpp"
|
||||||
|
#include "utils/service_env_overrides.hpp"
|
||||||
#include "services.hpp"
|
#include "services.hpp"
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
@@ -230,6 +231,13 @@ namespace dropshell
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// apply overrides from overrides.env (if present in server definition path)
|
||||||
|
{
|
||||||
|
auto changes = apply_overrides(server_name, service_name);
|
||||||
|
for (const auto& c : changes)
|
||||||
|
debug << "Override applied: " << c.key << "=" << c.new_val << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
// check docker.
|
// check docker.
|
||||||
if (service_info.requires_docker)
|
if (service_info.requires_docker)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,7 +8,9 @@
|
|||||||
#include "templates.hpp"
|
#include "templates.hpp"
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <termios.h>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
@@ -32,12 +34,13 @@ struct EditCommandRegister {
|
|||||||
false, // requires_install
|
false, // requires_install
|
||||||
0, // min_args (after command)
|
0, // min_args (after command)
|
||||||
2, // max_args (after command)
|
2, // max_args (after command)
|
||||||
"edit [SERVER] [SERVICE]",
|
"edit [SERVER] [SERVICE] | edit override",
|
||||||
"Edit dropshell, server or service configuration",
|
"Edit dropshell, server, service, or override configuration",
|
||||||
// heredoc
|
// heredoc
|
||||||
R"(
|
R"(
|
||||||
Edit dropshell, server or service configuration.
|
Edit dropshell, server or service configuration.
|
||||||
edit edit the dropshell config.
|
edit edit the dropshell config.
|
||||||
|
edit override edit the overrides.env for a server location.
|
||||||
edit <server> edit the server config.
|
edit <server> edit the server config.
|
||||||
edit <server> <service> edit the service config.
|
edit <server> <service> edit the service config.
|
||||||
)"
|
)"
|
||||||
@@ -241,6 +244,64 @@ int edit_service_config(const std::string &server, const std::string &service)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// edit override
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
int edit_override()
|
||||||
|
{
|
||||||
|
auto paths = gConfig().get_local_server_definition_paths();
|
||||||
|
if (paths.empty()) {
|
||||||
|
error << "No server definition paths configured." << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string chosen_path;
|
||||||
|
if (paths.size() == 1) {
|
||||||
|
chosen_path = paths[0];
|
||||||
|
} else {
|
||||||
|
// Multiple paths - ask user to choose with single keypress
|
||||||
|
info << "Multiple server locations found. Choose one:" << std::endl;
|
||||||
|
for (size_t i = 0; i < paths.size() && i < 9; ++i)
|
||||||
|
info << " " << (i + 1) << ") " << paths[i] << std::endl;
|
||||||
|
|
||||||
|
info << "Enter choice [1-" << std::min(paths.size(), (size_t)9) << "]: " << std::flush;
|
||||||
|
|
||||||
|
// Read single keypress
|
||||||
|
struct termios oldt, newt;
|
||||||
|
tcgetattr(STDIN_FILENO, &oldt);
|
||||||
|
newt = oldt;
|
||||||
|
newt.c_lflag &= ~(ICANON | ECHO);
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
|
||||||
|
int ch = getchar();
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
|
||||||
|
info << (char)ch << std::endl;
|
||||||
|
|
||||||
|
int idx = ch - '1';
|
||||||
|
if (idx < 0 || idx >= (int)paths.size()) {
|
||||||
|
error << "Invalid choice." << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
chosen_path = paths[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string override_file = (std::filesystem::path(chosen_path) / filenames::overrides_env).string();
|
||||||
|
|
||||||
|
// Create with comment header if it doesn't exist
|
||||||
|
if (!std::filesystem::exists(override_file)) {
|
||||||
|
std::ofstream f(override_file);
|
||||||
|
f << "# Overrides for all services in this server location." << std::endl;
|
||||||
|
f << "# Variables set here will be forced into every service.env" << std::endl;
|
||||||
|
f << "# during create-service and install." << std::endl;
|
||||||
|
f << "#" << std::endl;
|
||||||
|
f << "# Format is the same as service.env:" << std::endl;
|
||||||
|
f << "# VARIABLE_NAME=\"value\"" << std::endl;
|
||||||
|
f << std::endl;
|
||||||
|
info << "Created new overrides file: " << override_file << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return edit_file(override_file, true) ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// edit command handler
|
// edit command handler
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
@@ -258,6 +319,10 @@ int edit_handler(const CommandContext& ctx) {
|
|||||||
return edit_config();
|
return edit_config();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// edit override - check before server/service dispatch
|
||||||
|
if (safearg(ctx.args, 0) == "override")
|
||||||
|
return edit_override();
|
||||||
|
|
||||||
// edit server config
|
// edit server config
|
||||||
if (ctx.args.size() < 2) {
|
if (ctx.args.size() < 2) {
|
||||||
edit_server(safearg(ctx.args,0));
|
edit_server(safearg(ctx.args,0));
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "services.hpp"
|
#include "services.hpp"
|
||||||
#include "utils/output.hpp"
|
#include "utils/output.hpp"
|
||||||
#include "utils/service_env_validator.hpp"
|
#include "utils/service_env_validator.hpp"
|
||||||
|
#include "utils/service_env_overrides.hpp"
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@@ -147,6 +148,13 @@ namespace dropshell
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply overrides from overrides.env (if present in server definition path)
|
||||||
|
{
|
||||||
|
auto changes = apply_overrides(server, service);
|
||||||
|
for (const auto& c : changes)
|
||||||
|
warning << "Override: " << c.key << " changed from \"" << c.old_val << "\" to \"" << c.new_val << "\"" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
// ── Stage new template + config to _staging folder on remote ──
|
// ── Stage new template + config to _staging folder on remote ──
|
||||||
std::string staging_path = remote_service_path + "/_staging";
|
std::string staging_path = remote_service_path + "/_staging";
|
||||||
std::string staging_template = staging_path + "/template";
|
std::string staging_template = staging_path + "/template";
|
||||||
|
|||||||
@@ -43,6 +43,14 @@ namespace dropshell
|
|||||||
return localpath::agent_remote() + "/agent.hash";
|
return localpath::agent_remote() + "/agent.hash";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string overrides_env_for_server(const std::string &server_name)
|
||||||
|
{
|
||||||
|
std::string serverpath = localpath::server(server_name);
|
||||||
|
if (serverpath.empty())
|
||||||
|
return "";
|
||||||
|
return (fs::path(get_parent(serverpath)) / filenames::overrides_env).string();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace localfile
|
} // namespace localfile
|
||||||
|
|
||||||
std::string get_local_agent_hash()
|
std::string get_local_agent_hash()
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ namespace dropshell {
|
|||||||
static const std::string ds_run = "ds_run.sh";
|
static const std::string ds_run = "ds_run.sh";
|
||||||
static const std::string template_paths_json = "template_paths.json";
|
static const std::string template_paths_json = "template_paths.json";
|
||||||
static const std::string dropshell_templates_list = "dropshell-templates.list";
|
static const std::string dropshell_templates_list = "dropshell-templates.list";
|
||||||
|
static const std::string overrides_env = "overrides.env";
|
||||||
} // namespace filenames.
|
} // namespace filenames.
|
||||||
|
|
||||||
namespace localfile {
|
namespace localfile {
|
||||||
@@ -51,6 +52,7 @@ namespace dropshell {
|
|||||||
std::string service_env(const std::string &server_name, const std::string &service_name);
|
std::string service_env(const std::string &server_name, const std::string &service_name);
|
||||||
std::string bb64();
|
std::string bb64();
|
||||||
std::string agent_hash(); // Returns path to agent.hash file
|
std::string agent_hash(); // Returns path to agent.hash file
|
||||||
|
std::string overrides_env_for_server(const std::string &server_name); // overrides.env in server's definition path
|
||||||
} // namespace localfile
|
} // namespace localfile
|
||||||
|
|
||||||
// Get the content of the local agent hash (empty string if not found)
|
// Get the content of the local agent hash (empty string if not found)
|
||||||
|
|||||||
51
source/src/utils/service_env_overrides.cpp
Normal file
51
source/src/utils/service_env_overrides.cpp
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#include "service_env_overrides.hpp"
|
||||||
|
#include "directories.hpp"
|
||||||
|
#include "envmanager.hpp"
|
||||||
|
#include "service_env_validator.hpp"
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
namespace dropshell {
|
||||||
|
|
||||||
|
std::vector<OverrideChange> apply_overrides(
|
||||||
|
const std::string& server_name,
|
||||||
|
const std::string& service_name)
|
||||||
|
{
|
||||||
|
std::vector<OverrideChange> changes;
|
||||||
|
|
||||||
|
std::string overrides_path = localfile::overrides_env_for_server(server_name);
|
||||||
|
if (overrides_path.empty() || !std::filesystem::exists(overrides_path))
|
||||||
|
return changes;
|
||||||
|
|
||||||
|
std::string service_env_path = localfile::service_env(server_name, service_name);
|
||||||
|
if (service_env_path.empty() || !std::filesystem::exists(service_env_path))
|
||||||
|
return changes;
|
||||||
|
|
||||||
|
// Load overrides
|
||||||
|
envmanager overrides(overrides_path);
|
||||||
|
if (!overrides.load())
|
||||||
|
return changes;
|
||||||
|
|
||||||
|
ordered_env_vars override_vars;
|
||||||
|
overrides.get_all_variables(override_vars);
|
||||||
|
if (override_vars.empty())
|
||||||
|
return changes;
|
||||||
|
|
||||||
|
// Load current service env
|
||||||
|
envmanager service_env(service_env_path);
|
||||||
|
if (!service_env.load())
|
||||||
|
return changes;
|
||||||
|
|
||||||
|
// Apply each override
|
||||||
|
for (const auto& [key, new_val] : override_vars) {
|
||||||
|
std::string current_val = service_env.get_variable(key);
|
||||||
|
if (current_val != new_val) {
|
||||||
|
set_env_variable(service_env_path, key, new_val);
|
||||||
|
changes.push_back({key, current_val, new_val});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dropshell
|
||||||
24
source/src/utils/service_env_overrides.hpp
Normal file
24
source/src/utils/service_env_overrides.hpp
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#ifndef SERVICE_ENV_OVERRIDES_HPP
|
||||||
|
#define SERVICE_ENV_OVERRIDES_HPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace dropshell {
|
||||||
|
|
||||||
|
struct OverrideChange {
|
||||||
|
std::string key;
|
||||||
|
std::string old_val;
|
||||||
|
std::string new_val;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Apply overrides from overrides.env (in the server's definition path) to a service's service.env.
|
||||||
|
// Returns a list of changes that were made.
|
||||||
|
// If no overrides.env exists, returns empty (no-op).
|
||||||
|
std::vector<OverrideChange> apply_overrides(
|
||||||
|
const std::string& server_name,
|
||||||
|
const std::string& service_name);
|
||||||
|
|
||||||
|
} // namespace dropshell
|
||||||
|
|
||||||
|
#endif // SERVICE_ENV_OVERRIDES_HPP
|
||||||
Reference in New Issue
Block a user