From 1c4cf7c98d98c4881bb5a13bfa75941a5c63aa2b Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 8 Sep 2020 12:00:00 +0200 Subject: [PATCH] Config sync: send config::HaveZones to v2.13+ on connect refs #8210 --- lib/remote/apilistener-filesync.cpp | 116 ++++++++++++++++++++++++++++ lib/remote/apilistener.cpp | 6 +- lib/remote/apilistener.hpp | 1 + 3 files changed, 122 insertions(+), 1 deletion(-) diff --git a/lib/remote/apilistener-filesync.cpp b/lib/remote/apilistener-filesync.cpp index 3ced0027f..4f85153e6 100644 --- a/lib/remote/apilistener-filesync.cpp +++ b/lib/remote/apilistener-filesync.cpp @@ -17,6 +17,7 @@ #include #include #include +#include using namespace icinga; @@ -255,6 +256,121 @@ void ApiListener::SendConfigUpdate(const JsonRpcConnection::Ptr& aclient) aclient->SendMessage(message); } +static Dictionary::Ptr AssembleZoneDeclaration(const String& zoneName) +{ + auto base (ApiListener::GetApiZonesDir() + zoneName + "/"); + Dictionary::Ptr declaration = new Dictionary(); + + { + auto file (base + ".timestamp"); + std::ifstream fp (file.CStr(), std::ifstream::binary); + + if (!fp) { + return nullptr; + } + + double ts; + + try { + ts = Convert::ToDouble(String(std::istreambuf_iterator(fp), std::istreambuf_iterator())); + } catch (const std::exception&) { + return nullptr; + } + + declaration->Set("timestamp", ts); + } + + { + auto file (base + ".checksum"); + std::ifstream fp (file.CStr(), std::ifstream::binary); + + if (!fp) { + return nullptr; + } + + String content; + + try { + content = String(std::istreambuf_iterator(fp), std::istreambuf_iterator()); + } catch (const std::exception&) { + return nullptr; + } + + declaration->Set("checksum", content); + } + + return std::move(declaration); +} + +/** + * Entrypoint for sending a file based config declaration to a cluster client. + * This includes security checks for zone relations. + * Loads the zone config files where this client belongs to + * and sends the 'config::HasZones' JSON-RPC message. + * + * @param aclient Connected JSON-RPC client. + */ +void ApiListener::DeclareConfigUpdate(const JsonRpcConnection::Ptr& client) +{ + Endpoint::Ptr endpoint = client->GetEndpoint(); + ASSERT(endpoint); + + Zone::Ptr clientZone = endpoint->GetZone(); + + // Don't send config declarations to parent zones + if (!clientZone->IsChildOf(Zone::GetLocalZone())) { + return; + } + + Dictionary::Ptr declaration = new Dictionary(); + String zonesDir = GetApiZonesDir(); + + for (auto& zone : ConfigType::GetObjectsByType()) { + String zoneName = zone->GetName(); + String zoneDir = zonesDir + zoneName; + + // Only declare child and global zones. + if (!zone->IsChildOf(clientZone) && !zone->IsGlobal()) { + continue; + } + + // Zone was configured, but there's no configuration directory. + if (!Utility::PathExists(zoneDir)) { + continue; + } + + auto perZone (AssembleZoneDeclaration(zoneName)); + + if (!perZone) { + // We likely just were upgraded to v2.13 and our non-authoritive config + // hasn't been synced from the config master, yet. + + Log(LogNotice, "ApiListener") + << "Not informing endpoint '" << endpoint->GetName() << "' about " + << (zone->IsGlobal() ? "global " : "") << "zone '" << zoneName + << "' to due to yet missing/bad .timestamp/.checksum file in '" << zoneDir << "'."; + + continue; + } + + Log(LogInformation, "ApiListener") + << "Informing endpoint '" << endpoint->GetName() << "' about " + << (zone->IsGlobal() ? "global " : "") << "zone '" << zoneName << "'."; + + declaration->Set(zoneName, perZone); + } + + if (declaration->GetLength()) { + client->SendMessage(new Dictionary({ + { "jsonrpc", "2.0" }, + { "method", "config::HaveZones" }, + { "params", new Dictionary({ + { "zones", declaration } + }) } + })); + } +} + static bool CompareTimestampsConfigChange(const Dictionary::Ptr& productionConfig, const Dictionary::Ptr& receivedConfig, const String& stageConfigZoneDir) { diff --git a/lib/remote/apilistener.cpp b/lib/remote/apilistener.cpp index e13ce231d..5d042fbc7 100644 --- a/lib/remote/apilistener.cpp +++ b/lib/remote/apilistener.cpp @@ -790,7 +790,11 @@ void ApiListener::SyncClient(const JsonRpcConnection::Ptr& aclient, const Endpoi bool negotiate = endpoint->GetVersion() >= 21300; /* sync zone file config */ - SendConfigUpdate(aclient); + if (negotiate) { + DeclareConfigUpdate(aclient); + } else { + SendConfigUpdate(aclient); + } Log(LogInformation, "ApiListener") << "Finished sending config file updates for endpoint '" << endpoint->GetName() << "' in zone '" << eZone->GetName() << "'."; diff --git a/lib/remote/apilistener.hpp b/lib/remote/apilistener.hpp index dbdc79b37..5bce3f152 100644 --- a/lib/remote/apilistener.hpp +++ b/lib/remote/apilistener.hpp @@ -194,6 +194,7 @@ private: void SyncLocalZoneDir(const Zone::Ptr& zone) const; void SendConfigUpdate(const JsonRpcConnection::Ptr& aclient); + void DeclareConfigUpdate(const JsonRpcConnection::Ptr& aclient); static Dictionary::Ptr MergeConfigUpdate(const ConfigDirInformation& config);