2
2
3
3
#include " remote/configstageshandler.hpp"
4
4
#include " remote/configpackageutility.hpp"
5
+ #include " remote/configobjectslock.hpp"
5
6
#include " remote/httputility.hpp"
6
7
#include " remote/filterutility.hpp"
7
8
#include " base/application.hpp"
@@ -12,7 +13,10 @@ using namespace icinga;
12
13
13
14
REGISTER_URLHANDLER (" /v1/config/stages" , ConfigStagesHandler);
14
15
15
- std::atomic<bool > ConfigStagesHandler::m_RunningPackageUpdates (false );
16
+ static bool l_RunningPackageUpdates (false );
17
+ // A timestamp that indicates the last time an Icinga 2 reload failed.
18
+ static double l_LastReloadFailedTime (0 );
19
+ static std::mutex l_RunningPackageUpdatesMutex; // Protects the above two variables.
16
20
17
21
bool ConfigStagesHandler::HandleRequest (
18
22
const WaitGroup::Ptr&,
@@ -132,12 +136,42 @@ void ConfigStagesHandler::HandlePost(
132
136
if (reload && !activate)
133
137
BOOST_THROW_EXCEPTION (std::invalid_argument (" Parameter 'reload' must be false when 'activate' is false." ));
134
138
135
- if (m_RunningPackageUpdates.exchange (true )) {
136
- return HttpUtility::SendJsonError (response, params, 423 ,
137
- " Conflicting request, there is already an ongoing package update in progress. Please try it again later." );
139
+ ConfigObjectsSharedLock configObjectsSharedLock (std::try_to_lock);
140
+ if (!configObjectsSharedLock) {
141
+ HttpUtility::SendJsonError (response, params, 503 , " Icinga is reloading" );
142
+ return ;
138
143
}
139
144
140
- auto resetPackageUpdates (Shared<Defer>::Make ([]() { ConfigStagesHandler::m_RunningPackageUpdates.store (false ); }));
145
+ {
146
+ std::lock_guard runningPackageUpdatesLock (l_RunningPackageUpdatesMutex);
147
+ double currentReloadFailedTime = Application::GetLastReloadFailed ();
148
+
149
+ /* *
150
+ * Once the m_RunningPackageUpdates flag is set, it typically remains set until the current worker process is
151
+ * terminated, in which case the new worker will have its own m_RunningPackageUpdates flag set to false.
152
+ * However, if the reload fails for any reason, the m_RunningPackageUpdates flag will remain set to true
153
+ * in the current worker process, which will prevent any further package updates from being processed until
154
+ * the next Icinga 2 restart.
155
+ *
156
+ * So, in order to prevent such a situation, we are additionally tracking the last time a reload failed
157
+ * and allow to bypass the m_RunningPackageUpdates flag only if the last reload failed time was changed
158
+ * since the previous request.
159
+ */
160
+ if (l_RunningPackageUpdates && l_LastReloadFailedTime == currentReloadFailedTime) {
161
+ return HttpUtility::SendJsonError (
162
+ response, params, 423 ,
163
+ " Conflicting request, there is already an ongoing package update in progress. Please try it again later."
164
+ );
165
+ }
166
+
167
+ l_RunningPackageUpdates = true ;
168
+ l_LastReloadFailedTime = currentReloadFailedTime;
169
+ }
170
+
171
+ auto resetPackageUpdates (Shared<Defer>::Make ([]() {
172
+ std::lock_guard lock (l_RunningPackageUpdatesMutex);
173
+ l_RunningPackageUpdates = false ;
174
+ }));
141
175
142
176
std::unique_lock<std::mutex> lock (ConfigPackageUtility::GetStaticPackageMutex ());
143
177
@@ -201,6 +235,12 @@ void ConfigStagesHandler::HandleDelete(
201
235
if (!ConfigPackageUtility::ValidateStageName (stageName))
202
236
return HttpUtility::SendJsonError (response, params, 400 , " Invalid stage name '" + stageName + " '." );
203
237
238
+ ConfigObjectsSharedLock lock (std::try_to_lock);
239
+ if (!lock) {
240
+ HttpUtility::SendJsonError (response, params, 503 , " Icinga is reloading" );
241
+ return ;
242
+ }
243
+
204
244
try {
205
245
ConfigPackageUtility::DeleteStage (packageName, stageName);
206
246
} catch (const std::exception& ex) {
0 commit comments