Merge lp:~ted/ubuntu-app-launch/reset-units into lp:ubuntu-app-launch

Proposed by Ted Gould
Status: Merged
Approved by: dobey
Approved revision: 410
Merged at revision: 286
Proposed branch: lp:~ted/ubuntu-app-launch/reset-units
Merge into: lp:ubuntu-app-launch
Diff against target: 212 lines (+146/-0)
4 files modified
libubuntu-app-launch/jobs-systemd.cpp (+47/-0)
libubuntu-app-launch/jobs-systemd.h (+2/-0)
tests/jobs-systemd.cpp (+24/-0)
tests/systemd-mock.h (+73/-0)
To merge this branch: bzr merge lp:~ted/ubuntu-app-launch/reset-units
Reviewer Review Type Date Requested Status
dobey (community) Approve
unity-api-1-bot continuous-integration Approve
Review via email: mp+316351@code.launchpad.net

This proposal supersedes a proposal from 2017-02-02.

Commit message

Reset failed units so they can be tried again

To post a comment you must log in.
Revision history for this message
Ted Gould (ted) wrote :
Revision history for this message
unity-api-1-bot (unity-api-1-bot) wrote :

PASSED: Continuous integration, rev:410
https://jenkins.canonical.com/unity-api-1/job/lp-ubuntu-app-launch-ci/204/
Executed test runs:
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build/1602
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-0-fetch/1609
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/1387
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/1387/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=zesty/1387
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=zesty/1387/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/1387
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/1387/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=zesty/1387
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=zesty/1387/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/1387
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/1387/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=zesty/1387
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=zesty/1387/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/unity-api-1/job/lp-ubuntu-app-launch-ci/204/rebuild

review: Approve (continuous-integration)
Revision history for this message
dobey (dobey) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'libubuntu-app-launch/jobs-systemd.cpp'
--- libubuntu-app-launch/jobs-systemd.cpp 2017-02-02 15:09:24 +0000
+++ libubuntu-app-launch/jobs-systemd.cpp 2017-02-04 04:10:31 +0000
@@ -1342,6 +1342,9 @@
1342 }1342 }
1343 g_variant_dict_clear(&dict);1343 g_variant_dict_clear(&dict);
13441344
1345 /* Reset the failure bit on the unit */
1346 manager->resetUnit(unitinfo);
1347
1345 /* Oh, we might want to do something now */1348 /* Oh, we might want to do something now */
1346 auto reason{Registry::FailureType::CRASH};1349 auto reason{Registry::FailureType::CRASH};
1347 if (g_strcmp0(value, "exit-code") == 0)1350 if (g_strcmp0(value, "exit-code") == 0)
@@ -1368,6 +1371,50 @@
1368 return sig_appFailed;1371 return sig_appFailed;
1369}1372}
13701373
1374/** Requests that systemd reset a unit that has been marked as
1375 failed so that we can continue to work with it. This includes
1376 starting it anew, which can fail if it is left in the failed
1377 state. */
1378void SystemD::resetUnit(const UnitInfo& info) const
1379{
1380 auto registry = registry_.lock();
1381 auto unitname = unitName(info);
1382 auto bus = userbus_;
1383 auto cancel = registry->impl->thread.getCancellable();
1384
1385 registry->impl->thread.executeOnThread([bus, unitname, cancel] {
1386 g_dbus_connection_call(bus.get(), /* user bus */
1387 SYSTEMD_DBUS_ADDRESS, /* bus name */
1388 SYSTEMD_DBUS_PATH_MANAGER, /* path */
1389 SYSTEMD_DBUS_IFACE_MANAGER, /* interface */
1390 "ResetFailedUnit", /* method */
1391 g_variant_new("(s)", /* params */
1392 unitname.c_str()), /* param: specify unit */
1393 nullptr, /* ret type */
1394 G_DBUS_CALL_FLAGS_NONE, /* flags */
1395 -1, /* timeout */
1396 cancel.get(), /* cancellable */
1397 [](GObject* obj, GAsyncResult* res, gpointer user_data) {
1398 GError* error{nullptr};
1399 GVariant* callt = g_dbus_connection_call_finish(G_DBUS_CONNECTION(obj), res, &error);
1400
1401 if (error != nullptr)
1402 {
1403 if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1404 {
1405 g_warning("Unable to reset failed unit: %s", error->message);
1406 }
1407 g_error_free(error);
1408 return;
1409 }
1410
1411 g_clear_pointer(&callt, g_variant_unref);
1412 g_debug("Reset Failed Unit");
1413 },
1414 nullptr);
1415 });
1416}
1417
1371} // namespace manager1418} // namespace manager
1372} // namespace jobs1419} // namespace jobs
1373} // namespace app_launch1420} // namespace app_launch
13741421
=== modified file 'libubuntu-app-launch/jobs-systemd.h'
--- libubuntu-app-launch/jobs-systemd.h 2017-01-27 18:12:35 +0000
+++ libubuntu-app-launch/jobs-systemd.h 2017-02-04 04:10:31 +0000
@@ -128,6 +128,8 @@
128128
129 static std::vector<std::string> parseExec(std::list<std::pair<std::string, std::string>>& env);129 static std::vector<std::string> parseExec(std::list<std::pair<std::string, std::string>>& env);
130 static void application_start_cb(GObject* obj, GAsyncResult* res, gpointer user_data);130 static void application_start_cb(GObject* obj, GAsyncResult* res, gpointer user_data);
131
132 void resetUnit(const UnitInfo& info) const;
131};133};
132134
133} // namespace manager135} // namespace manager
134136
=== modified file 'tests/jobs-systemd.cpp'
--- tests/jobs-systemd.cpp 2017-01-28 05:27:48 +0000
+++ tests/jobs-systemd.cpp 2017-02-04 04:10:31 +0000
@@ -360,3 +360,27 @@
360360
361 EXPECT_EQ(multipleAppID(), removeunit.get_future().get());361 EXPECT_EQ(multipleAppID(), removeunit.get_future().get());
362}362}
363
364TEST_F(JobsSystemd, UnitFailure)
365{
366 auto manager = std::make_shared<ubuntu::app_launch::jobs::manager::SystemD>(registry);
367 registry->impl->jobs = manager;
368
369 ubuntu::app_launch::AppID failedappid;
370 manager->appFailed().connect([&](const std::shared_ptr<ubuntu::app_launch::Application> &app,
371 const std::shared_ptr<ubuntu::app_launch::Application::Instance> &inst,
372 ubuntu::app_launch::Registry::FailureType type) { failedappid = app->appId(); });
373
374 systemd->managerEmitFailed({defaultJobName(), std::string{multipleAppID()}, "1234567890", 1, {}});
375
376 EXPECT_EVENTUALLY_EQ(multipleAppID(), failedappid);
377
378 std::list<std::string> resets;
379 EXPECT_EVENTUALLY_FUNC_LT(0u, std::function<unsigned int()>([&]() {
380 resets = systemd->resetCalls();
381 return resets.size();
382 }));
383
384 EXPECT_EQ(SystemdMock::instanceName({defaultJobName(), std::string{multipleAppID()}, "1234567890", 1, {}}),
385 *resets.begin());
386}
363387
=== modified file 'tests/systemd-mock.h'
--- tests/systemd-mock.h 2017-01-28 05:27:48 +0000
+++ tests/systemd-mock.h 2017-02-04 04:10:31 +0000
@@ -140,6 +140,11 @@
140 "ret = '/'", &error);140 "ret = '/'", &error);
141 throwError(error);141 throwError(error);
142142
143 dbus_test_dbus_mock_object_add_method(mock, managerobj, "ResetFailedUnit", G_VARIANT_TYPE_STRING,
144 nullptr, /* ret type */
145 "", &error);
146 throwError(error);
147
143 for (auto& instance : instances)148 for (auto& instance : instances)
144 {149 {
145 auto obj = dbus_test_dbus_mock_get_object(mock, instancePath(instance).c_str(),150 auto obj = dbus_test_dbus_mock_get_object(mock, instancePath(instance).c_str(),
@@ -148,6 +153,9 @@
148 dbus_test_dbus_mock_object_add_property(mock, obj, "MainPID", G_VARIANT_TYPE_UINT32,153 dbus_test_dbus_mock_object_add_property(mock, obj, "MainPID", G_VARIANT_TYPE_UINT32,
149 g_variant_new_uint32(instance.primaryPid), &error);154 g_variant_new_uint32(instance.primaryPid), &error);
150 throwError(error);155 throwError(error);
156 dbus_test_dbus_mock_object_add_property(mock, obj, "Result", G_VARIANT_TYPE_STRING,
157 g_variant_new_string("success"), &error);
158 throwError(error);
151159
152 /* Control Group */160 /* Control Group */
153 auto dir = g_build_filename(controlGroupPath.c_str(), instancePath(instance).c_str(), nullptr);161 auto dir = g_build_filename(controlGroupPath.c_str(), instancePath(instance).c_str(), nullptr);
@@ -432,6 +440,46 @@
432 return retval;440 return retval;
433 }441 }
434442
443 std::list<std::string> resetCalls()
444 {
445 guint len = 0;
446 GError* error = nullptr;
447
448 auto calls = dbus_test_dbus_mock_object_get_method_calls(mock, /* mock */
449 managerobj, /* manager */
450 "ResetFailedUnit", /* function */
451 &len, /* number */
452 &error /* error */
453 );
454
455 if (error != nullptr)
456 {
457 g_warning("Unable to get 'ResetFailedUnit' calls from systemd mock: %s", error->message);
458 g_error_free(error);
459 throw std::runtime_error{"Mock disfunctional"};
460 }
461
462 std::list<std::string> retval;
463
464 for (unsigned int i = 0; i < len; i++)
465 {
466 auto& call = calls[i];
467 gchar* name = nullptr;
468
469 g_variant_get(call.params, "(&s)", &name);
470
471 if (name == nullptr)
472 {
473 g_warning("Invalid 'name' on 'ResetFailedUnit' call");
474 continue;
475 }
476
477 retval.emplace_back(name);
478 }
479
480 return retval;
481 }
482
435 void managerClear()483 void managerClear()
436 {484 {
437 GError* error = nullptr;485 GError* error = nullptr;
@@ -478,4 +526,29 @@
478 throw std::runtime_error{"Mock disfunctional"};526 throw std::runtime_error{"Mock disfunctional"};
479 }527 }
480 }528 }
529
530 void managerEmitFailed(const Instance& inst)
531 {
532 auto instobj =
533 std::find_if(insts.begin(), insts.end(), [inst](const std::pair<Instance, DbusTestDbusMockObject*>& item) {
534 return item.first.job == inst.job && item.first.appid == inst.appid &&
535 item.first.instanceid == inst.instanceid;
536 });
537
538 if (instobj == insts.end())
539 {
540 throw std::runtime_error{"Unable to find instance"};
541 }
542
543 GError* error = nullptr;
544 dbus_test_dbus_mock_object_update_property(mock, instobj->second, "Result", g_variant_new_string("fail"),
545 &error);
546
547 if (error != nullptr)
548 {
549 g_warning("Unable to set result to 'fail': %s", error->message);
550 g_error_free(error);
551 throw std::runtime_error{"Mock disfunctional"};
552 }
553 }
481};554};

Subscribers

People subscribed via source and target branches