|
5 | 5 | #include "remote/configobjectutility.hpp" |
6 | 6 | #include "remote/jsonrpc.hpp" |
7 | 7 | #include "base/configtype.hpp" |
8 | | -#include "base/json.hpp" |
9 | 8 | #include "base/convert.hpp" |
| 9 | +#include "base/dependencygraph.hpp" |
| 10 | +#include "base/json.hpp" |
10 | 11 | #include "config/vmops.hpp" |
11 | 12 | #include "remote/configobjectslock.hpp" |
12 | 13 | #include <fstream> |
| 14 | +#include <unordered_set> |
13 | 15 |
|
14 | 16 | using namespace icinga; |
15 | 17 |
|
@@ -393,6 +395,40 @@ void ApiListener::UpdateConfigObject(const ConfigObject::Ptr& object, const Mess |
393 | 395 | } |
394 | 396 | } |
395 | 397 |
|
| 398 | +/** |
| 399 | + * Syncs the specified object and its direct and indirect parents to the provided client |
| 400 | + * in topological order of their dependency graph recursively. |
| 401 | + * |
| 402 | + * Objects that the client does not have access to are skipped without going through their dependency graph. |
| 403 | + * |
| 404 | + * Please do not use this method to forward remote generated cluster updates; it should only be used to |
| 405 | + * send local updates to that specific non-nullptr client. |
| 406 | + * |
| 407 | + * @param object The config object you want to sync. |
| 408 | + * @param azone The zone of the client you want to send the update to. |
| 409 | + * @param client The JsonRpc client you send the update to. |
| 410 | + * @param syncedObjects Used to cache the already synced objects. |
| 411 | + */ |
| 412 | +void ApiListener::UpdateConfigObjectWithParents(const ConfigObject::Ptr& object, const Zone::Ptr& azone, |
| 413 | + const JsonRpcConnection::Ptr& client, std::unordered_set<ConfigObject*>& syncedObjects) |
| 414 | +{ |
| 415 | + if (syncedObjects.find(object.get()) != syncedObjects.end()) { |
| 416 | + return; |
| 417 | + } |
| 418 | + |
| 419 | + /* don't sync objects for non-matching parent-child zones */ |
| 420 | + if (!azone->CanAccessObject(object)) { |
| 421 | + return; |
| 422 | + } |
| 423 | + syncedObjects.emplace(object.get()); |
| 424 | + |
| 425 | + for (const auto& parent : DependencyGraph::GetParents(object)) { |
| 426 | + UpdateConfigObjectWithParents(parent, azone, client, syncedObjects); |
| 427 | + } |
| 428 | + |
| 429 | + /* send the config object to the connected client */ |
| 430 | + UpdateConfigObject(object, nullptr, client); |
| 431 | +} |
396 | 432 |
|
397 | 433 | void ApiListener::DeleteConfigObject(const ConfigObject::Ptr& object, const MessageOrigin::Ptr& origin, |
398 | 434 | const JsonRpcConnection::Ptr& client) |
@@ -454,19 +490,17 @@ void ApiListener::SendRuntimeConfigObjects(const JsonRpcConnection::Ptr& aclient |
454 | 490 | Log(LogInformation, "ApiListener") |
455 | 491 | << "Syncing runtime objects to endpoint '" << endpoint->GetName() << "'."; |
456 | 492 |
|
| 493 | + std::unordered_set<ConfigObject*> syncedObjects; |
457 | 494 | for (const Type::Ptr& type : Type::GetAllTypes()) { |
458 | | - auto *dtype = dynamic_cast<ConfigType *>(type.get()); |
459 | | - |
460 | | - if (!dtype) |
461 | | - continue; |
462 | | - |
463 | | - for (const ConfigObject::Ptr& object : dtype->GetObjects()) { |
464 | | - /* don't sync objects for non-matching parent-child zones */ |
465 | | - if (!azone->CanAccessObject(object)) |
466 | | - continue; |
467 | | - |
468 | | - /* send the config object to the connected client */ |
469 | | - UpdateConfigObject(object, nullptr, aclient); |
| 495 | + if (auto *ctype = dynamic_cast<ConfigType *>(type.get())) { |
| 496 | + for (const auto& object : ctype->GetObjects()) { |
| 497 | + // All objects must be synced sorted by their dependency graph. |
| 498 | + // Otherwise, downtimes/comments etc. might get synced before their respective Checkables, which will |
| 499 | + // result in comments and downtimes being ignored by the other endpoint since it does not yet know |
| 500 | + // about their checkables. Given that the runtime config updates event does not trigger a reload on the |
| 501 | + // remote endpoint, these objects won't be synced again until the next reload. |
| 502 | + UpdateConfigObjectWithParents(object, azone, aclient, syncedObjects); |
| 503 | + } |
470 | 504 | } |
471 | 505 | } |
472 | 506 |
|
|
0 commit comments