feat: persistent custom labels
This commit is contained in:
@@ -10,12 +10,18 @@
|
||||
Spore::Spore() : ctx(), network(ctx), taskManager(ctx), cluster(ctx, taskManager),
|
||||
apiServer(ctx, taskManager, ctx.config.api_server_port),
|
||||
cpuUsage(), initialized(false), apiServerStarted(false) {
|
||||
|
||||
// Rebuild labels from constructor + config labels
|
||||
ctx.rebuildLabels();
|
||||
}
|
||||
|
||||
Spore::Spore(std::initializer_list<std::pair<String, String>> initialLabels)
|
||||
: ctx(initialLabels), network(ctx), taskManager(ctx), cluster(ctx, taskManager),
|
||||
apiServer(ctx, taskManager, ctx.config.api_server_port),
|
||||
cpuUsage(), initialized(false), apiServerStarted(false) {
|
||||
|
||||
// Rebuild labels from constructor + config labels (config takes precedence)
|
||||
ctx.rebuildLabels();
|
||||
}
|
||||
|
||||
Spore::~Spore() {
|
||||
|
||||
@@ -12,6 +12,7 @@ NodeContext::NodeContext() {
|
||||
|
||||
NodeContext::NodeContext(std::initializer_list<std::pair<String, String>> initialLabels) : NodeContext() {
|
||||
for (const auto& kv : initialLabels) {
|
||||
constructorLabels[kv.first] = kv.second;
|
||||
self.labels[kv.first] = kv.second;
|
||||
}
|
||||
}
|
||||
@@ -37,3 +38,18 @@ void NodeContext::fire(const std::string& event, void* data) {
|
||||
void NodeContext::onAny(AnyEventCallback cb) {
|
||||
anyEventSubscribers.push_back(cb);
|
||||
}
|
||||
|
||||
void NodeContext::rebuildLabels() {
|
||||
// Clear current labels
|
||||
self.labels.clear();
|
||||
|
||||
// Add constructor labels first
|
||||
for (const auto& kv : constructorLabels) {
|
||||
self.labels[kv.first] = kv.second;
|
||||
}
|
||||
|
||||
// Add config labels (these override constructor labels if same key)
|
||||
for (const auto& kv : config.labels) {
|
||||
self.labels[kv.first] = kv.second;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,13 @@ void NodeService::registerEndpoints(ApiServer& api) {
|
||||
[this](AsyncWebServerRequest* request) { handleEndpointsRequest(request); },
|
||||
std::vector<ParamSpec>{});
|
||||
|
||||
// Config endpoint for setting labels
|
||||
api.registerEndpoint("/api/node/config", HTTP_POST,
|
||||
[this](AsyncWebServerRequest* request) { handleConfigRequest(request); },
|
||||
std::vector<ParamSpec>{
|
||||
ParamSpec{String("labels"), true, String("body"), String("json"), {}, String("")}
|
||||
});
|
||||
|
||||
// Generic local event endpoint
|
||||
api.registerEndpoint("/api/node/event", HTTP_POST,
|
||||
[this](AsyncWebServerRequest* request) {
|
||||
@@ -175,3 +182,54 @@ void NodeService::handleEndpointsRequest(AsyncWebServerRequest* request) {
|
||||
serializeJson(doc, json);
|
||||
request->send(200, "application/json", json);
|
||||
}
|
||||
|
||||
void NodeService::handleConfigRequest(AsyncWebServerRequest* request) {
|
||||
if (!request->hasParam("labels", true)) {
|
||||
request->send(400, "application/json", "{\"error\":\"Missing 'labels' parameter\"}");
|
||||
return;
|
||||
}
|
||||
|
||||
String labelsJson = request->getParam("labels", true)->value();
|
||||
|
||||
// Parse the JSON
|
||||
JsonDocument doc;
|
||||
DeserializationError error = deserializeJson(doc, labelsJson);
|
||||
if (error) {
|
||||
request->send(400, "application/json", "{\"error\":\"Invalid JSON format: " + String(error.c_str()) + "\"}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Update config labels
|
||||
ctx.config.labels.clear();
|
||||
if (doc.is<JsonObject>()) {
|
||||
JsonObject labelsObj = doc.as<JsonObject>();
|
||||
for (JsonPair kv : labelsObj) {
|
||||
ctx.config.labels[kv.key().c_str()] = kv.value().as<String>();
|
||||
}
|
||||
}
|
||||
|
||||
// Rebuild self.labels from constructor + config labels
|
||||
ctx.rebuildLabels();
|
||||
|
||||
// TODO think of a better way to update the member list entry for the local node
|
||||
// Update the member list entry for this node if it exists
|
||||
if (ctx.memberList) {
|
||||
auto it = ctx.memberList->find(ctx.hostname);
|
||||
if (it != ctx.memberList->end()) {
|
||||
// Update the labels in the member list entry
|
||||
it->second.labels.clear();
|
||||
for (const auto& kv : ctx.self.labels) {
|
||||
it->second.labels[kv.first] = kv.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save config to file
|
||||
if (ctx.config.saveToFile()) {
|
||||
LOG_INFO("NodeService", "Labels updated and saved to config");
|
||||
request->send(200, "application/json", "{\"status\":\"success\",\"message\":\"Labels updated and saved\"}");
|
||||
} else {
|
||||
LOG_ERROR("NodeService", "Failed to save labels to config file");
|
||||
request->send(500, "application/json", "{\"error\":\"Failed to save configuration\"}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,9 @@ void Config::setDefaults() {
|
||||
low_memory_threshold_bytes = DEFAULT_LOW_MEMORY_THRESHOLD_BYTES; // 10KB
|
||||
critical_memory_threshold_bytes = DEFAULT_CRITICAL_MEMORY_THRESHOLD_BYTES; // 5KB
|
||||
max_concurrent_http_requests = DEFAULT_MAX_CONCURRENT_HTTP_REQUESTS;
|
||||
|
||||
// Custom Labels - start empty by default
|
||||
labels.clear();
|
||||
}
|
||||
|
||||
bool Config::saveToFile(const String& filename) {
|
||||
@@ -104,6 +107,12 @@ bool Config::saveToFile(const String& filename) {
|
||||
doc["memory"]["critical_memory_threshold_bytes"] = critical_memory_threshold_bytes;
|
||||
doc["memory"]["max_concurrent_http_requests"] = max_concurrent_http_requests;
|
||||
|
||||
// Custom Labels
|
||||
JsonObject labelsObj = doc["labels"].to<JsonObject>();
|
||||
for (const auto& kv : labels) {
|
||||
labelsObj[kv.first] = kv.second;
|
||||
}
|
||||
|
||||
// Add metadata
|
||||
doc["_meta"]["version"] = "1.0";
|
||||
doc["_meta"]["saved_at"] = millis();
|
||||
@@ -178,6 +187,15 @@ bool Config::loadFromFile(const String& filename) {
|
||||
critical_memory_threshold_bytes = doc["memory"]["critical_memory_threshold_bytes"] | DEFAULT_CRITICAL_MEMORY_THRESHOLD_BYTES;
|
||||
max_concurrent_http_requests = doc["memory"]["max_concurrent_http_requests"] | DEFAULT_MAX_CONCURRENT_HTTP_REQUESTS;
|
||||
|
||||
// Load Custom Labels
|
||||
labels.clear();
|
||||
if (doc["labels"].is<JsonObject>()) {
|
||||
JsonObject labelsObj = doc["labels"].as<JsonObject>();
|
||||
for (JsonPair kv : labelsObj) {
|
||||
labels[kv.key().c_str()] = kv.value().as<String>();
|
||||
}
|
||||
}
|
||||
|
||||
LOG_DEBUG("Config", "Loaded WiFi SSID: " + wifi_ssid);
|
||||
LOG_DEBUG("Config", "Config file version: " + String(doc["_meta"]["version"] | "unknown"));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user