Merge pull request #7584 from Icinga/bugfix/cluster-sync-checksums-timestamps

Cluster Config Sync: Check the timestamp prior to config file checksums for changes
This commit is contained in:
Michael Friedrich 2019-10-22 17:22:27 +02:00 committed by GitHub
commit 3b49c10d0a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 58 additions and 40 deletions

View File

@ -229,6 +229,56 @@ void ApiListener::SendConfigUpdate(const JsonRpcConnection::Ptr& aclient)
aclient->SendMessage(message);
}
static bool CompareTimestampsConfigChange(const Dictionary::Ptr& productionConfig, const Dictionary::Ptr& receivedConfig,
const String& stageConfigZoneDir)
{
double productionTimestamp;
double receivedTimestamp;
// Missing production timestamp means that something really broke. Always trigger a config change then.
if (!productionConfig->Contains("/.timestamp"))
productionTimestamp = 0;
else
productionTimestamp = productionConfig->Get("/.timestamp");
// Missing received config timestamp means that something really broke. Always trigger a config change then.
if (!receivedConfig->Contains("/.timestamp"))
receivedTimestamp = Utility::GetTime() + 10;
else
receivedTimestamp = receivedConfig->Get("/.timestamp");
bool configChange;
// Skip update if our configuration files are more recent.
if (productionTimestamp >= receivedTimestamp) {
Log(LogInformation, "ApiListener")
<< "Our production configuration is more recent than the received configuration update."
<< " Ignoring configuration file update for path '" << stageConfigZoneDir << "'. Current timestamp '"
<< Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", productionTimestamp) << "' ("
<< std::fixed << std::setprecision(6) << productionTimestamp
<< ") >= received timestamp '"
<< Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", receivedTimestamp) << "' ("
<< receivedTimestamp << ").";
configChange = false;
} else {
configChange = true;
}
// Update the .timestamp file inside the staging directory.
String tsPath = stageConfigZoneDir + "/.timestamp";
if (!Utility::PathExists(tsPath)) {
std::ofstream fp(tsPath.CStr(), std::ofstream::out | std::ostream::trunc);
fp << std::fixed << receivedTimestamp;
fp.close();
}
return configChange;
}
/**
* Registered handler when a new config::Update message is received.
*
@ -360,11 +410,13 @@ Value ApiListener::ConfigUpdateHandler(const MessageOrigin::Ptr& origin, const D
if (checksums) {
Log(LogInformation, "ApiListener")
<< "Received configuration for zone '" << zoneName << "' from endpoint '"
<< fromEndpointName << "'. Comparing the checksums.";
<< fromEndpointName << "'. Comparing the timestamp and checksums.";
if (CompareTimestampsConfigChange(productionConfig, newConfig, stageConfigZoneDir)) {
// TODO: Do this earlier in hello-handshakes?
if (CheckConfigChange(productionConfigInfo, newConfigInfo))
configChange = true;
}
} else {
/* Fallback to timestamp handling when the parent endpoint didn't send checks.
@ -377,33 +429,7 @@ Value ApiListener::ConfigUpdateHandler(const MessageOrigin::Ptr& origin, const D
<< "Received configuration update without checksums from parent endpoint "
<< fromEndpointName << ". This behaviour is deprecated. Please upgrade the parent endpoint to 2.11+";
double productionTimestamp;
if (!productionConfig->Contains("/.timestamp"))
productionTimestamp = 0;
else
productionTimestamp = productionConfig->Get("/.timestamp");
double newTimestamp;
if (!newConfig->Contains("/.timestamp"))
newTimestamp = Utility::GetTime();
else
newTimestamp = newConfig->Get("/.timestamp");
// Skip update if our configuration files are more recent.
if (productionTimestamp >= newTimestamp) {
Log(LogInformation, "ApiListener")
<< "Our configuration is more recent than the received configuration update."
<< " Ignoring configuration file update for path '" << stageConfigZoneDir << "'. Current timestamp '"
<< Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", productionTimestamp) << "' ("
<< std::fixed << std::setprecision(6) << productionTimestamp
<< ") >= received timestamp '"
<< Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", newTimestamp) << "' ("
<< newTimestamp << ").";
} else {
if (CompareTimestampsConfigChange(productionConfig, newConfig, stageConfigZoneDir)) {
configChange = true;
}
@ -420,14 +446,6 @@ Value ApiListener::ConfigUpdateHandler(const MessageOrigin::Ptr& origin, const D
}
}
}
// Update the .timestamp file.
String tsPath = stageConfigZoneDir + "/.timestamp";
if (!Utility::PathExists(tsPath)) {
std::ofstream fp(tsPath.CStr(), std::ofstream::out | std::ostream::trunc);
fp << std::fixed << newTimestamp;
fp.close();
}
}
// Dump the received configuration for this zone into the stage directory.
@ -507,7 +525,7 @@ Value ApiListener::ConfigUpdateHandler(const MessageOrigin::Ptr& origin, const D
} else {
Log(LogInformation, "ApiListener")
<< "Received configuration updates (" << count << ") from endpoint '" << fromEndpointName
<< "' are equal to production, not triggering reload.";
<< "' do not qualify for production, not triggering reload.";
}
return Empty;