Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix broken downtime comment sync #10000

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/icinga/dependency.ti
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class Dependency : CustomVarObject < DependencyNameComposer
{
load_after Host;
load_after Service;
load_after TimePeriod;

[config, no_user_modify, required, navigation(child_host)] name(Host) child_host_name {
navigate {{{
Expand Down
4 changes: 4 additions & 0 deletions lib/icinga/host.ti
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ class Host : Checkable
load_after ApiListener;
load_after Endpoint;
load_after Zone;
load_after CheckCommand;
load_after EventCommand;
load_after HostGroup;
load_after TimePeriod;

[config, no_user_modify, required, signal_with_old_value] array(name(HostGroup)) groups {
default {{{ return new Array(); }}}
Expand Down
5 changes: 5 additions & 0 deletions lib/icinga/notification.ti
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ class Notification : CustomVarObject < NotificationNameComposer
{
load_after Host;
load_after Service;
load_after Endpoint;
load_after TimePeriod;
load_after NotificationCommand;
load_after User;
load_after UserGroup;

[config, protected, required, navigation] name(NotificationCommand) command (CommandRaw) {
navigate {{{
Expand Down
4 changes: 4 additions & 0 deletions lib/icinga/service.ti
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ class Service : Checkable < ServiceNameComposer
load_after Endpoint;
load_after Host;
load_after Zone;
load_after CheckCommand;
load_after EventCommand;
load_after ServiceGroup;
load_after TimePeriod;

[config, no_user_modify, required, signal_with_old_value] array(name(ServiceGroup)) groups {
default {{{ return new Array(); }}}
Expand Down
3 changes: 3 additions & 0 deletions lib/icinga/user.ti
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ namespace icinga

class User : CustomVarObject
{
load_after TimePeriod;
load_after UserGroup;

[config] String display_name {
get {{{
String displayName = m_DisplayName.load();
Expand Down
74 changes: 60 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,52 @@ void ApiListener::UpdateConfigObject(const ConfigObject::Ptr& object, const Mess
}
}

/**
* Syncs the specified object and its direct and intermediate 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 ConfigObject::Ptr The config object you want to sync.
* @param client JsonRpcConnection::Ptr The JsonRpc client you send the update to.
* @param syncedObjects std::unordered_set<ConfigObject*> Used to cache the already synced objects.
*/
void ApiListener::UpdateConfigObjectWithParents(const ConfigObject::Ptr& object, const JsonRpcConnection::Ptr& client,
std::unordered_set<ConfigObject*>& syncedObjects)
{
// Don't sync already synced objects.
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 Object::Ptr& 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 (ConfigObject::Ptr 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 +502,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 Type::Ptr& type : Type::GetConfigTypesSortedByLoadDependencies()) {
if (auto *ctype = dynamic_cast<ConfigType *>(type.get()); ctype) {
for (const ConfigObject::Ptr& 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
Loading