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
1=== modified file 'softwarecenter/backend/installbackend_impl/aptd.py'
2--- softwarecenter/backend/installbackend_impl/aptd.py 2012-08-07 13:15:08 +0000
3+++ softwarecenter/backend/installbackend_impl/aptd.py 2012-08-23 12:29:44 +0000
4@@ -21,6 +21,12 @@
5 import logging
6 import os
7 import re
8+import traceback
9+
10+from subprocess import (
11+ Popen,
12+ PIPE,
13+ )
14
15 from gi.repository import GObject
16
17@@ -29,7 +35,11 @@
18 obfuscate_private_ppa_details,
19 utf8,
20 )
21-from softwarecenter.enums import TransactionTypes, PURCHASE_TRANSACTION_ID
22+from softwarecenter.enums import (
23+ TransactionTypes,
24+ PURCHASE_TRANSACTION_ID,
25+ )
26+from softwarecenter.paths import APPORT_RECOVERABLE_ERROR
27
28 from aptdaemon import client
29 from aptdaemon import enums
30@@ -973,13 +983,53 @@
31
32 self.emit("transaction-stopped", result)
33 if isinstance(error, dbus.DBusException):
34+ # ignore errors that the user knows about already (like
35+ # that he entered a wrong password or that he does not
36+ # care about (like no-reply)
37 name = error.get_dbus_name()
38 if name in ["org.freedesktop.PolicyKit.Error.NotAuthorized",
39 "org.freedesktop.DBus.Error.NoReply"]:
40- pass
41- else:
42- self._logger.exception("_on_trans_error")
43- #raise error
44+ return
45+ # we want to give some advice here to the user but also
46+ # report this via apport
47+ if name in ["org.freedesktop.PolicyKit.Error.Failed"]:
48+ summary = _("Authentication Error")
49+ text = _("Software can't be installed or removed because "
50+ "the authentication service is not available. "
51+ "(%s") % error
52+ # send to apport for reporting
53+ self._call_apport_recoverable_error(text, error)
54+ # ... and display as a dialog
55+ self.ui.error(None, summary, text)
56+ return
57+ # show a apport recoverable error dialog to the user as we want
58+ # to know about these issues
59+ self._call_apport_recoverable_error(
60+ _("There was an error submitting the transaction"), error)
61+
62+ def _call_apport_recoverable_error(self, text, error):
63+ """Call apports recoverable_problem dialog """
64+
65+ # ensure we have a proper expection string in the report
66+ if isinstance(error, Exception):
67+ error = traceback.format_exc(error)
68+
69+ # mvo: I don't think we need to send "Package\0software-center",
70+ # apport should figure this out itself
71+ data = "DialogBody\0%(text)s\0Traceback\0%(error)s" % {
72+ 'text': text,
73+ 'error': error,
74+ }
75+ # This will be quick as it just writes the report file. Then
76+ # the report gets picked up asynchronously by a inotify watch
77+ # and displayed to the user in a seperate process.
78+ p = Popen(
79+ [APPORT_RECOVERABLE_ERROR], stdin=PIPE, stdout=PIPE, stderr=PIPE)
80+
81+ (stdout, stderr) = p.communicate(input=data)
82+ if p.returncode != 0:
83+ logging.warn("%s returned '%s' ('%s', '%s')" % (
84+ APPORT_RECOVERABLE_ERROR, p.returncode, stdout, stderr))
85
86
87 if __name__ == "__main__":
88
89=== modified file 'softwarecenter/paths.py'
90--- softwarecenter/paths.py 2012-08-22 08:16:30 +0000
91+++ softwarecenter/paths.py 2012-08-23 12:29:44 +0000
92@@ -73,7 +73,15 @@
93 # OEM
94 OEM_CHANNEL_DESCRIPTOR = "/var/lib/ubuntu-dist-channel"
95
96-
97+<<<<<<< TREE
98+
99+=======
100+
101+# apport
102+APPORT_RECOVERABLE_ERROR = "/usr/share/apport/recoverable_problem"
103+
104+
105+>>>>>>> MERGE-SOURCE
106 # ratings&review
107 # relative to datadir
108 class RNRApps:
109
110=== modified file 'tests/test_aptd.py'
111--- tests/test_aptd.py 2012-06-28 08:51:44 +0000
112+++ tests/test_aptd.py 2012-08-23 12:29:44 +0000
113@@ -3,18 +3,23 @@
114
115 import logging
116 import os
117+import subprocess
118 import time
119 import unittest
120
121+import dbus
122+
123 from tests.utils import (
124 setup_test_env,
125 )
126 setup_test_env()
127
128+from softwarecenter.backend.installbackend_impl.aptd import AptdaemonBackend
129 from softwarecenter.db.application import Application
130-from softwarecenter.backend.installbackend_impl.aptd import AptdaemonBackend
131+from softwarecenter.paths import APPORT_RECOVERABLE_ERROR
132+
133 from defer import inline_callbacks
134-from mock import Mock
135+from mock import Mock, patch
136
137 import aptdaemon.loop
138
139@@ -24,6 +29,7 @@
140
141 def setUp(self):
142 self.aptd = AptdaemonBackend()
143+ self.aptd.ui = Mock()
144 # monkey patch
145 self.aptd.aptd_client.install_packages = self._mock_aptd_client_install_packages
146 self._pkgs_to_install = []
147@@ -105,6 +111,25 @@
148 addons_remove = ["gimp-plugin-registry"]
149 yield self.aptd.apply_changes(pkgname, appname ,iconname, addons_install, addons_remove)
150
151+
152+ def test_trans_error_ui_display(self):
153+ """ test if the right error ui is displayed for various dbus
154+ errors
155+ """
156+ error = dbus.DBusException()
157+ dbus_name_mock = Mock()
158+ with patch.object(self.aptd.ui, "error") as error_ui_mock:
159+ for dbus_name, show_error_ui in [
160+ ("org.freedesktop.PolicyKit.Error.NotAuthorized", False),
161+ ("org.freedesktop.PolicyKit.Error.Failed", True),
162+ ("moo.baa.lalala", True),
163+ ]:
164+ error_ui_mock.reset()
165+ dbus_name_mock.return_value = dbus_name
166+ error.get_dbus_name = dbus_name_mock
167+ self.aptd._on_trans_error(error)
168+ self.assertEqual(error_ui_mock.called, show_error_ui)
169+
170 @inline_callbacks
171 def _inline_add_repo_call(self):
172 deb_line = "deb https://foo"
173@@ -113,7 +138,7 @@
174 iconname = "iconname"
175 yield self.aptd.add_repo_add_key_and_install_app(
176 deb_line, signing_key_id, app, iconname, None, None)
177-
178+
179 def test_add_repo_add_key_and_install_app(self):
180 from mock import patch
181 with patch.object(self.aptd._logger, "info") as mock:
182@@ -121,5 +146,22 @@
183 self.assertTrue(
184 mock.call_args[0][0].startswith("add_repo_add_key"))
185
186+ @patch("softwarecenter.backend.installbackend_impl.aptd.Popen")
187+ def test_recoverable_error(self, mock_popen):
188+ mock_popen_instance = Mock()
189+ mock_popen_instance.communicate.return_value = ("stdout", "stderr")
190+ mock_popen_instance.returncode = 0
191+ mock_popen.return_value = mock_popen_instance
192+ self.aptd._call_apport_recoverable_error("msg", "traceback-error")
193+ mock_popen.assert_called_with(
194+ [APPORT_RECOVERABLE_ERROR], stdin=subprocess.PIPE,
195+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
196+ # check that we really send the right data
197+ args, kwargs = mock_popen_instance.communicate.call_args
198+ self.assertEqual(
199+ kwargs["input"].split("\0"),
200+ [ 'DialogBody', 'msg','Traceback', 'traceback-error'])
201+
202+
203 if __name__ == "__main__":
204 unittest.main()

Subscribers

People subscribed via source and target branches