]> granicus.if.org Git - icinga2/commitdiff
Split config file sync updates, part I
authorMichael Friedrich <michael.friedrich@icinga.com>
Thu, 25 Oct 2018 12:10:30 +0000 (14:10 +0200)
committerMichael Friedrich <michael.friedrich@icinga.com>
Wed, 19 Jun 2019 12:46:11 +0000 (14:46 +0200)
This commit also introduces a playground for checksums,
whilst refactoring the code in large parts.

lib/remote/apilistener-filesync.cpp
lib/remote/apilistener.cpp
lib/remote/apilistener.hpp

index a656173187c4fb94d8fb47c071579b8cc48cd8fb..cec19e580a432bd875e32483b73f2ca70189beed 100644 (file)
@@ -3,6 +3,8 @@
 #include "remote/apilistener.hpp"
 #include "remote/apifunction.hpp"
 #include "config/configcompiler.hpp"
+#include "base/tlsutility.hpp"
+#include "base/json.hpp"
 #include "base/configtype.hpp"
 #include "base/logger.hpp"
 #include "base/convert.hpp"
@@ -18,196 +20,18 @@ REGISTER_APIFUNCTION(Update, config, &ApiListener::ConfigUpdateHandler);
 boost::mutex ApiListener::m_ConfigSyncStageLock;
 
 /**
- * Read the given file and store it in the config information structure.
- * Callback function for Glob().
- *
- * @param config Reference to the config information object.
- * @param path File path.
- * @param file Full file name.
- */
-void ApiListener::ConfigGlobHandler(ConfigDirInformation& config, const String& path, const String& file)
-{
-       CONTEXT("Creating config update for file '" + file + "'");
-
-       Log(LogNotice, "ApiListener")
-               << "Creating config update for file '" << file << "'.";
-
-       std::ifstream fp(file.CStr(), std::ifstream::binary);
-       if (!fp)
-               return;
-
-       String content((std::istreambuf_iterator<char>(fp)), std::istreambuf_iterator<char>());
-
-       Dictionary::Ptr update;
-
-       /*
-        * 'update' messages contain conf files. 'update_v2' syncs everything else (.timestamp).
-        *
-        * **Keep this intact to stay compatible with older clients.**
-        */
-       if (Utility::Match("*.conf", file))
-               update = config.UpdateV1;
-       else
-               update = config.UpdateV2;
-
-       update->Set(file.SubStr(path.GetLength()), content);
-}
-
-/**
- * Compatibility helper for merging config update v1 and v2 into a global result.
- *
- * @param config Config information structure.
- * @returns Dictionary which holds the merged information.
- */
-Dictionary::Ptr ApiListener::MergeConfigUpdate(const ConfigDirInformation& config)
-{
-       Dictionary::Ptr result = new Dictionary();
-
-       if (config.UpdateV1)
-               config.UpdateV1->CopyTo(result);
-
-       if (config.UpdateV2)
-               config.UpdateV2->CopyTo(result);
-
-       return result;
-}
-
-/**
- * Load the given config dir and read their file content into the config structure.
- *
- * @param dir Path to the config directory.
- * @returns ConfigInformation structure.
- */
-ConfigDirInformation ApiListener::LoadConfigDir(const String& dir)
-{
-       ConfigDirInformation config;
-       config.UpdateV1 = new Dictionary();
-       config.UpdateV2 = new Dictionary();
-       Utility::GlobRecursive(dir, "*", std::bind(&ApiListener::ConfigGlobHandler, std::ref(config), dir, _1), GlobFile);
-       return config;
-}
-
-/**
- * Diffs the old current configuration with the new configuration
- * and copies the collected content. Detects whether a change
- * happened, this is used for later restarts.
- *
- * This generic function is called in two situations:
- * - Local zones.d to var/lib/api/zones copy on the master (authoritative: true)
- * - Received config update on a cluster node (authoritative: false)
+ * Entrypoint for updating all authoritative configs into var/lib/icinga2/api/zones
  *
- * @param oldConfigInfo Config information struct for the current old deployed config.
- * @param newConfigInfo Config information struct for the received synced config.
- * @param configDir Destination for copying new files (production, or stage dir).
- * @param zoneName Currently processed zone, for storing the relative paths for later.
- * @param relativePaths Reference which stores all updated config path destinations.
- * @param Whether we're authoritative for this config.
- * @returns Whether a config change happened.
  */
-bool ApiListener::UpdateConfigDir(const ConfigDirInformation& oldConfigInfo, const ConfigDirInformation& newConfigInfo,
-       const String& configDir, const String& zoneName, std::vector<String>& relativePaths, bool authoritative)
+void ApiListener::SyncLocalZoneDirs() const
 {
-       bool configChange = false;
-
-       Dictionary::Ptr oldConfig = MergeConfigUpdate(oldConfigInfo);
-       Dictionary::Ptr newConfig = MergeConfigUpdate(newConfigInfo);
-
-       double oldTimestamp;
-
-       if (!oldConfig->Contains("/.timestamp"))
-               oldTimestamp = 0;
-       else
-               oldTimestamp = oldConfig->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 (oldTimestamp >= newTimestamp) {
-               Log(LogNotice, "ApiListener")
-                       << "Our configuration is more recent than the received configuration update."
-                       << " Ignoring configuration file update for path '" << configDir << "'. Current timestamp '"
-                       << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", oldTimestamp) << "' ("
-                       << std::fixed << std::setprecision(6) << oldTimestamp
-                       << ") >= received timestamp '"
-                       << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", newTimestamp) << "' ("
-                       << newTimestamp << ").";
-               return false;
-       }
-
-       size_t numBytes = 0;
-
-       {
-               ObjectLock olock(newConfig);
-               for (const Dictionary::Pair& kv : newConfig) {
-                       if (oldConfig->Get(kv.first) != kv.second) {
-                               if (!Utility::Match("*/.timestamp", kv.first))
-                                       configChange = true;
-
-                               /* Store the relative config file path for later. */
-                               relativePaths.push_back(zoneName + "/" + kv.first);
-
-                               String path = configDir + "/" + kv.first;
-                               Log(LogInformation, "ApiListener")
-                                       << "Updating configuration file: " << path;
-
-                               /* Sync string content only. */
-                               String content = kv.second;
-
-                               /* Generate a directory tree (zones/1/2/3 might not exist yet). */
-                               Utility::MkDirP(Utility::DirName(path), 0755);
-                               std::ofstream fp(path.CStr(), std::ofstream::out | std::ostream::binary | std::ostream::trunc);
-                               fp << content;
-                               fp.close();
-
-                               numBytes += content.GetLength();
-                       }
-               }
-       }
-
-       /* Log something whether we're authoritative or receing a staged config. */
-       Log(LogInformation, "ApiListener")
-               << "Applying configuration file update for " << (authoritative ? "" : "stage ")
-               << "path '" << configDir << "' (" << numBytes << " Bytes). Received timestamp '"
-               << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", newTimestamp) << "' ("
-               << std::fixed << std::setprecision(6) << newTimestamp
-               << "), Current timestamp '"
-               << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", oldTimestamp) << "' ("
-               << oldTimestamp << ").";
-
-       /* If the update removes a path, delete it on disk. */
-       ObjectLock xlock(oldConfig);
-       for (const Dictionary::Pair& kv : oldConfig) {
-               if (!newConfig->Contains(kv.first)) {
-                       configChange = true;
-
-                       String path = configDir + "/" + kv.first;
-                       (void) unlink(path.CStr());
-               }
-       }
-
-       /* Consider that one of the paths leaves an empty directory here. Such is not copied from stage to prod and purged then automtically. */
-
-       String tsPath = configDir + "/.timestamp";
-       if (!Utility::PathExists(tsPath)) {
-               std::ofstream fp(tsPath.CStr(), std::ofstream::out | std::ostream::trunc);
-               fp << std::fixed << newTimestamp;
-               fp.close();
-       }
-
-       if (authoritative) {
-               String authPath = configDir + "/.authoritative";
-               if (!Utility::PathExists(authPath)) {
-                       std::ofstream fp(authPath.CStr(), std::ofstream::out | std::ostream::trunc);
-                       fp.close();
+       for (const Zone::Ptr& zone : ConfigType::GetObjectsByType<Zone>()) {
+               try {
+                       SyncLocalZoneDir(zone);
+               } catch (const std::exception&) {
+                       continue;
                }
        }
-
-       return configChange;
 }
 
 /**
@@ -221,7 +45,7 @@ bool ApiListener::UpdateConfigDir(const ConfigDirInformation& oldConfigInfo, con
  *
  * @param zone Pointer to the zone object being synced.
  */
-void ApiListener::SyncZoneDir(const Zone::Ptr& zone) const
+void ApiListener::SyncLocalZoneDir(const Zone::Ptr& zone) const
 {
        if (!zone)
                return;
@@ -229,23 +53,31 @@ void ApiListener::SyncZoneDir(const Zone::Ptr& zone) const
        ConfigDirInformation newConfigInfo;
        newConfigInfo.UpdateV1 = new Dictionary();
        newConfigInfo.UpdateV2 = new Dictionary();
+       newConfigInfo.Checksums = new Dictionary();
 
        String zoneName = zone->GetName();
 
+       /* Load registered zone paths, e.g. '_etc', '_api' and user packages. */
        for (const ZoneFragment& zf : ConfigCompiler::GetZoneDirs(zoneName)) {
                ConfigDirInformation newConfigPart = LoadConfigDir(zf.Path);
 
+               /* Config files '*.conf'. */
                {
                        ObjectLock olock(newConfigPart.UpdateV1);
                        for (const Dictionary::Pair& kv : newConfigPart.UpdateV1) {
-                               newConfigInfo.UpdateV1->Set("/" + zf.Tag + kv.first, kv.second);
+                               String path = "/" + zf.Tag + kv.first;
+                               newConfigInfo.UpdateV1->Set(path, kv.second);
+                               newConfigInfo.Checksums->Set(path, GetChecksum(kv.second));
                        }
                }
 
+               /* Meta files. */
                {
                        ObjectLock olock(newConfigPart.UpdateV2);
                        for (const Dictionary::Pair& kv : newConfigPart.UpdateV2) {
-                               newConfigInfo.UpdateV2->Set("/" + zf.Tag + kv.first, kv.second);
+                               String path = "/" + zf.Tag + kv.first;
+                               newConfigInfo.UpdateV2->Set(path, kv.second);
+                               newConfigInfo.Checksums->Set(path, GetChecksum(kv.second));
                        }
                }
        }
@@ -255,34 +87,68 @@ void ApiListener::SyncZoneDir(const Zone::Ptr& zone) const
        if (sumUpdates == 0)
                return;
 
-       String currentDir = Configuration::DataDir + "/api/zones/" + zoneName;
+       String productionZonesDir = GetApiZonesDir() + zoneName;
 
        Log(LogInformation, "ApiListener")
-               << "Copying " << sumUpdates << " zone configuration files for zone '" << zoneName << "' to '" << currentDir << "'.";
-
-       ConfigDirInformation oldConfigInfo = LoadConfigDir(currentDir);
+               << "Copying " << sumUpdates << " zone configuration files for zone '" << zoneName << "' to '" << productionZonesDir << "'.";
 
        /* Purge files to allow deletion via zones.d. */
-       Utility::RemoveDirRecursive(currentDir);
-       Utility::MkDirP(currentDir, 0700);
+       if (Utility::PathExists(productionZonesDir))
+               Utility::RemoveDirRecursive(productionZonesDir);
 
-       std::vector<String> relativePaths;
-       UpdateConfigDir(oldConfigInfo, newConfigInfo, currentDir, zoneName, relativePaths, true);
-}
+       Utility::MkDirP(productionZonesDir, 0700);
 
-/**
- * Entrypoint for updating all authoritative configs into var/lib/icinga2/api/zones
- *
- */
-void ApiListener::SyncZoneDirs() const
-{
-       for (const Zone::Ptr& zone : ConfigType::GetObjectsByType<Zone>()) {
-               try {
-                       SyncZoneDir(zone);
-               } catch (const std::exception&) {
-                       continue;
+       /* Copy content and add additional meta data. */
+       size_t numBytes = 0;
+
+       /* Note: We cannot simply copy directories here.
+        *
+        * Zone directories are registered from everywhere and we already
+        * have read their content into memory with LoadConfigDir().
+        */
+       Dictionary::Ptr newConfig = MergeConfigUpdate(newConfigInfo);
+
+       {
+               ObjectLock olock(newConfig);
+               for (const Dictionary::Pair& kv : newConfig) {
+                       String dst = productionZonesDir + "/" + kv.first;
+                       Utility::MkDirP(Utility::DirName(dst), 0755);
+
+                       Log(LogInformation, "ApiListener")
+                               << "Updating configuration file: " << dst;
+
+                       String content = kv.second;
+                       std::ofstream fp(dst.CStr(), std::ofstream::out | std::ostream::binary | std::ostream::trunc);
+                       fp << content;
+                       fp.close();
+
+                       numBytes += content.GetLength();
                }
        }
+
+       /* Additional metadata. */
+       String tsPath = productionZonesDir + "/.timestamp";
+
+       if (!Utility::PathExists(tsPath)) {
+               std::ofstream fp(tsPath.CStr(), std::ofstream::out | std::ostream::trunc);
+               fp << std::fixed << Utility::GetTime();
+               fp.close();
+       }
+
+       String authPath = productionZonesDir + "/.authoritative";
+
+       if (!Utility::PathExists(authPath)) {
+               std::ofstream fp(authPath.CStr(), std::ofstream::out | std::ostream::trunc);
+       }
+
+       String checksumsPath = productionZonesDir + "/.checksums";
+
+       if (Utility::PathExists(checksumsPath))
+               (void) unlink(checksumsPath.CStr());
+
+       std::ofstream fp(checksumsPath.CStr(), std::ofstream::out | std::ostream::trunc);
+       fp << std::fixed << JsonEncode(newConfigInfo.Checksums);
+       fp.close();
 }
 
 /**
@@ -307,11 +173,13 @@ void ApiListener::SendConfigUpdate(const JsonRpcConnection::Ptr& aclient)
 
        Dictionary::Ptr configUpdateV1 = new Dictionary();
        Dictionary::Ptr configUpdateV2 = new Dictionary();
+       Dictionary::Ptr configUpdateChecksums = new Dictionary();
 
-       String zonesDir = Configuration::DataDir + "/api/zones";
+       String zonesDir = GetApiZonesDir();
 
        for (const Zone::Ptr& zone : ConfigType::GetObjectsByType<Zone>()) {
-               String zoneDir = zonesDir + "/" + zone->GetName();
+               String zoneName = zone->GetName();
+               String zoneDir = zonesDir + zoneName;
 
                if (!zone->IsChildOf(azone) && !zone->IsGlobal())
                        continue;
@@ -321,11 +189,13 @@ void ApiListener::SendConfigUpdate(const JsonRpcConnection::Ptr& aclient)
 
                Log(LogInformation, "ApiListener")
                        << "Syncing configuration files for " << (zone->IsGlobal() ? "global " : "")
-                       << "zone '" << zone->GetName() << "' to endpoint '" << endpoint->GetName() << "'.";
+                       << "zone '" << zoneName << "' to endpoint '" << endpoint->GetName() << "'.";
+
+               ConfigDirInformation config = LoadConfigDir(zoneDir);
 
-               ConfigDirInformation config = LoadConfigDir(zonesDir + "/" + zone->GetName());
-               configUpdateV1->Set(zone->GetName(), config.UpdateV1);
-               configUpdateV2->Set(zone->GetName(), config.UpdateV2);
+               configUpdateV1->Set(zoneName, config.UpdateV1);
+               configUpdateV2->Set(zoneName, config.UpdateV2);
+               configUpdateChecksums->Set(zoneName, config.Checksums);
        }
 
        Dictionary::Ptr message = new Dictionary({
@@ -333,7 +203,8 @@ void ApiListener::SendConfigUpdate(const JsonRpcConnection::Ptr& aclient)
                { "method", "config::Update" },
                { "params", new Dictionary({
                        { "update", configUpdateV1 },
-                       { "update_v2", configUpdateV2 }
+                       { "update_v2", configUpdateV2 },        /* Since 2.4.2. */
+                       { "checksums", configUpdateChecksums }  /* Since 2.11.0. */
                }) }
        });
 
@@ -457,6 +328,129 @@ Value ApiListener::ConfigUpdateHandler(const MessageOrigin::Ptr& origin, const D
        return Empty;
 }
 
+/**
+ * Diffs the old current configuration with the new configuration
+ * and copies the collected content. Detects whether a change
+ * happened, this is used for later restarts.
+ *
+ * This generic function is called in two situations:
+ * - Local zones.d to var/lib/api/zones copy on the master (authoritative: true)
+ * - Received config update on a cluster node (authoritative: false)
+ *
+ * @param oldConfigInfo Config information struct for the current old deployed config.
+ * @param newConfigInfo Config information struct for the received synced config.
+ * @param configDir Destination for copying new files (production, or stage dir).
+ * @param zoneName Currently processed zone, for storing the relative paths for later.
+ * @param relativePaths Reference which stores all updated config path destinations.
+ * @param Whether we're authoritative for this config.
+ * @returns Whether a config change happened.
+ */
+bool ApiListener::UpdateConfigDir(const ConfigDirInformation& oldConfigInfo, const ConfigDirInformation& newConfigInfo,
+       const String& configDir, const String& zoneName, std::vector<String>& relativePaths, bool authoritative)
+{
+       bool configChange = false;
+
+       Dictionary::Ptr oldConfig = MergeConfigUpdate(oldConfigInfo);
+       Dictionary::Ptr newConfig = MergeConfigUpdate(newConfigInfo);
+
+       double oldTimestamp;
+
+       if (!oldConfig->Contains("/.timestamp"))
+               oldTimestamp = 0;
+       else
+               oldTimestamp = oldConfig->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 (oldTimestamp >= newTimestamp) {
+               Log(LogNotice, "ApiListener")
+                       << "Our configuration is more recent than the received configuration update."
+                       << " Ignoring configuration file update for path '" << configDir << "'. Current timestamp '"
+                       << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", oldTimestamp) << "' ("
+                       << std::fixed << std::setprecision(6) << oldTimestamp
+                       << ") >= received timestamp '"
+                       << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", newTimestamp) << "' ("
+                       << newTimestamp << ").";
+               return false;
+       }
+
+       size_t numBytes = 0;
+
+       {
+               ObjectLock olock(newConfig);
+               for (const Dictionary::Pair& kv : newConfig) {
+                       if (oldConfig->Get(kv.first) != kv.second) {
+                               if (!Utility::Match("*/.timestamp", kv.first))
+                                       configChange = true;
+
+                               /* Store the relative config file path for later. */
+                               relativePaths.push_back(zoneName + "/" + kv.first);
+
+                               String path = configDir + "/" + kv.first;
+                               Log(LogInformation, "ApiListener")
+                                       << "Updating configuration file: " << path;
+
+                               /* Sync string content only. */
+                               String content = kv.second;
+
+                               /* Generate a directory tree (zones/1/2/3 might not exist yet). */
+                               Utility::MkDirP(Utility::DirName(path), 0755);
+                               std::ofstream fp(path.CStr(), std::ofstream::out | std::ostream::binary | std::ostream::trunc);
+                               fp << content;
+                               fp.close();
+
+                               numBytes += content.GetLength();
+                       }
+               }
+       }
+
+       /* Log something whether we're authoritative or receing a staged config. */
+       Log(LogInformation, "ApiListener")
+               << "Applying configuration file update for " << (authoritative ? "" : "stage ")
+               << "path '" << configDir << "' (" << numBytes << " Bytes). Received timestamp '"
+               << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", newTimestamp) << "' ("
+               << std::fixed << std::setprecision(6) << newTimestamp
+               << "), Current timestamp '"
+               << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", oldTimestamp) << "' ("
+               << oldTimestamp << ").";
+
+       /* If the update removes a path, delete it on disk. */
+       ObjectLock xlock(oldConfig);
+       for (const Dictionary::Pair& kv : oldConfig) {
+               if (!newConfig->Contains(kv.first)) {
+                       configChange = true;
+
+                       String path = configDir + "/" + kv.first;
+                       (void) unlink(path.CStr());
+               }
+       }
+
+       /* Consider that one of the paths leaves an empty directory here. Such is not copied from stage to prod and purged then automtically. */
+
+       String tsPath = configDir + "/.timestamp";
+       if (!Utility::PathExists(tsPath)) {
+               std::ofstream fp(tsPath.CStr(), std::ofstream::out | std::ostream::trunc);
+               fp << std::fixed << newTimestamp;
+               fp.close();
+       }
+
+       if (authoritative) {
+               String authPath = configDir + "/.authoritative";
+               if (!Utility::PathExists(authPath)) {
+                       std::ofstream fp(authPath.CStr(), std::ofstream::out | std::ostream::trunc);
+                       fp.close();
+               }
+       }
+
+       return configChange;
+}
+
 /**
  * Callback for stage config validation.
  * When validation was successful, the configuration is copied from
@@ -586,3 +580,90 @@ void ApiListener::ClearLastFailedZonesStageValidation()
 {
        SetLastFailedZonesStageValidation(Dictionary::Ptr());
 }
+
+/**
+ * Generate a config checksum.
+ *
+ * @param
+ */
+String ApiListener::GetChecksum(const String& content)
+{
+       return SHA256(content);
+}
+
+/**
+ * Load the given config dir and read their file content into the config structure.
+ *
+ * @param dir Path to the config directory.
+ * @returns ConfigInformation structure.
+ */
+ConfigDirInformation ApiListener::LoadConfigDir(const String& dir)
+{
+       ConfigDirInformation config;
+       config.UpdateV1 = new Dictionary();
+       config.UpdateV2 = new Dictionary();
+       config.Checksums = new Dictionary();
+
+       Utility::GlobRecursive(dir, "*", std::bind(&ApiListener::ConfigGlobHandler, std::ref(config), dir, _1), GlobFile);
+       return config;
+}
+
+/**
+ * Read the given file and store it in the config information structure.
+ * Callback function for Glob().
+ *
+ * @param config Reference to the config information object.
+ * @param path File path.
+ * @param file Full file name.
+ */
+void ApiListener::ConfigGlobHandler(ConfigDirInformation& config, const String& path, const String& file)
+{
+       CONTEXT("Creating config update for file '" + file + "'");
+
+       Log(LogNotice, "ApiListener")
+               << "Creating config update for file '" << file << "'.";
+
+       std::ifstream fp(file.CStr(), std::ifstream::binary);
+       if (!fp)
+               return;
+
+       String content((std::istreambuf_iterator<char>(fp)), std::istreambuf_iterator<char>());
+
+       Dictionary::Ptr update;
+       String relativePath = file.SubStr(path.GetLength());
+
+       /*
+        * 'update' messages contain conf files. 'update_v2' syncs everything else (.timestamp).
+        *
+        * **Keep this intact to stay compatible with older clients.**
+        */
+       if (Utility::Match("*.conf", file))
+               update = config.UpdateV1;
+       else
+               update = config.UpdateV2;
+
+       update->Set(relativePath, content);
+
+       /* Calculate a checksum for each file (and a global one later). */
+       config.Checksums->Set(relativePath, GetChecksum(content));
+}
+
+/**
+ * Compatibility helper for merging config update v1 and v2 into a global result.
+ *
+ * @param config Config information structure.
+ * @returns Dictionary which holds the merged information.
+ */
+Dictionary::Ptr ApiListener::MergeConfigUpdate(const ConfigDirInformation& config)
+{
+       Dictionary::Ptr result = new Dictionary();
+
+       if (config.UpdateV1)
+               config.UpdateV1->CopyTo(result);
+
+       if (config.UpdateV2)
+               config.UpdateV2->CopyTo(result);
+
+       return result;
+}
+
index b9f8b9780cf059e43eeab39f11d2883b04fda0b6..661a4eaaf6f2aac8add181b57e1c6698eb59165d 100644 (file)
@@ -242,7 +242,7 @@ void ApiListener::Start(bool runtimeCreated)
        Log(LogInformation, "ApiListener")
                << "'" << GetName() << "' started.";
 
-       SyncZoneDirs();
+       SyncLocalZoneDirs();
 
        ObjectImpl<ApiListener>::Start(runtimeCreated);
 
index 3d1082444b5847f78fbf671952893dbcd755bddc..f9d92848fdacbb3bf6246e025e244c70d498afcd 100644 (file)
@@ -32,6 +32,7 @@ struct ConfigDirInformation
 {
        Dictionary::Ptr UpdateV1;
        Dictionary::Ptr UpdateV2;
+       Dictionary::Ptr Checksums;
 };
 
 /**
@@ -170,21 +171,24 @@ private:
        /* filesync */
        static boost::mutex m_ConfigSyncStageLock;
 
-       static ConfigDirInformation LoadConfigDir(const String& dir);
+       void SyncLocalZoneDirs() const;
+       void SyncLocalZoneDir(const Zone::Ptr& zone) const;
+
+       void SendConfigUpdate(const JsonRpcConnection::Ptr& aclient);
+
        static Dictionary::Ptr MergeConfigUpdate(const ConfigDirInformation& config);
        static bool UpdateConfigDir(const ConfigDirInformation& oldConfigInfo, const ConfigDirInformation& newConfigInfo,
                const String& configDir, const String& zoneName, std::vector<String>& relativePaths, bool authoritative);
 
-       void SyncZoneDirs() const;
-       void SyncZoneDir(const Zone::Ptr& zone) const;
-
+       static ConfigDirInformation LoadConfigDir(const String& dir);
        static void ConfigGlobHandler(ConfigDirInformation& config, const String& path, const String& file);
-       void SendConfigUpdate(const JsonRpcConnection::Ptr& aclient);
 
        static void TryActivateZonesStageCallback(const ProcessResult& pr,
                const std::vector<String>& relativePaths);
        static void AsyncTryActivateZonesStage(const std::vector<String>& relativePaths);
 
+       static String GetChecksum(const String& content);
+
        void UpdateLastFailedZonesStageValidation(const String& log);
        void ClearLastFailedZonesStageValidation();