Skip to content

Commit

Permalink
API Package: Repair active stage from local directory; avoid exceptions
Browse files Browse the repository at this point in the history
This partially reverts #7150 and adds new creation/repair methods.
  • Loading branch information
Michael Friedrich committed May 9, 2019
1 parent 98039e8 commit 853030b
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 25 deletions.
3 changes: 3 additions & 0 deletions lib/cli/daemoncommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,9 @@ int DaemonCommand::Run(const po::variables_map& vm, const std::vector<std::strin
Logger::DisableConsoleLog();
}

/* Create the internal API object storage. Do this here too with setups without API. */
ConfigObjectUtility::CreateStorage();

/* Remove ignored Downtime/Comment objects. */
try {
String configDir = ConfigObjectUtility::GetConfigDir();
Expand Down
4 changes: 4 additions & 0 deletions lib/remote/apilistener.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "remote/jsonrpc.hpp"
#include "remote/apifunction.hpp"
#include "remote/configpackageutility.hpp"
#include "remote/configobjectutility.hpp"
#include "base/convert.hpp"
#include "base/defer.hpp"
#include "base/io-engine.hpp"
Expand Down Expand Up @@ -135,6 +136,9 @@ void ApiListener::OnConfigLoaded()
Log(LogWarning, "ApiListener", "Please read the upgrading documentation for v2.8: https://icinga.com/docs/icinga2/latest/doc/16-upgrading-icinga-2/");
}

/* Create the internal API object storage. */
ConfigObjectUtility::CreateStorage();

/* Cache API packages and their active stage name. */
UpdateActivePackageStagesCache();

Expand Down
74 changes: 61 additions & 13 deletions lib/remote/configobjectutility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,21 @@
#include "base/dependencygraph.hpp"
#include "base/utility.hpp"
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/filesystem.hpp>
#include <boost/system/error_code.hpp>
#include <fstream>

using namespace icinga;

String ConfigObjectUtility::GetConfigDir()
{
/* This may throw an exception the caller above must handle. */
return ConfigPackageUtility::GetPackageDir() + "/_api/" +
ConfigPackageUtility::GetActiveStage("_api");
String prefix = ConfigPackageUtility::GetPackageDir() + "/_api/";
String activeStage = ConfigPackageUtility::GetActiveStage("_api");

if (activeStage.IsEmpty())
RepairPackage("_api");

return prefix + activeStage;
}

String ConfigObjectUtility::GetObjectConfigPath(const Type::Ptr& type, const String& fullName)
Expand All @@ -33,6 +39,57 @@ String ConfigObjectUtility::GetObjectConfigPath(const Type::Ptr& type, const Str
"/" + EscapeName(fullName) + ".conf";
}

void ConfigObjectUtility::RepairPackage(const String& package)
{
String activeStage = ConfigPackageUtility::GetActiveStage("_api");

/* Try to fix the active stage, whenever we find a directory in there. */
if (activeStage.IsEmpty()) {
namespace fs = boost::filesystem;

fs::path path(ConfigPackageUtility::GetPackageDir() + "/_api/");

fs::recursive_directory_iterator end;

String foundActiveStage;

for (fs::recursive_directory_iterator it(path); it != end; it++) {
boost::system::error_code ec;

const fs::path d = *it;
if (fs::is_directory(d, ec)) {
foundActiveStage = d.string();

break; //Use the first found directory.
}
}

if (!foundActiveStage.IsEmpty()) {
ConfigPackageUtility::ActivateStage("_api", foundActiveStage);
} else {
Log(LogCritical, "ConfigObjectUtility")
<< "Cannot repair internal _api package, no directories.";
/* TODO: Should be rmdir _api and re-create it? */
}

}
}

void ConfigObjectUtility::CreateStorage()
{
boost::mutex::scoped_lock lock(ConfigPackageUtility::GetStaticPackageMutex());

if (!ConfigPackageUtility::PackageExists("_api")) {
Log(LogWarning, "ConfigObjectUtility")
<< "Package _api doesn't exist yet, creating it.";

ConfigPackageUtility::CreatePackage("_api");

String stage = ConfigPackageUtility::CreateStage("_api");
ConfigPackageUtility::ActivateStage("_api", stage);
}
}

String ConfigObjectUtility::EscapeName(const String& name)
{
return Utility::EscapeString(name, "<>:\"/\\|?*", true);
Expand Down Expand Up @@ -88,16 +145,7 @@ String ConfigObjectUtility::CreateObjectConfig(const Type::Ptr& type, const Stri
bool ConfigObjectUtility::CreateObject(const Type::Ptr& type, const String& fullName,
const String& config, const Array::Ptr& errors, const Array::Ptr& diagnosticInformation)
{
{
boost::mutex::scoped_lock lock(ConfigPackageUtility::GetStaticPackageMutex());

if (!ConfigPackageUtility::PackageExists("_api")) {
ConfigPackageUtility::CreatePackage("_api");

String stage = ConfigPackageUtility::CreateStage("_api");
ConfigPackageUtility::ActivateStage("_api", stage);
}
}
CreateStorage();

ConfigItem::Ptr item = ConfigItem::GetByTypeAndName(type, fullName);

Expand Down
2 changes: 2 additions & 0 deletions lib/remote/configobjectutility.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ class ConfigObjectUtility
public:
static String GetConfigDir();
static String GetObjectConfigPath(const Type::Ptr& type, const String& fullName);
static void RepairPackage(const String& package);
static void CreateStorage();

static String CreateObjectConfig(const Type::Ptr& type, const String& fullName,
bool ignoreOnError, const Array::Ptr& templates, const Dictionary::Ptr& attrs);
Expand Down
25 changes: 13 additions & 12 deletions lib/remote/configpackageutility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ String ConfigPackageUtility::GetActiveStageFromFile(const String& packageName)
fp.close();

if (fp.fail())
BOOST_THROW_EXCEPTION(std::invalid_argument("Cannot detect active stage for package '" + packageName + "'. Broken config package, check the troubleshooting documentation."));
return ""; /* Don't use exceptions here. The caller must deal with empty stages at this point. Happens on initial package creation for example. */

return stage.Trim();
}
Expand All @@ -283,13 +283,16 @@ void ConfigPackageUtility::SetActiveStageToFile(const String& packageName, const

String ConfigPackageUtility::GetActiveStage(const String& packageName)
{
String activeStage;

ApiListener::Ptr listener = ApiListener::GetInstance();

/* config packages without API make no sense. */
/* If we don't have an API feature, just use the file storage without caching this.
* This happens when ScheduledDowntime objects generate Downtime objects.
* TODO: Make the API a first class citizen.
*/
if (!listener)
BOOST_THROW_EXCEPTION(std::invalid_argument("No ApiListener instance configured."));

String activeStage;
return GetActiveStageFromFile(packageName);

/* First use runtime state. */
try {
Expand All @@ -301,25 +304,23 @@ String ConfigPackageUtility::GetActiveStage(const String& packageName)
/* When we've read something, correct memory. */
if (!activeStage.IsEmpty())
listener->SetActivePackageStage(packageName, activeStage);
else
BOOST_THROW_EXCEPTION(std::invalid_argument("Cannot detect active stage for package '" + packageName + "'. Broken config package, check the troubleshooting documentation."));
}

return activeStage;
}

void ConfigPackageUtility::SetActiveStage(const String& packageName, const String& stageName)
{
/* Update the marker on disk for restarts. */
SetActiveStageToFile(packageName, stageName);

ApiListener::Ptr listener = ApiListener::GetInstance();

/* config packages without API make no sense. */
/* No API, no caching. */
if (!listener)
BOOST_THROW_EXCEPTION(std::invalid_argument("No ApiListener instance configured."));
return;

listener->SetActivePackageStage(packageName, stageName);

/* Also update the marker on disk for restarts. */
SetActiveStageToFile(packageName, stageName);
}

std::vector<std::pair<String, bool> > ConfigPackageUtility::GetFiles(const String& packageName, const String& stageName)
Expand Down

0 comments on commit 853030b

Please sign in to comment.