Merge lp:~mvo/software-center/polkit-lp785117 into lp:software-center

Proposed by Michael Vogt
Status: Merged
Merged at revision: 3123
Proposed branch: lp:~mvo/software-center/polkit-lp785117
Merge into: lp:software-center
Diff against target: 204 lines (+109/-9) (has conflicts)
3 files modified
softwarecenter/backend/installbackend_impl/aptd.py (+55/-5)
softwarecenter/paths.py (+9/-1)
tests/test_aptd.py (+45/-3)
Text conflict in softwarecenter/paths.py
To merge this branch: bzr merge lp:~mvo/software-center/polkit-lp785117
Reviewer Review Type Date Requested Status
Evan (community) Approve
Matthew Paul Thomas Pending
software-store-developers Pending
Review via email: mp+112292@code.launchpad.net

Description of the change

This branch adds more error handling in the failure case.

Just like now errors like "NotAuthorized" or "NoReply" are ignored as policykit will already have displayed
something to the user. There is a new error message if the agent failed to launch, this can be tested in e.g.
a fvwm session.

Plus unknown errors are also displayed and the dialog asks for a bugreport, this should give us a idea if
there is anything else hidding here that needs attention. Matthew may want to tweak the error dialog texts,
but I think its ok to have them somewhat technically as this is a pretty rare circumstance when no policykit-agent
is available and lots of stuff will break in this case.

To post a comment you must log in.
Revision history for this message
Evan (ev) wrote :

I believe that recovering from errors while at the same time reporting them is something that we're going to want to do often and consistently.

Matthew had a work item this cycle to create the interface for an application-triggered recoverable error report:
https://wiki.ubuntu.com/ErrorTracker#app-requested

I've implemented this first as a DBus service:
https://code.launchpad.net/~ev/apport/recoverable-errors/+merge/111840

There is an ongoing discussion on picking a communication channel with far fewer dependencies than DBus, but whatever the result we will have something implemented for 12.10.

Michael, I would love your input on that merge request in the hope that we can build something that addresses what you're trying to achieve here.

Revision history for this message
Michael Vogt (mvo) wrote :

I updated the code now to use the new apport mechanism for reporting recoverable problems.

Revision history for this message
Michael Vogt (mvo) wrote :

I updated the code now to use the new apport mechanism for reporting recoverable problems.

Revision history for this message
Evan (ev) wrote :
Download full text (3.2 KiB)

On Wed, Aug 22, 2012 at 8:57 AM, Michael Vogt <email address hidden> wrote:
> === modified file 'softwarecenter/backend/installbackend_impl/aptd.py'
> --- softwarecenter/backend/installbackend_impl/aptd.py 2012-08-07 13:15:08 +0000
> +++ softwarecenter/backend/installbackend_impl/aptd.py 2012-08-22 07:56:18 +0000
> @@ -973,13 +979,48 @@
> + return
> + if name in ["org.freedesktop.PolicyKit.Error.Failed"]:
> + summary = _("Authentication Error")
> + text = _("Is policykit installed and an authentication "
> + "agent running? Error Details:\n%s") % error
> + self.ui.error(None, summary, text)
> + return

Can you clarify the rationale for creating a software-center dialog
and exiting the function here? I would have thought you'd want to have
graphs of the occurrence of the authentication agent not running, as
it may be more often than we suspect and be evidence of a larger
problem. We won't know until we measure though.

> + # show a apport recoverable error dialog to the user as we want
> + # to know about these issues
> + self._call_apport_recoverable_error(
> + _("There was a error submitting the transaction"), error)

Grammar nit: an before vowel sounds. So it's "there was an error."

> + # mvo: I don't think we need to send "Package\0software-center",
> + # apport should figure this out itself
> + data = "DialogBody\0%(text)s\0Traceback\0%(error)s" % {
> + 'text': text,
> + 'error': error,
> + }
> + # this will be quick as it just writes the report file and the
> + # helper is spawned by apport asyncronsoly

Correct, but to clarify: the report is written, update-notifier has an
inotify watch on /var/crash and it then calls apport to process unseen
reports.

> + APPORT_RECOVERABLE_ERROR = "/usr/share/apport/recoverable_problem"
> + p = Popen(
> + [APPORT_RECOVERABLE_ERROR], stdin=PIPE, stdout=PIPE, stderr=PIPE)
> +
> + (stdout, stderr) = p.communicate(input=data)
> + return_code = p.wait()

subprocess.communicate() calls wait():
Lib/subprocess.py:communicate:751

So you could just grab it off the pid member. There's no harm in
calling wait() again though, so feel free to leave that.

> === modified file 'tests/test_aptd.py'
> --- tests/test_aptd.py 2012-06-28 08:51:44 +0000
> +++ tests/test_aptd.py 2012-08-22 07:56:18 +0000
> @@ -121,5 +143,15 @@
> + @patch("softwarecenter.backend.installbackend_impl.aptd.Popen")
> + def test_recoverable_error(self, mock_popen):
> + mock_popen_instance = Mock()
> + mock_popen_instance.communicate.return_value = ("stdout", "stderr")
> + mock_popen_instance.wait.return_value = 0
> + mock_popen.return_value = mock_popen_instance
> + self.aptd._call_apport_recoverable_error("msg", "error-error")
> + mock_popen_instance.communicate.assert_called()

You could expand this to check the arguments to Popen and put a
StringIO on stdin to ensure that you're getting three nuls and the
right key-value pair...

Read more...

Revision history for this message
Matthew Paul Thomas (mpt) wrote :

Even if you did convert the custom error to an Apport one, though...

+ text = _("Is policykit installed and an authentication "
+ "agent running? Error Details:\n%s") % error

...A big part of USC's day job is to tell whether or not things are installed. Why is it now asking me whether something is installed? :-) (Even I have trouble with this particular question, because I confuse policykit and policykit-1.) Besides, software-center Depends on policykit-1 and policykit-1-gnome, so being uninstalled is unlikely to be the problem, unless it was removed (together with software-center itself) while USC was open.

Instead I suggest something like "Software can’t be installed or removed because the authentication service is not available. (%s)" % error

3032. By Michael Vogt

address the easy points of evans review

Revision history for this message
Michael Vogt (mvo) wrote :

On Wed, Aug 22, 2012 at 11:31:08AM -0000, Matthew Paul Thomas wrote:
> Even if you did convert the custom error to an Apport one, though...
>
> + text = _("Is policykit installed and an authentication "
> + "agent running? Error Details:\n%s") % error
>
> ...A big part of USC's day job is to tell whether or not things are installed. Why is it now asking me whether something is installed? :-) (Even I have trouble with this particular question, because I confuse policykit and policykit-1.) Besides, software-center Depends on policykit-1 and policykit-1-gnome, so being uninstalled is unlikely to be the problem, unless it was removed (together with software-center itself) while USC was open.

Indeed, this part of the text dosn't make a lot of sense, it will be
installed, but it might not be running because its running inside a
environement that does not autostart it or because there is something
wrong with the install.

> Instead I suggest something like "Software can’t be installed or removed because the authentication service is not available. (%s)" % error

Thanks, I updated the text to match what you write.

I will also make this part of what is reported via apport as a
recoverable error so that we can get data on it as Evan suggested.

Cheers,
 Michael

3033. By Michael Vogt

softwarecenter/backend/installbackend_impl/aptd.py: update policykit error text to match the suggestion of mpt (thanks!)

3034. By Michael Vogt

tests/test_aptd.py: improve test (and as a consequence the code) as suggested by Evan

3035. By Michael Vogt

tests/test_aptd.py: further improve the test

3036. By Michael Vogt

softwarecenter/backend/installbackend_impl/aptd.py: send unavailable polkit agent also to apport

Revision history for this message
Michael Vogt (mvo) wrote :
Download full text (4.2 KiB)

On Wed, Aug 22, 2012 at 11:10:00AM -0000, Evan Dandrea wrote:
> On Wed, Aug 22, 2012 at 8:57 AM, Michael Vogt <email address hidden> wrote:
> > === modified file 'softwarecenter/backend/installbackend_impl/aptd.py'
> > --- softwarecenter/backend/installbackend_impl/aptd.py 2012-08-07 13:15:08 +0000
> > +++ softwarecenter/backend/installbackend_impl/aptd.py 2012-08-22 07:56:18 +0000
> > @@ -973,13 +979,48 @@
> > + return
> > + if name in ["org.freedesktop.PolicyKit.Error.Failed"]:
> > + summary = _("Authentication Error")
> > + text = _("Is policykit installed and an authentication "
> > + "agent running? Error Details:\n%s") % error
> > + self.ui.error(None, summary, text)
> > + return
>
> Can you clarify the rationale for creating a software-center dialog
> and exiting the function here? I would have thought you'd want to have
> graphs of the occurrence of the authentication agent not running, as
> it may be more often than we suspect and be evidence of a larger
> problem. We won't know until we measure though.

You are right, it should be meassured, I now modified the code to do
that.

What is your opinion on showing the internal dialog still in addition
to fire of the report to apport? The assumption here is that the user
might not have update-notifier runnig to pick the report up
immediately and because in this case we know what the problem is its
nice to show it to the user. Alternatively I could make this
conditional, i.e. if update-notifier is availalbe rely on that and if
not display a internal dialog. WDYT?

> > + # show a apport recoverable error dialog to the user as we want
> > + # to know about these issues
> > + self._call_apport_recoverable_error(
> > + _("There was a error submitting the transaction"), error)
>
> Grammar nit: an before vowel sounds. So it's "there was an error."

Fixed.

> > + # mvo: I don't think we need to send "Package\0software-center",
> > + # apport should figure this out itself
> > + data = "DialogBody\0%(text)s\0Traceback\0%(error)s" % {
> > + 'text': text,
> > + 'error': error,
> > + }
> > + # this will be quick as it just writes the report file and the
> > + # helper is spawned by apport asyncronsoly
>
> Correct, but to clarify: the report is written, update-notifier has an
> inotify watch on /var/crash and it then calls apport to process unseen
> reports.

I updated the comment in the code to make this a bit clearer.

> > + APPORT_RECOVERABLE_ERROR = "/usr/share/apport/recoverable_problem"
> > + p = Popen(
> > + [APPORT_RECOVERABLE_ERROR], stdin=PIPE, stdout=PIPE, stderr=PIPE)
> > +
> > + (stdout, stderr) = p.communicate(input=data)
> > + return_code = p.wait()
>
> subprocess.communicate() calls wait():
> Lib/subprocess.py:communicate:751
>
> So you could just grab it off the pid member. There's no harm in
> calling wait() again though, so feel free to leave that.

Good point :) I simply use p.returncode now.

> > === modifie...

Read more...

3037. By Michael Vogt

trivial pep8 fixes

Revision history for this message
Evan (ev) wrote :

On Thu, Aug 23, 2012 at 1:30 PM, Michael Vogt <email address hidden> wrote:
> What is your opinion on showing the internal dialog still in addition
> to fire of the report to apport? The assumption here is that the user
> might not have update-notifier runnig to pick the report up
> immediately and because in this case we know what the problem is its
> nice to show it to the user. Alternatively I could make this
> conditional, i.e. if update-notifier is availalbe rely on that and if
> not display a internal dialog. WDYT?

I think the cases of update-notifier are sufficiently small to not
burden everyone with redundant dialogs. For what it's worth, using
update-notifier for inotify watches will be replaced by upstart
inotify events once James has finished those.

The rest looks good.

Revision history for this message
Evan (ev) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'softwarecenter/backend/installbackend_impl/aptd.py'
--- softwarecenter/backend/installbackend_impl/aptd.py 2012-08-07 13:15:08 +0000
+++ softwarecenter/backend/installbackend_impl/aptd.py 2012-08-23 12:29:44 +0000
@@ -21,6 +21,12 @@
21import logging21import logging
22import os22import os
23import re23import re
24import traceback
25
26from subprocess import (
27 Popen,
28 PIPE,
29 )
2430
25from gi.repository import GObject31from gi.repository import GObject
2632
@@ -29,7 +35,11 @@
29 obfuscate_private_ppa_details,35 obfuscate_private_ppa_details,
30 utf8,36 utf8,
31 )37 )
32from softwarecenter.enums import TransactionTypes, PURCHASE_TRANSACTION_ID38from softwarecenter.enums import (
39 TransactionTypes,
40 PURCHASE_TRANSACTION_ID,
41 )
42from softwarecenter.paths import APPORT_RECOVERABLE_ERROR
3343
34from aptdaemon import client44from aptdaemon import client
35from aptdaemon import enums45from aptdaemon import enums
@@ -973,13 +983,53 @@
973983
974 self.emit("transaction-stopped", result)984 self.emit("transaction-stopped", result)
975 if isinstance(error, dbus.DBusException):985 if isinstance(error, dbus.DBusException):
986 # ignore errors that the user knows about already (like
987 # that he entered a wrong password or that he does not
988 # care about (like no-reply)
976 name = error.get_dbus_name()989 name = error.get_dbus_name()
977 if name in ["org.freedesktop.PolicyKit.Error.NotAuthorized",990 if name in ["org.freedesktop.PolicyKit.Error.NotAuthorized",
978 "org.freedesktop.DBus.Error.NoReply"]:991 "org.freedesktop.DBus.Error.NoReply"]:
979 pass992 return
980 else:993 # we want to give some advice here to the user but also
981 self._logger.exception("_on_trans_error")994 # report this via apport
982 #raise error995 if name in ["org.freedesktop.PolicyKit.Error.Failed"]:
996 summary = _("Authentication Error")
997 text = _("Software can't be installed or removed because "
998 "the authentication service is not available. "
999 "(%s") % error
1000 # send to apport for reporting
1001 self._call_apport_recoverable_error(text, error)
1002 # ... and display as a dialog
1003 self.ui.error(None, summary, text)
1004 return
1005 # show a apport recoverable error dialog to the user as we want
1006 # to know about these issues
1007 self._call_apport_recoverable_error(
1008 _("There was an error submitting the transaction"), error)
1009
1010 def _call_apport_recoverable_error(self, text, error):
1011 """Call apports recoverable_problem dialog """
1012
1013 # ensure we have a proper expection string in the report
1014 if isinstance(error, Exception):
1015 error = traceback.format_exc(error)
1016
1017 # mvo: I don't think we need to send "Package\0software-center",
1018 # apport should figure this out itself
1019 data = "DialogBody\0%(text)s\0Traceback\0%(error)s" % {
1020 'text': text,
1021 'error': error,
1022 }
1023 # This will be quick as it just writes the report file. Then
1024 # the report gets picked up asynchronously by a inotify watch
1025 # and displayed to the user in a seperate process.
1026 p = Popen(
1027 [APPORT_RECOVERABLE_ERROR], stdin=PIPE, stdout=PIPE, stderr=PIPE)
1028
1029 (stdout, stderr) = p.communicate(input=data)
1030 if p.returncode != 0:
1031 logging.warn("%s returned '%s' ('%s', '%s')" % (
1032 APPORT_RECOVERABLE_ERROR, p.returncode, stdout, stderr))
9831033
9841034
985if __name__ == "__main__":1035if __name__ == "__main__":
9861036
=== modified file 'softwarecenter/paths.py'
--- softwarecenter/paths.py 2012-08-22 08:16:30 +0000
+++ softwarecenter/paths.py 2012-08-23 12:29:44 +0000
@@ -73,7 +73,15 @@
73# OEM73# OEM
74OEM_CHANNEL_DESCRIPTOR = "/var/lib/ubuntu-dist-channel"74OEM_CHANNEL_DESCRIPTOR = "/var/lib/ubuntu-dist-channel"
7575
7676<<<<<<< TREE
77
78=======
79
80# apport
81APPORT_RECOVERABLE_ERROR = "/usr/share/apport/recoverable_problem"
82
83
84>>>>>>> MERGE-SOURCE
77# ratings&review85# ratings&review
78# relative to datadir86# relative to datadir
79class RNRApps:87class RNRApps:
8088
=== modified file 'tests/test_aptd.py'
--- tests/test_aptd.py 2012-06-28 08:51:44 +0000
+++ tests/test_aptd.py 2012-08-23 12:29:44 +0000
@@ -3,18 +3,23 @@
33
4import logging4import logging
5import os5import os
6import subprocess
6import time7import time
7import unittest8import unittest
89
10import dbus
11
9from tests.utils import (12from tests.utils import (
10 setup_test_env,13 setup_test_env,
11)14)
12setup_test_env()15setup_test_env()
1316
17from softwarecenter.backend.installbackend_impl.aptd import AptdaemonBackend
14from softwarecenter.db.application import Application18from softwarecenter.db.application import Application
15from softwarecenter.backend.installbackend_impl.aptd import AptdaemonBackend19from softwarecenter.paths import APPORT_RECOVERABLE_ERROR
20
16from defer import inline_callbacks21from defer import inline_callbacks
17from mock import Mock22from mock import Mock, patch
1823
19import aptdaemon.loop24import aptdaemon.loop
2025
@@ -24,6 +29,7 @@
2429
25 def setUp(self):30 def setUp(self):
26 self.aptd = AptdaemonBackend()31 self.aptd = AptdaemonBackend()
32 self.aptd.ui = Mock()
27 # monkey patch33 # monkey patch
28 self.aptd.aptd_client.install_packages = self._mock_aptd_client_install_packages34 self.aptd.aptd_client.install_packages = self._mock_aptd_client_install_packages
29 self._pkgs_to_install = []35 self._pkgs_to_install = []
@@ -105,6 +111,25 @@
105 addons_remove = ["gimp-plugin-registry"]111 addons_remove = ["gimp-plugin-registry"]
106 yield self.aptd.apply_changes(pkgname, appname ,iconname, addons_install, addons_remove)112 yield self.aptd.apply_changes(pkgname, appname ,iconname, addons_install, addons_remove)
107113
114
115 def test_trans_error_ui_display(self):
116 """ test if the right error ui is displayed for various dbus
117 errors
118 """
119 error = dbus.DBusException()
120 dbus_name_mock = Mock()
121 with patch.object(self.aptd.ui, "error") as error_ui_mock:
122 for dbus_name, show_error_ui in [
123 ("org.freedesktop.PolicyKit.Error.NotAuthorized", False),
124 ("org.freedesktop.PolicyKit.Error.Failed", True),
125 ("moo.baa.lalala", True),
126 ]:
127 error_ui_mock.reset()
128 dbus_name_mock.return_value = dbus_name
129 error.get_dbus_name = dbus_name_mock
130 self.aptd._on_trans_error(error)
131 self.assertEqual(error_ui_mock.called, show_error_ui)
132
108 @inline_callbacks133 @inline_callbacks
109 def _inline_add_repo_call(self):134 def _inline_add_repo_call(self):
110 deb_line = "deb https://foo"135 deb_line = "deb https://foo"
@@ -113,7 +138,7 @@
113 iconname = "iconname"138 iconname = "iconname"
114 yield self.aptd.add_repo_add_key_and_install_app(139 yield self.aptd.add_repo_add_key_and_install_app(
115 deb_line, signing_key_id, app, iconname, None, None)140 deb_line, signing_key_id, app, iconname, None, None)
116 141
117 def test_add_repo_add_key_and_install_app(self):142 def test_add_repo_add_key_and_install_app(self):
118 from mock import patch143 from mock import patch
119 with patch.object(self.aptd._logger, "info") as mock:144 with patch.object(self.aptd._logger, "info") as mock:
@@ -121,5 +146,22 @@
121 self.assertTrue(146 self.assertTrue(
122 mock.call_args[0][0].startswith("add_repo_add_key"))147 mock.call_args[0][0].startswith("add_repo_add_key"))
123148
149 @patch("softwarecenter.backend.installbackend_impl.aptd.Popen")
150 def test_recoverable_error(self, mock_popen):
151 mock_popen_instance = Mock()
152 mock_popen_instance.communicate.return_value = ("stdout", "stderr")
153 mock_popen_instance.returncode = 0
154 mock_popen.return_value = mock_popen_instance
155 self.aptd._call_apport_recoverable_error("msg", "traceback-error")
156 mock_popen.assert_called_with(
157 [APPORT_RECOVERABLE_ERROR], stdin=subprocess.PIPE,
158 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
159 # check that we really send the right data
160 args, kwargs = mock_popen_instance.communicate.call_args
161 self.assertEqual(
162 kwargs["input"].split("\0"),
163 [ 'DialogBody', 'msg','Traceback', 'traceback-error'])
164
165
124if __name__ == "__main__":166if __name__ == "__main__":
125 unittest.main()167 unittest.main()

Subscribers

People subscribed via source and target branches