Add overrides.env support for per-location service env overrides
All checks were successful
Build-Test-Publish / build (linux/amd64) (push) Successful in 48s
Build-Test-Publish / build (linux/arm64) (push) Successful in 3m44s

This commit is contained in:
j
2026-03-30 08:04:23 +13:00
parent bf9cdeccc7
commit 735e2f083a
7 changed files with 169 additions and 3 deletions

View File

@@ -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)
{ {

View File

@@ -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));

View File

@@ -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";

View File

@@ -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()

View File

@@ -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)

View 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

View 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