Merge pull request #5620 from Icinga/fix/api-staging-3668

WIP: Ensure that the REST API config package/stage creation is atomic
This commit is contained in:
Gunnar Beutner 2017-10-24 12:51:09 +02:00 committed by GitHub
commit 2bbb5366fd
5 changed files with 29 additions and 12 deletions

View File

@ -102,11 +102,14 @@ 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)
{
if (!ConfigPackageUtility::PackageExists("_api")) {
ConfigPackageUtility::CreatePackage("_api");
{
boost::mutex::scoped_lock lock(ConfigPackageUtility::GetStaticMutex());
if (!ConfigPackageUtility::PackageExists("_api")) {
ConfigPackageUtility::CreatePackage("_api");
String stage = ConfigPackageUtility::CreateStage("_api");
ConfigPackageUtility::ActivateStage("_api", stage);
String stage = ConfigPackageUtility::CreateStage("_api");
ConfigPackageUtility::ActivateStage("_api", stage);
}
}
String path = GetObjectConfigPath(type, fullName);
@ -240,4 +243,3 @@ bool ConfigObjectUtility::DeleteObject(const ConfigObject::Ptr& object, bool cas
return DeleteObjectHelper(object, cascade, errors);
}

View File

@ -53,12 +53,15 @@ void ConfigPackagesHandler::HandleGet(const ApiUser::Ptr& user, HttpRequest& req
Array::Ptr results = new Array();
for (const String& package : packages) {
Dictionary::Ptr packageInfo = new Dictionary();
packageInfo->Set("name", package);
packageInfo->Set("stages", Array::FromVector(ConfigPackageUtility::GetStages(package)));
packageInfo->Set("active-stage", ConfigPackageUtility::GetActiveStage(package));
results->Add(packageInfo);
{
boost::mutex::scoped_lock lock(ConfigPackageUtility::GetStaticMutex());
for (const String& package : packages) {
Dictionary::Ptr packageInfo = new Dictionary();
packageInfo->Set("name", package);
packageInfo->Set("stages", Array::FromVector(ConfigPackageUtility::GetStages(package)));
packageInfo->Set("active-stage", ConfigPackageUtility::GetActiveStage(package));
results->Add(packageInfo);
}
}
Dictionary::Ptr result = new Dictionary();
@ -85,6 +88,7 @@ void ConfigPackagesHandler::HandlePost(const ApiUser::Ptr& user, HttpRequest& re
Dictionary::Ptr result1 = new Dictionary();
try {
boost::mutex::scoped_lock lock(ConfigPackageUtility::GetStaticMutex());
ConfigPackageUtility::CreatePackage(packageName);
} catch (const std::exception& ex) {
HttpUtility::SendJsonError(response, 500, "Could not create package.",

View File

@ -190,7 +190,10 @@ void ConfigPackageUtility::TryActivateStageCallback(const ProcessResult& pr, con
/* validation went fine, activate stage and reload */
if (pr.ExitStatus == 0) {
ActivateStage(packageName, stageName);
{
boost::mutex::scoped_lock lock(GetStaticMutex());
ActivateStage(packageName, stageName);
}
if (reload)
Application::RequestRestart();
@ -317,3 +320,8 @@ bool ConfigPackageUtility::ValidateName(const String& name)
return (!boost::regex_search(name.GetData(), what, expr));
}
boost::mutex& ConfigPackageUtility::GetStaticMutex(void)
{
static boost::mutex mutex;
return mutex;
}

View File

@ -58,6 +58,8 @@ public:
static bool ContainsDotDot(const String& path);
static bool ValidateName(const String& name);
static boost::mutex& GetStaticMutex(void);
private:
static void CollectDirNames(const String& path, std::vector<String>& dirs);
static void CollectPaths(const String& path, std::vector<std::pair<String, bool> >& paths);

View File

@ -110,6 +110,7 @@ void ConfigStagesHandler::HandlePost(const ApiUser::Ptr& user, HttpRequest& requ
if (!files)
BOOST_THROW_EXCEPTION(std::invalid_argument("Parameter 'files' must be specified."));
boost::mutex::scoped_lock lock(ConfigPackageUtility::GetStaticMutex());
stageName = ConfigPackageUtility::CreateStage(packageName, files);
/* validate the config. on success, activate stage and reload */