Merge lp:~brian-murray/apport/support-ppa-packages into lp:~apport-hackers/apport/trunk

Proposed by Brian Murray
Status: Merged
Merged at revision: 2980
Proposed branch: lp:~brian-murray/apport/support-ppa-packages
Merge into: lp:~apport-hackers/apport/trunk
Diff against target: 503 lines (+274/-20)
6 files modified
AUTHORS (+4/-1)
apport/packaging.py (+5/-1)
apport/sandboxutils.py (+21/-6)
backends/packaging-apt-dpkg.py (+151/-7)
bin/apport-retrace (+4/-1)
test/test_backend_apt_dpkg.py (+89/-4)
To merge this branch: bzr merge lp:~brian-murray/apport/support-ppa-packages
Reviewer Review Type Date Requested Status
Martin Pitt (community) Approve
Review via email: mp+263437@code.launchpad.net

Description of the change

This branch builds upon Tim Lunn's initial work (https://code.launchpad.net/~darkxst/apport/per-ppa-config2/+merge/180972) to add PPA support to apport-retrace.

A list of "origins" is created for packages with a foreign origin. This is then passed to install packages so that the sandbox will be created with proper apt data sources for the PPA. Its also possible to setup an apt data source that will be used when creating the sandbox. For consistency with apt these sources are located in /etc/apt/sources.list.d/, they can either be named the same as the PPA (brian-murray-ppa.list) or with LP-PPA- preceeding the PPA name. This was done since the origin information is listed as LP-PPA-brian-murray-ppa and people may use that when setting up a configuration for apport-retrace. I've also added apt keyring support for any PPAs added.

The following new tests were added to test_backend_apt_dpkg.py:
    def test_create_sources_for_a_named_ppa(self):
    def test_create_sources_for_an_unnamed_ppa(self):
    def test_use_sources_for_a_ppa(self):
    def test_install_package_from_a_ppa(self):

They test the creation of a sources.list entry for two different types of PPAs, using a configured sources.list entry for a PPA, and installing a package from a PPA. Three of the tests are dependent upon two PPAs being available which might be a point of concern.

To post a comment you must log in.
Revision history for this message
Martin Pitt (pitti) wrote :

Thanks Brian! The general idea looks fine; with the restricted "Origin:" fields that we have there's no way around the guesswork.

review: Needs Fixing
2979. By Brian Murray

mess o' modifications based on pitti's feeback

Revision history for this message
Brian Murray (brian-murray) wrote :

Thanks for all the comments, I think I have addressed all of them and the code should look better now.

Revision history for this message
Brian Murray (brian-murray) wrote :

Steve indicated there may be some security concerns about installing any package from Launchpad and running that package's binaries inside gdb. Subsequently, I have made the ability to create apt data sources for PPAs an optional one and set the default value to false. Regardless, if there are sources.list entries in the configdir corresponding to a PPA then packages will be used from those PPAs.

2980. By Brian Murray

Add an option to apport-retrace (and pass it along) so that apt sources data is only created for PPAs if requested.

Revision history for this message
Steve Langasek (vorlon) wrote :

Security concerns, and also infrastructure load concerns. I don't believe we've made any committment to providing retracing resources for crashes of packages originating from arbitrary ppas.

2981. By Brian Murray

Fix origins re, pass log_timestamps to make_sandbox, fix a bug in origin_path existence checking.

2982. By Brian Murray

explicitly close the results from urlopen

Revision history for this message
Martin Pitt (pitti) wrote :

A couple of inline replies to the previous review round. I'll review the new code in a separate comment.

Revision history for this message
Martin Pitt (pitti) wrote :

Thanks for the updates! Another round, then this should be perfect.

review: Needs Fixing
2983. By Brian Murray

make testing for gpg keys less fragile

2984. By Brian Murray

address pitti's feedback and simplify how using origins is specified

Revision history for this message
Brian Murray (brian-murray) wrote :

Okay, I think I've addressed everything, except for your concern regarding sources.list entries which I've commented on in-line.

Revision history for this message
Martin Pitt (pitti) wrote :

Responding to sources.list parsing.

Revision history for this message
Martin Pitt (pitti) wrote :

Some tiny nitpicks left, which I'd also be happy to do myself during merge. Do you want to change the sources.list parsing as discussed? If not I'm okay with leaving it like that and cleaning this up in a separate MP. Thanks!

2985. By Brian Murray

address final nitpicks

Revision history for this message
Brian Murray (brian-murray) wrote :

I've fixed the nitpicks and would prefer to sort out the sources.list parsing in a separate merge proposal.

Revision history for this message
Martin Pitt (pitti) wrote :
Download full text (3.3 KiB)

The last commit changed the wrong len(components), I fixed that and added a NEWS entry.

The tests fail with PYTHON=python2, with this:

======================================================================
ERROR: test_create_sources_for_a_named_ppa (__main__.T)
Add sources.list entries for a named PPA.
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test/test_backend_apt_dpkg.py", line 987, in test_create_sources_for_a_named_ppa
    'ubuntu', 'trusty', origins=[ppa])
  File "backends/packaging-apt-dpkg.py", line 1368, in _build_apt_sandbox
    ppa_info = apport.packaging.json_request(ppa_archive_url)
  File "/home/martin/ubuntu/apport/trunk/apport/packaging_impl.py", line 284, in json_request
    return json.loads(content)
  File "/usr/lib/python2.7/json/__init__.py", line 338, in loads
    return _default_decoder.decode(s)
  File "/usr/lib/python2.7/json/decoder.py", line 366, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/lib/python2.7/json/decoder.py", line 384, in raw_decode
    raise ValueError("No JSON object could be decoded")
ValueError: No JSON object could be decoded

I checked json_request, and content == "Object: <Person at 0x2b0b217fc8d0 daisy (Daisy Bachmann)>, name: u'+archive'" which is indeed not JSON.

In the "good" python 3 case the URL is https//api.launchpad.net/devel/~daisy-pluckers/+archive/ubuntu/daisy-seeds, in the "bad" python 2 case it is https://api.launchpad.net/devel/~daisy/+archive/ubuntu/pluckers-daisy-seeds, which is obviously scrambled.

This is indeed weird:

$ python3 -c 'from urllib.request import urlopen; print(urlopen("https://api.launchpad.net/devel
/~daisy/+archive/ubuntu/pluckers-daisy-seeds").read())'
[...]
urllib.error.HTTPError: HTTP Error 404: Not Found

$ python -c 'from urllib import urlopen; print(urlopen("https://api.launchpad.net/devel/~daisy/+archive/ubuntu/pluckers-daisy-seeds").read())'
Object: <Person at 0x2b4df96bcbd0 daisy (Daisy Bachmann)>, name: u'+archive'

However, .getcode() says "404" in python 2, so this needs to be checked separately. I added that now.

There is one failure left with Python 2:

======================================================================
ERROR: test_use_sources_for_a_ppa (__main__.T)
Use a sources.list.d file for a PPA.
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test/test_backend_apt_dpkg.py", line 1028, in test_use_sources_for_a_ppa
    'ubuntu', 'trusty', origins=['LP-PPA-%s' % ppa])
  File "backends/packaging-apt-dpkg.py", line 1372, in _build_apt_sandbox
    ppa_info = apport.packaging.json_request(ppa_archive_url)
  File "/home/martin/ubuntu/apport/trunk/apport/packaging_impl.py", line 282, in json_request
    return json.loads(content)
  File "/usr/lib/python2.7/json/__init__.py", line 338, in loads
    return _default_decoder.decode(s)
  File "/usr/lib/python2.7/json/decoder.py", line 366, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/lib/python2.7/json/decoder.py", line 384, in raw_decode
    raise ValueError("No JSON object could be decoded")
Valu...

Read more...

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'AUTHORS'
--- AUTHORS 2011-11-09 08:40:10 +0000
+++ AUTHORS 2015-07-13 22:32:23 +0000
@@ -1,7 +1,7 @@
1Copyright:1Copyright:
2---------2---------
3General:3General:
4 Copyright (C) 2006 - 2011 Canonical Ltd.4 Copyright (C) 2006 - 2015 Canonical Ltd.
55
6backends/packaging_rpm.py:6backends/packaging_rpm.py:
7 Copyright (C) 2007 Red Hat Inc.7 Copyright (C) 2007 Red Hat Inc.
@@ -32,3 +32,6 @@
3232
33Kees Cook <kees.cook@canonical.com>:33Kees Cook <kees.cook@canonical.com>:
34 Various fixes, additional GDB output, SEGV parser.34 Various fixes, additional GDB output, SEGV parser.
35
36Brian Murray <brian.murray@canonical.com>:
37 Various fixes, installation of packages from Launchpad and PPAs.
3538
=== modified file 'apport/packaging.py'
--- apport/packaging.py 2015-07-02 16:13:23 +0000
+++ apport/packaging.py 2015-07-13 22:32:23 +0000
@@ -192,7 +192,8 @@
192192
193 def install_packages(self, rootdir, configdir, release, packages,193 def install_packages(self, rootdir, configdir, release, packages,
194 verbose=False, cache_dir=None,194 verbose=False, cache_dir=None,
195 permanent_rootdir=False, architecture=None):195 permanent_rootdir=False, architecture=None,
196 origins=None):
196 '''Install packages into a sandbox (for apport-retrace).197 '''Install packages into a sandbox (for apport-retrace).
197198
198 In order to work without any special permissions and without touching199 In order to work without any special permissions and without touching
@@ -221,6 +222,9 @@
221 the given architecture (as specified in a report's "Architecture"222 the given architecture (as specified in a report's "Architecture"
222 field). If not given it defaults to the host system's architecture.223 field). If not given it defaults to the host system's architecture.
223224
225 If origins is given, the sandbox will be created with apt data sources
226 for foreign origins.
227
224 Return a string with outdated packages, or None if all packages were228 Return a string with outdated packages, or None if all packages were
225 installed.229 installed.
226230
227231
=== modified file 'apport/sandboxutils.py'
--- apport/sandboxutils.py 2015-06-11 05:49:39 +0000
+++ apport/sandboxutils.py 2015-07-13 22:32:23 +0000
@@ -10,7 +10,7 @@
10# option) any later version. See http://www.gnu.org/copyleft/gpl.html for10# option) any later version. See http://www.gnu.org/copyleft/gpl.html for
11# the full text of the license.11# the full text of the license.
1212
13import atexit, os, os.path, shutil, tempfile13import atexit, os, os.path, re, shutil, tempfile
14import apport14import apport
1515
1616
@@ -103,7 +103,8 @@
103103
104104
105def make_sandbox(report, config_dir, cache_dir=None, sandbox_dir=None,105def make_sandbox(report, config_dir, cache_dir=None, sandbox_dir=None,
106 extra_packages=[], verbose=False, log_timestamps=False):106 extra_packages=[], verbose=False, log_timestamps=False,
107 dynamic_origins=False):
107 '''Build a sandbox with the packages that belong to a particular report.108 '''Build a sandbox with the packages that belong to a particular report.
108109
109 This downloads and unpacks all packages from the report's Package and110 This downloads and unpacks all packages from the report's Package and
@@ -140,8 +141,14 @@
140 are not derived from the report.141 are not derived from the report.
141142
142 If verbose is True (False by default), this will write some additional143 If verbose is True (False by default), this will write some additional
143 logging to stdout. If log_timestamps is True, these log messages will be144 logging to stdout.
144 prefixed with the current time.145
146 If log_timestamps is True, these log messages will be prefixed with the
147 current time.
148
149 If dynamic_origins is True (False by default), the sandbox will be built
150 with packages from foreign origins that appear in the report's
151 Packages:/Dependencies:.
145152
146 Return a tuple (sandbox_dir, cache_dir, outdated_msg).153 Return a tuple (sandbox_dir, cache_dir, outdated_msg).
147 '''154 '''
@@ -179,12 +186,20 @@
179 if config_dir == 'system':186 if config_dir == 'system':
180 config_dir = None187 config_dir = None
181188
189 origins = None
190 if dynamic_origins:
191 pkg_list = report.get('Package', '') + '\n' + report.get('Dependencies', '')
192 m = re.compile('\[origin: ([a-zA-Z0-9][a-zA-Z0-9\+\.\-]+)\]')
193 origins = set(m.findall(pkg_list))
194 if origins:
195 apport.log("Origins: %s" % origins)
196
182 # unpack packages, if any, using cache and sandbox197 # unpack packages, if any, using cache and sandbox
183 try:198 try:
184 outdated_msg = apport.packaging.install_packages(199 outdated_msg = apport.packaging.install_packages(
185 sandbox_dir, config_dir, report['DistroRelease'], pkgs,200 sandbox_dir, config_dir, report['DistroRelease'], pkgs,
186 verbose, cache_dir, permanent_rootdir,201 verbose, cache_dir, permanent_rootdir,
187 architecture=report.get('Architecture'))202 architecture=report.get('Architecture'), origins=origins)
188 except SystemError as e:203 except SystemError as e:
189 apport.fatal(str(e))204 apport.fatal(str(e))
190205
@@ -210,7 +225,7 @@
210 outdated_msg += apport.packaging.install_packages(225 outdated_msg += apport.packaging.install_packages(
211 sandbox_dir, config_dir, report['DistroRelease'], pkgs,226 sandbox_dir, config_dir, report['DistroRelease'], pkgs,
212 verbose, cache_dir, permanent_rootdir,227 verbose, cache_dir, permanent_rootdir,
213 architecture=report.get('Architecture'))228 architecture=report.get('Architecture'), origins=origins)
214 except SystemError as e:229 except SystemError as e:
215 apport.fatal(str(e))230 apport.fatal(str(e))
216231
217232
=== modified file 'backends/packaging-apt-dpkg.py'
--- backends/packaging-apt-dpkg.py 2015-07-02 16:13:23 +0000
+++ backends/packaging-apt-dpkg.py 2015-07-13 22:32:23 +0000
@@ -16,6 +16,8 @@
16import hashlib16import hashlib
17import json17import json
1818
19from contextlib import closing
20
19import warnings21import warnings
20warnings.filterwarnings('ignore', 'apt API not stable yet', FutureWarning)22warnings.filterwarnings('ignore', 'apt API not stable yet', FutureWarning)
21import apt23import apt
@@ -24,9 +26,10 @@
24 from urllib import urlopen, quote, unquote26 from urllib import urlopen, quote, unquote
25 (pickle, urlopen, quote, unquote) # pyflakes27 (pickle, urlopen, quote, unquote) # pyflakes
26 URLError = IOError28 URLError = IOError
29 HTTPError = IOError
27except ImportError:30except ImportError:
28 # python 331 # python 3
29 from urllib.error import URLError32 from urllib.error import URLError, HTTPError
30 from urllib.request import urlopen33 from urllib.request import urlopen
31 from urllib.parse import quote, unquote34 from urllib.parse import quote, unquote
32 import pickle35 import pickle
@@ -47,6 +50,7 @@
47 self._virtual_mapping_obj = None50 self._virtual_mapping_obj = None
48 self._launchpad_base = 'https://api.launchpad.net/devel'51 self._launchpad_base = 'https://api.launchpad.net/devel'
49 self._archive_url = self._launchpad_base + '/%s/main_archive'52 self._archive_url = self._launchpad_base + '/%s/main_archive'
53 self._ppa_archive_url = self._launchpad_base + '/~%(user)s/+archive/%(distro)s/%(ppaname)s'
5054
51 def __del__(self):55 def __del__(self):
52 try:56 try:
@@ -88,14 +92,15 @@
88 self._apt_cache = apt.Cache(rootdir='/')92 self._apt_cache = apt.Cache(rootdir='/')
89 return self._apt_cache93 return self._apt_cache
9094
91 def _sandbox_cache(self, aptroot, apt_sources, fetchProgress):95 def _sandbox_cache(self, aptroot, apt_sources, fetchProgress, distro_name, release_codename, origins):
92 '''Build apt sandbox and return apt.Cache(rootdir=) (initialized lazily).96 '''Build apt sandbox and return apt.Cache(rootdir=) (initialized lazily).
9397
94 Clear the package selection on subsequent calls.98 Clear the package selection on subsequent calls.
95 '''99 '''
96 self._apt_cache = None100 self._apt_cache = None
97 if not self._sandbox_apt_cache:101 if not self._sandbox_apt_cache:
98 self._build_apt_sandbox(aptroot, apt_sources)102 self._build_apt_sandbox(aptroot, apt_sources, distro_name,
103 release_codename, origins)
99 rootdir = os.path.abspath(aptroot)104 rootdir = os.path.abspath(aptroot)
100 self._sandbox_apt_cache = apt.Cache(rootdir=rootdir)105 self._sandbox_apt_cache = apt.Cache(rootdir=rootdir)
101 try:106 try:
@@ -660,7 +665,8 @@
660665
661 def install_packages(self, rootdir, configdir, release, packages,666 def install_packages(self, rootdir, configdir, release, packages,
662 verbose=False, cache_dir=None,667 verbose=False, cache_dir=None,
663 permanent_rootdir=False, architecture=None):668 permanent_rootdir=False, architecture=None,
669 origins=None):
664 '''Install packages into a sandbox (for apport-retrace).670 '''Install packages into a sandbox (for apport-retrace).
665671
666 In order to work without any special permissions and without touching672 In order to work without any special permissions and without touching
@@ -689,6 +695,9 @@
689 the given architecture (as specified in a report's "Architecture"695 the given architecture (as specified in a report's "Architecture"
690 field). If not given it defaults to the host system's architecture.696 field). If not given it defaults to the host system's architecture.
691697
698 If origins is given, the sandbox will be created with apt data sources
699 for foreign origins.
700
692 Return a string with outdated packages, or None if all packages were701 Return a string with outdated packages, or None if all packages were
693 installed.702 installed.
694703
@@ -747,9 +756,14 @@
747 else:756 else:
748 fetchProgress = apt.progress.base.AcquireProgress()757 fetchProgress = apt.progress.base.AcquireProgress()
749 if not tmp_aptroot:758 if not tmp_aptroot:
750 cache = self._sandbox_cache(aptroot, apt_sources, fetchProgress)759 cache = self._sandbox_cache(aptroot, apt_sources, fetchProgress,
760 self.get_distro_name(),
761 self.current_release_codename,
762 origins)
751 else:763 else:
752 self._build_apt_sandbox(aptroot, apt_sources)764 self._build_apt_sandbox(aptroot, apt_sources,
765 self.get_distro_name(),
766 self.current_release_codename, origins)
753 cache = apt.Cache(rootdir=os.path.abspath(aptroot))767 cache = apt.Cache(rootdir=os.path.abspath(aptroot))
754 try:768 try:
755 cache.update(fetchProgress)769 cache.update(fetchProgress)
@@ -1203,7 +1217,67 @@
1203 return None1217 return None
12041218
1205 @classmethod1219 @classmethod
1206 def _build_apt_sandbox(klass, apt_root, apt_sources):1220 def create_ppa_source_from_origin(klass, origin, distro, release_codename):
1221 '''For an origin from a Launchpad PPA create sources.list content.
1222
1223 distro is the distribution for which content is being created e.g.
1224 ubuntu.
1225
1226 release_codename is the codename of the release for which content is
1227 being created e.g. trusty.
1228
1229 Return a string containing content suitable for writing to a sources.list
1230 file, or None if the origin is not a Launchpad PPA.
1231 '''
1232
1233 if origin.startswith("LP-PPA-"):
1234 components = origin.split("-")[2:]
1235 # If the PPA is unnamed, it will not appear in origin information
1236 # but is named ppa in Launchpad.
1237 try_ppa = True
1238 if len(components) == 1:
1239 components.append('ppa')
1240 try_ppa = False
1241
1242 index = 1
1243 while components[index:]:
1244 # For an origin we can't tell where the user name ends and the
1245 # PPA name starts, so split on each "-" until we find a PPA
1246 # that exists.
1247 user = str.join('-', components[0:index])
1248 ppa_name = str.join('-', components[index:len(components)])
1249 try:
1250 with closing(urlopen(apport.packaging._ppa_archive_url %
1251 {'user': user, 'distro': distro,
1252 'ppaname': ppa_name})) as response:
1253 response.read()
1254 except (URLError, HTTPError):
1255 index += 1
1256 if index == len(components):
1257 if try_ppa:
1258 components.append('ppa')
1259 try_ppa = False
1260 index = 2
1261 else:
1262 user = None
1263 continue
1264 break
1265 if user and ppa_name:
1266 ppa_line = 'deb http://ppa.launchpad.net/%s/%s/%s %s main' % \
1267 (user, ppa_name, distro, release_codename)
1268 debug_url = 'http://ppa.launchpad.net/%s/%s/%s/dists/%s/main/debug' % \
1269 (user, ppa_name, distro, release_codename)
1270 try:
1271 with closing(urlopen(debug_url)) as response:
1272 response.read()
1273 add_debug = ' main/debug'
1274 except (URLError, HTTPError):
1275 add_debug = ''
1276 return ppa_line + add_debug + '\ndeb-src' + ppa_line[3:] + '\n'
1277 return None
1278
1279 @classmethod
1280 def _build_apt_sandbox(klass, apt_root, apt_sources, distro_name, release_codename, origins):
1207 # pre-create directories, to avoid apt.Cache() printing "creating..."1281 # pre-create directories, to avoid apt.Cache() printing "creating..."
1208 # messages on stdout1282 # messages on stdout
1209 if not os.path.exists(os.path.join(apt_root, 'var', 'lib', 'apt')):1283 if not os.path.exists(os.path.join(apt_root, 'var', 'lib', 'apt')):
@@ -1225,6 +1299,46 @@
1225 with open(os.path.join(apt_root, 'etc', 'apt', 'sources.list'), 'w') as dest:1299 with open(os.path.join(apt_root, 'etc', 'apt', 'sources.list'), 'w') as dest:
1226 dest.write(src.read())1300 dest.write(src.read())
12271301
1302 if origins:
1303 source_list_content = ''
1304 # map an origin to a Launchpad username and PPA name
1305 origin_data = {}
1306 for origin in origins:
1307 # apport's report format uses unknown for packages w/o an origin
1308 if origin == 'unknown':
1309 continue
1310 origin_path = None
1311 if os.path.isdir(apt_sources + '.d'):
1312 # check to see if there is a sources.list file for the origin,
1313 # if there isn't try using a sources.list file w/o LP-PPA-
1314 origin_path = os.path.join(apt_sources + '.d', origin + '.list')
1315 if not os.path.exists(origin_path) and 'LP-PPA' in origin:
1316 origin_path = os.path.join(apt_sources + '.d',
1317 origin.strip('LP-PPA-') + '.list')
1318 if not os.path.exists(origin_path):
1319 origin_path = None
1320 elif not os.path.exists(origin_path):
1321 origin_path = None
1322 if origin_path:
1323 with open(origin_path) as src_ext:
1324 source_list_content = src_ext.read()
1325 else:
1326 source_list_content = klass.create_ppa_source_from_origin(origin, distro_name, release_codename)
1327 if source_list_content:
1328 with open(os.path.join(apt_root, 'etc', 'apt',
1329 'sources.list.d', origin + '.list'), 'a') as dest:
1330 dest.write(source_list_content)
1331 for line in source_list_content.splitlines():
1332 if line.startswith('#'):
1333 continue
1334 if 'ppa.launchpad.net' not in line:
1335 continue
1336 user = line.split()[1].split('/')[3]
1337 ppa = line.split()[1].split('/')[4]
1338 origin_data[origin] = (user, ppa)
1339 else:
1340 apport.warning("Could not find or create source config for %s" % origin)
1341
1228 # install apt keyrings; prefer the ones from the config dir, fall back1342 # install apt keyrings; prefer the ones from the config dir, fall back
1229 # to system1343 # to system
1230 trusted_gpg = os.path.join(os.path.dirname(apt_sources), 'trusted.gpg')1344 trusted_gpg = os.path.join(os.path.dirname(apt_sources), 'trusted.gpg')
@@ -1244,6 +1358,36 @@
1244 else:1358 else:
1245 os.makedirs(trusted_d)1359 os.makedirs(trusted_d)
12461360
1361 # install apt keyrings for PPAs
1362 if origins and source_list_content:
1363 for origin, (ppa_user, ppa_name) in origin_data.items():
1364 ppa_archive_url = apport.packaging._ppa_archive_url % \
1365 {'user': quote(ppa_user), 'distro': distro_name,
1366 'ppaname': quote(ppa_name)}
1367 ppa_info = apport.packaging.json_request(ppa_archive_url)
1368 if not ppa_info:
1369 continue
1370 try:
1371 signing_key_fingerprint = ppa_info['signing_key_fingerprint']
1372 except IndexError:
1373 apport.warning("Error: can't find signing_key_fingerprint at %s"
1374 % ppa_archive_url)
1375 continue
1376 argv = ['gpg', '--no-options',
1377 '--no-default-keyring',
1378 '--no-auto-check-trustdb',
1379 '--keyring',
1380 os.path.join(trusted_d,
1381 '%s.gpg' % origin),
1382 ]
1383 argv += ['--quiet', '--batch',
1384 '--keyserver', 'hkp://keyserver.ubuntu.com:80/',
1385 '--recv', signing_key_fingerprint]
1386 if subprocess.call(argv) != 0:
1387 apport.warning('Unable to import key for %s' %
1388 ppa_archive_url)
1389 pass
1390
1247 @classmethod1391 @classmethod
1248 def _deb_version(klass, pkg):1392 def _deb_version(klass, pkg):
1249 '''Return the version of a .deb file'''1393 '''Return the version of a .deb file'''
12501394
=== modified file 'bin/apport-retrace'
--- bin/apport-retrace 2015-05-29 21:53:42 +0000
+++ bin/apport-retrace 2015-07-13 22:32:23 +0000
@@ -53,6 +53,8 @@
53 help=_('Report download/install progress when installing packages into sandbox'))53 help=_('Report download/install progress when installing packages into sandbox'))
54 argparser.add_argument('--timestamps', action='store_true',54 argparser.add_argument('--timestamps', action='store_true',
55 help=_('Prepend timestamps to log messages, for batch operation'))55 help=_('Prepend timestamps to log messages, for batch operation'))
56 argparser.add_argument('--dynamic-origins', action='store_true',
57 help=_('Create and use third-party repositories from origins specified in reports'))
56 argparser.add_argument('-C', '--cache', metavar='DIR',58 argparser.add_argument('-C', '--cache', metavar='DIR',
57 help=_('Cache directory for packages downloaded in the sandbox'))59 help=_('Cache directory for packages downloaded in the sandbox'))
58 argparser.add_argument('--sandbox-dir', metavar='DIR',60 argparser.add_argument('--sandbox-dir', metavar='DIR',
@@ -295,7 +297,8 @@
295if options.sandbox:297if options.sandbox:
296 sandbox, cache, outdated_msg = apport.sandboxutils.make_sandbox(298 sandbox, cache, outdated_msg = apport.sandboxutils.make_sandbox(
297 report, options.sandbox, options.cache, options.sandbox_dir,299 report, options.sandbox, options.cache, options.sandbox_dir,
298 options.extra_package, options.verbose)300 options.extra_package, options.verbose, log_timestamps,
301 options.dynamic_origins)
299else:302else:
300 sandbox = None303 sandbox = None
301 outdated_msg = None304 outdated_msg = None
302305
=== modified file 'test/test_backend_apt_dpkg.py'
--- test/test_backend_apt_dpkg.py 2015-06-29 13:31:02 +0000
+++ test/test_backend_apt_dpkg.py 2015-07-13 22:32:23 +0000
@@ -953,7 +953,8 @@
953 self._setup_foonux_config()953 self._setup_foonux_config()
954 out_dir = os.path.join(self.workdir, 'out')954 out_dir = os.path.join(self.workdir, 'out')
955 os.mkdir(out_dir)955 os.mkdir(out_dir)
956 impl._build_apt_sandbox(self.rootdir, os.path.join(self.configdir, 'Foonux 1.2', 'sources.list'))956 impl._build_apt_sandbox(self.rootdir, os.path.join(self.configdir, 'Foonux 1.2', 'sources.list'),
957 'ubuntu', 'trusty', origins=None)
957 res = impl.get_source_tree('base-files', out_dir, sandbox=self.rootdir,958 res = impl.get_source_tree('base-files', out_dir, sandbox=self.rootdir,
958 apt_update=True)959 apt_update=True)
959 self.assertTrue(os.path.isdir(os.path.join(res, 'debian')))960 self.assertTrue(os.path.isdir(os.path.join(res, 'debian')))
@@ -967,7 +968,8 @@
967 self._setup_foonux_config()968 self._setup_foonux_config()
968 out_dir = os.path.join(self.workdir, 'out')969 out_dir = os.path.join(self.workdir, 'out')
969 os.mkdir(out_dir)970 os.mkdir(out_dir)
970 impl._build_apt_sandbox(self.rootdir, os.path.join(self.configdir, 'Foonux 1.2', 'sources.list'))971 impl._build_apt_sandbox(self.rootdir, os.path.join(self.configdir, 'Foonux 1.2', 'sources.list'),
972 'ubuntu', 'trusty', origins=None)
971 res = impl.get_source_tree('debian-installer', out_dir, version='20101020ubuntu318.16',973 res = impl.get_source_tree('debian-installer', out_dir, version='20101020ubuntu318.16',
972 sandbox=self.rootdir, apt_update=True)974 sandbox=self.rootdir, apt_update=True)
973 self.assertTrue(os.path.isdir(os.path.join(res, 'debian')))975 self.assertTrue(os.path.isdir(os.path.join(res, 'debian')))
@@ -976,8 +978,86 @@
976 self.assertTrue(res.endswith('/debian-installer-20101020ubuntu318.16'),978 self.assertTrue(res.endswith('/debian-installer-20101020ubuntu318.16'),
977 'unexpected version: ' + res.split('/')[-1])979 'unexpected version: ' + res.split('/')[-1])
978980
979 def _setup_foonux_config(self, updates=False, release='trusty'):981 @unittest.skipUnless(_has_internet(), 'online test')
980 '''Set up directories and configuration for install_packages()'''982 def test_create_sources_for_a_named_ppa(self):
983 '''Add sources.list entries for a named PPA.'''
984 ppa = 'LP-PPA-daisy-pluckers-daisy-seeds'
985 self._setup_foonux_config()
986 impl._build_apt_sandbox(self.rootdir, os.path.join(self.configdir, 'Foonux 1.2', 'sources.list'),
987 'ubuntu', 'trusty', origins=[ppa])
988 with open(os.path.join(self.rootdir, 'etc', 'apt', 'sources.list.d', ppa + '.list')) as f:
989 sources = f.read().splitlines()
990 self.assertIn('deb http://ppa.launchpad.net/daisy-pluckers/daisy-seeds/ubuntu trusty main main/debug', sources)
991 self.assertIn('deb-src http://ppa.launchpad.net/daisy-pluckers/daisy-seeds/ubuntu trusty main', sources)
992
993 d = subprocess.Popen(['gpg', '--no-options', '--no-default-keyring',
994 '--no-auto-check-trustdb', '--trust-model',
995 'always', '--batch', '--list-keys', '--keyring',
996 os.path.join(self.rootdir, 'etc', 'apt', 'trusted.gpg.d', 'LP-PPA-daisy-pluckers-daisy-seeds.gpg')],
997 stdout=subprocess.PIPE)
998 apt_keys = d.communicate()[0].decode()
999 assert d.returncode == 0
1000 self.assertIn('Launchpad PPA for Daisy Pluckers', apt_keys)
1001
1002 @unittest.skipUnless(_has_internet(), 'online test')
1003 def test_create_sources_for_an_unnamed_ppa(self):
1004 '''Add sources.list entries for an unnamed PPA.'''
1005 ppa = 'LP-PPA-brian-murray'
1006 self._setup_foonux_config()
1007 impl._build_apt_sandbox(self.rootdir, os.path.join(self.configdir, 'Foonux 1.2', 'sources.list'),
1008 'ubuntu', 'trusty', origins=[ppa])
1009 with open(os.path.join(self.rootdir, 'etc', 'apt', 'sources.list.d', ppa + '.list')) as f:
1010 sources = f.read().splitlines()
1011 self.assertIn('deb http://ppa.launchpad.net/brian-murray/ppa/ubuntu trusty main', sources)
1012 self.assertIn('deb-src http://ppa.launchpad.net/brian-murray/ppa/ubuntu trusty main', sources)
1013
1014 d = subprocess.Popen(['gpg', '--no-options', '--no-default-keyring',
1015 '--no-auto-check-trustdb', '--trust-model',
1016 'always', '--batch', '--list-keys', '--keyring',
1017 os.path.join(self.rootdir, 'etc', 'apt', 'trusted.gpg.d', 'LP-PPA-brian-murray.gpg')],
1018 stdout=subprocess.PIPE)
1019 apt_keys = d.communicate()[0].decode()
1020 assert d.returncode == 0
1021 self.assertIn('Launchpad PPA for Brian Murray', apt_keys)
1022
1023 def test_use_sources_for_a_ppa(self):
1024 '''Use a sources.list.d file for a PPA.'''
1025 ppa = 'fooser-bar-ppa'
1026 self._setup_foonux_config(ppa=True)
1027 impl._build_apt_sandbox(self.rootdir, os.path.join(self.configdir, 'Foonux 1.2', 'sources.list'),
1028 'ubuntu', 'trusty', origins=['LP-PPA-%s' % ppa])
1029 with open(os.path.join(self.rootdir, 'etc', 'apt', 'sources.list.d', ppa + '.list')) as f:
1030 sources = f.read().splitlines()
1031 self.assertIn('deb http://ppa.launchpad.net/fooser/bar-ppa/ubuntu trusty main main/debug', sources)
1032 self.assertIn('deb-src http://ppa.launchpad.net/fooser/bar-ppa/ubuntu trusty main', sources)
1033
1034 @unittest.skipUnless(_has_internet(), 'online test')
1035 def test_install_package_from_a_ppa(self):
1036 '''Install a package from a PPA.'''
1037 ppa = 'LP-PPA-brian-murray'
1038 self._setup_foonux_config()
1039 obsolete = impl.install_packages(self.rootdir, self.configdir, 'Foonux 1.2',
1040 [('apport',
1041 '2.14.1-0ubuntu3.7~ppa4')
1042 ], False, self.cachedir, origins=[ppa])
1043
1044 self.assertEqual(obsolete, '')
1045
1046 def sandbox_ver(pkg):
1047 with gzip.open(os.path.join(self.rootdir, 'usr/share/doc', pkg,
1048 'changelog.Debian.gz')) as f:
1049 return f.readline().decode().split()[1][1:-1]
1050
1051 self.assertEqual(sandbox_ver('apport'),
1052 '2.14.1-0ubuntu3.7~ppa4')
1053
1054 def _setup_foonux_config(self, updates=False, release='trusty', ppa=False):
1055 '''Set up directories and configuration for install_packages()
1056
1057 If ppa is True, then a sources.list file for a PPA will be created
1058 in sources.list.d used to test copying of a sources.list file to a
1059 sandbox.
1060 '''
9811061
982 self.cachedir = os.path.join(self.workdir, 'cache')1062 self.cachedir = os.path.join(self.workdir, 'cache')
983 self.rootdir = os.path.join(self.workdir, 'root')1063 self.rootdir = os.path.join(self.workdir, 'root')
@@ -994,6 +1074,11 @@
994 f.write('deb http://archive.ubuntu.com/ubuntu/ %s-updates main\n' % release)1074 f.write('deb http://archive.ubuntu.com/ubuntu/ %s-updates main\n' % release)
995 f.write('deb-src http://archive.ubuntu.com/ubuntu/ %s-updates main\n' % release)1075 f.write('deb-src http://archive.ubuntu.com/ubuntu/ %s-updates main\n' % release)
996 f.write('deb http://ddebs.ubuntu.com/ %s-updates main\n' % release)1076 f.write('deb http://ddebs.ubuntu.com/ %s-updates main\n' % release)
1077 if ppa:
1078 os.mkdir(os.path.join(self.configdir, 'Foonux 1.2', 'sources.list.d'))
1079 with open(os.path.join(self.configdir, 'Foonux 1.2', 'sources.list.d', 'fooser-bar-ppa.list'), 'w') as f:
1080 f.write('deb http://ppa.launchpad.net/fooser/bar-ppa/ubuntu %s main main/debug\n' % release)
1081 f.write('deb-src http://ppa.launchpad.net/fooser/bar-ppa/ubuntu %s main\n' % release)
997 os.mkdir(os.path.join(self.configdir, 'Foonux 1.2', 'armhf'))1082 os.mkdir(os.path.join(self.configdir, 'Foonux 1.2', 'armhf'))
998 with open(os.path.join(self.configdir, 'Foonux 1.2', 'armhf', 'sources.list'), 'w') as f:1083 with open(os.path.join(self.configdir, 'Foonux 1.2', 'armhf', 'sources.list'), 'w') as f:
999 f.write('deb http://ports.ubuntu.com/ %s main\n' % release)1084 f.write('deb http://ports.ubuntu.com/ %s main\n' % release)

Subscribers

People subscribed via source and target branches