Skip to content

Commit

Permalink
ApiListener: Sync runtime configs in order
Browse files Browse the repository at this point in the history
  • Loading branch information
yhabteab committed Dec 3, 2024
1 parent c004469 commit 1261254
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 14 deletions.
73 changes: 59 additions & 14 deletions lib/remote/apilistener-configsync.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
#include "remote/configobjectutility.hpp"
#include "remote/jsonrpc.hpp"
#include "base/configtype.hpp"
#include "base/json.hpp"
#include "base/convert.hpp"
#include "base/dependencygraph.hpp"
#include "base/json.hpp"
#include "config/vmops.hpp"
#include "remote/configobjectslock.hpp"
#include <fstream>
#include <unordered_set>

using namespace icinga;

Expand Down Expand Up @@ -393,6 +395,51 @@ void ApiListener::UpdateConfigObject(const ConfigObject::Ptr& object, const Mess
}
}

/**
* Syncs the specified object and its direct and indirect parents to the provided client
* in topological order of their dependency graph recursively.
*
* Objects that the client does not have access to are skipped without going through their dependency graph.
*
* Please do not use this method to forward remote generated cluster updates; it should only be used to
* send local updates to that specific non-nullptr client.
*
* @param object The config object you want to sync.
* @param client The JsonRpc client you send the update to.
* @param syncedObjects Used to cache the already synced objects.
*/
void ApiListener::UpdateConfigObjectWithParents(const ConfigObject::Ptr& object, const JsonRpcConnection::Ptr& client,
std::unordered_set<ConfigObject*>& syncedObjects)
{
if (syncedObjects.find(object.get()) != syncedObjects.end()) {
return;
}

VERIFY(client);

Endpoint::Ptr endpoint = client->GetEndpoint();
ASSERT(endpoint);

Zone::Ptr azone = endpoint->GetZone();

/* don't sync objects for non-matching parent-child zones */
if (!azone->CanAccessObject(object)) {
return;
}
syncedObjects.emplace(object.get());

for (const auto& parent : DependencyGraph::GetParents(object)) {
// Actually, the following dynamic cast should never fail, since the DependencyGraph class
// expects the types to always be of type Object::Ptr and such an object is supposed to always
// point to an instance of the specific derived ConfigObject class. See TypeHelper<>::GetFactory().
if (auto parentObj = dynamic_pointer_cast<ConfigObject>(parent)) {
UpdateConfigObjectWithParents(parentObj, client, syncedObjects);
}
}

/* send the config object to the connected client */
UpdateConfigObject(object, nullptr, client);
}

void ApiListener::DeleteConfigObject(const ConfigObject::Ptr& object, const MessageOrigin::Ptr& origin,
const JsonRpcConnection::Ptr& client)
Expand Down Expand Up @@ -454,19 +501,17 @@ void ApiListener::SendRuntimeConfigObjects(const JsonRpcConnection::Ptr& aclient
Log(LogInformation, "ApiListener")
<< "Syncing runtime objects to endpoint '" << endpoint->GetName() << "'.";

for (const Type::Ptr& type : Type::GetAllTypes()) {
auto *dtype = dynamic_cast<ConfigType *>(type.get());

if (!dtype)
continue;

for (const ConfigObject::Ptr& object : dtype->GetObjects()) {
/* don't sync objects for non-matching parent-child zones */
if (!azone->CanAccessObject(object))
continue;

/* send the config object to the connected client */
UpdateConfigObject(object, nullptr, aclient);
std::unordered_set<ConfigObject*> syncedObjects;
for (const auto& type : Type::GetConfigTypesSortedByLoadDependencies()) {
if (auto *ctype = dynamic_cast<ConfigType *>(type.get())) {
for (const auto& object : ctype->GetObjects()) {
// All objects must be synced sorted by their dependency graph.
// Otherwise, downtimes/comments etc. might get synced before their respective Checkables, which will
// result in comments and downtimes being ignored by the other endpoint since it does not yet know
// about their checkables. Given that the runtime config updates event does not trigger a reload on the
// remote endpoint, these objects won't be synced again until the next reload.
UpdateConfigObjectWithParents(object, aclient, syncedObjects);
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions lib/remote/apilistener.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,8 @@ class ApiListener final : public ObjectImpl<ApiListener>
/* configsync */
void UpdateConfigObject(const ConfigObject::Ptr& object, const MessageOrigin::Ptr& origin,
const JsonRpcConnection::Ptr& client = nullptr);
void UpdateConfigObjectWithParents(const ConfigObject::Ptr& object, const JsonRpcConnection::Ptr& client,
std::unordered_set<ConfigObject*>& syncedObjects);
void DeleteConfigObject(const ConfigObject::Ptr& object, const MessageOrigin::Ptr& origin,
const JsonRpcConnection::Ptr& client = nullptr);
void SendRuntimeConfigObjects(const JsonRpcConnection::Ptr& aclient);
Expand Down

0 comments on commit 1261254

Please sign in to comment.