~launchpad-pqm/launchpad/devel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
Soyuz Upload Test
-----------------

This test will:

  * Upload packages
  * Import gpg key for katie
  * Register gpg key for katie
  * Register the katie user in the right team
  * Turn on the test keyserver
  * Include the non_free component in the database
  * Run process-upload.py
  * Check result
  * Mark packages as ACCEPTED
  * Runs process-accepted.py
  * Check results
  * Cleanup


Uploading Packages
------------------


First, let's create a temporary directory where we'll put
uploaded files in.

    >>> import os
    >>> import shutil
    >>> import tempfile
    >>> temp_dir = tempfile.mkdtemp()
    >>> incoming_dir = os.path.join(temp_dir, "incoming")
    >>> accepted_dir = os.path.join(temp_dir, "accepted")
    >>> rejected_dir = os.path.join(temp_dir, "rejected")
    >>> failed_dir = os.path.join(temp_dir, "failed")
    >>> os.mkdir(incoming_dir)


Now let's copy the files into separate upload directories, as if they'd
been uploaded over FTP.

    >>> from lp.services.config import config
    >>> from lp.archiveuploader.tagfiles import parse_tagfile
    >>> import glob
    >>> test_files_dir = os.path.join(config.root,
    ...                               "lib/lp/soyuz/scripts/"
    ...                               "tests/upload_test_files/")
    ...
    >>> changes = sorted(glob.glob(test_files_dir + "*.changes"))
    >>> sent_filenames = []
    >>> uploads = []
    >>> package_names = []

    >>> seq = 1
    >>> for changes_filepath in changes:
    ...     tf = parse_tagfile(changes_filepath)
    ...
    ...     if tf.has_key("Source"):
    ...         package_names.append(tf["Source"])
    ...
    ...     send_filepaths = [changes_filepath]
    ...     if tf.has_key("Files"):
    ...         send_filepaths.extend(
    ...             [os.path.join(test_files_dir, line.split()[-1])
    ...              for line in tf["Files"].splitlines() if line])
    ...
    ...     sent_filenames.extend(
    ...         os.path.basename(filepath) for filepath in send_filepaths)
    ...
    ...     upload_dir = os.path.join(
    ...         incoming_dir, 'upload-%06d' % seq, 'ubuntutest')
    ...     os.makedirs(upload_dir)
    ...
    ...     for filepath in send_filepaths:
    ...         shutil.copyfile(
    ...             filepath,
    ...             os.path.join(upload_dir, os.path.basename(filepath)))
    ...
    ...     uploads.append(send_filepaths)
    ...     seq += 1

Check that what we've just uploaded (everything in test_files_dir) is
what we were expecting to have uploaded.

    >>> package_names
    ['drdsl', 'etherwake']

At that point we must have a bunch of directories in the upload base
directory named upload-XXXXXX, as would result from several FTP
sessions.  Below we ensure that, and also that the content of these
files match the uploaded ones.

    >>> import hashlib
    >>> def get_md5(filename):
    ...     return hashlib.md5(open(filename).read()).digest()

    >>> def get_upload_dir(num, dir=incoming_dir):
    ...     """Return the path to the upload, if found in the dir."""
    ...     for upload_dir in os.listdir(dir):
    ...         if upload_dir.endswith("%06d" % num):
    ...             return os.path.join(dir, upload_dir)
    ...     return None

    >>> def find_upload_dir(num):
    ...     """Return a tuple (result, path) for the numbered upload."""
    ...     for name, dir in (("incoming", incoming_dir),
    ...         ("accepted", accepted_dir), ("rejected", rejected_dir),
    ...         ("failed", failed_dir)):
    ...         result = get_upload_dir(num, dir)
    ...         if result is not None:
    ...             return (name, result)
    ...     return (None, None)

    >>> def find_upload_dir_result(num):
    ...     """Return the result for the numbered upload."""
    ...     return find_upload_dir(num)[0]

    >>> def find_upload_dir_path(num):
    ...     """Return the path of the numbered upload."""
    ...     return find_upload_dir(num)[1]

    >>> for i, sent_filenames in enumerate(uploads):
    ...     upload_dir = get_upload_dir(i + 1)
    ...     distro_upload_dir = os.path.join(upload_dir, 'ubuntutest')
    ...     assert len(os.listdir(distro_upload_dir)) == len(sent_filenames)
    ...     for filename in sent_filenames:
    ...         upload_filename = os.path.join(distro_upload_dir,
    ...                                        os.path.basename(filename))
    ...         assert os.path.isfile(upload_filename)
    ...         assert get_md5(filename) == get_md5(upload_filename)

Finally, we'll just create an entirely empty upload folder. We rely for
our tests on a poppy-like naming system, ie. that the upload folder
end with 000004 (being our fourth upload).

    >>> os.mkdir("%s/fake_upload_000004" % incoming_dir)


Processing Uploads
------------------

Before asking the system to process the upload, we must prepare the
database to receive it. This consists mainly of adding the katie
user, since that's the email used in the Changed-By field for the
.changes files we are going to process, and the ftpmaster@canonical.com
GPG key, since that's the one used to sign the .changes file.

We don't have to check the .dsc file, since we're using the 'sync'
policy in process-upload.py.

# XXX: gustavo 2005-12-10
#     It might be interesting to move these entries into the sample data
#     rather than leaving it here. On the other hand, it's nice to have
#     it here as we have a good reference of what the uploading
#     procedure depends upon.

So, load the GPG key:

    >>> from zope.component import getUtility
    >>> from lp.services.gpg.interfaces import IGPGHandler
    >>> from lp.testing.gpgkeys import gpgkeysdir
    >>> gpg_handler = getUtility(IGPGHandler)
    >>> key_path = os.path.join(gpgkeysdir, 'ftpmaster@canonical.com.pub')
    >>> key_data = open(key_path).read()
    >>> key = gpg_handler.importPublicKey(key_data)
    >>> assert key is not None
    >>> print key.fingerprint
    33C0A61893A5DC5EB325B29E415A12CAC2F30234


Create the katie user and register it in a team that is allowed to
do uploads:

    >>> from lp.services.identity.interfaces.emailaddress import (
    ...     IEmailAddressSet)
    >>> from lp.registry.interfaces.gpg import (
    ...     GPGKeyAlgorithm,
    ...     IGPGKeySet,
    ...     )
    >>> from lp.registry.interfaces.person import (
    ...     IPersonSet,
    ...     PersonCreationRationale,
    ...     )
    >>> name, address = "Katie", "katie@rockhopper.ubuntu.com"
    >>> user = getUtility(IPersonSet).ensurePerson(
    ...     address, name, PersonCreationRationale.OWNER_CREATED_LAUNCHPAD)
    >>> assert user is not None
    >>> email = getUtility(IEmailAddressSet).getByEmail(address)
    >>> user.validateAndEnsurePreferredEmail(email)

    >>> uploader_team = getUtility(IPersonSet).getByName("ubuntu-team")
    >>> assert uploader_team is not None

    >>> login("foo.bar@canonical.com")
    >>> unused = uploader_team.addMember(
    ...     user, reviewer=uploader_team.teamowner)
    >>> login("test@canonical.com")


Assign the loaded GPG key to the katie user.

    >>> key_set = getUtility(IGPGKeySet)
    >>> user_key = key_set.new(
    ...     ownerID=user.id, keyid=key.keyid, fingerprint=key.fingerprint,
    ...     algorithm=GPGKeyAlgorithm.items[key.algorithm],
    ...       keysize=key.keysize, can_encrypt=key.can_encrypt, active=True)


Now we want to turn on the test key server to provide the key we
just imported. Remember that process-upload.py is running as
a different process.

    >>> from lp.testing.keyserver import KeyServerTac
    >>> keyserver = KeyServerTac()
    >>> keyserver.setUp()


Include non-free in the database. This will be done by the
NascentUpload in the 'sync' policy in the future.

    >>> from lp.soyuz.interfaces.component import IComponentSet
    >>> component_set = getUtility(IComponentSet)
    >>> non_free = component_set.new("non-free")
    >>> contrib = component_set.new("contrib")
    >>> import transaction
    >>> transaction.commit()

Now we are ready to process the uploaded packages.
This is done by running process-upload.py on each upload directory.

    >>> import subprocess, sys
    >>> script = os.path.join(config.root, "scripts/process-upload.py")

First, we will test process-upload's -J option, which limits which uploads
should be processed. We'll do this by locating and uploading initially
just upload number 1.

    >>> upload_dir_1_path = get_upload_dir(1)
    >>> upload_dir_1_name = os.path.basename(upload_dir_1_path)
    >>> process = subprocess.Popen([
    ...     sys.executable, script, "--no-mails", "-vv",
    ...     "-C", "sync", "-J", upload_dir_1_name, temp_dir,
    ...     ],
    ...     stdout=subprocess.PIPE,
    ...     stderr=subprocess.PIPE)
    >>> stdout, stderr = process.communicate()
    >>> process.returncode
    0

Check the four uploads are all where we expect - number 1 in rejected,
the other three still in incoming.

    >>> for i in range(4):
    ...     find_upload_dir_result(i + 1)
    'rejected'
    'incoming'
    'incoming'
    'incoming'


Now continue with the real upload.

    >>> process = subprocess.Popen([
    ...     sys.executable, script, "--no-mails", "-vv",
    ...     "-C", "sync", temp_dir,
    ...     ],
    ...     stdout=subprocess.PIPE,
    ...     stderr=subprocess.PIPE)

    >>> stdout, stderr = process.communicate()
    >>> if process.returncode != 0:
    ...     print stdout
    ...     print stderr


Let's check if packages were uploaded correctly.

    >>> from lp.registry.model.sourcepackagename import SourcePackageName
    >>> from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease
    >>> from pprint import pprint
    >>> spn = SourcePackageName.selectOneBy(name="drdsl")
    >>> spn.name
    u'drdsl'
    >>> spr = SourcePackageRelease.selectOneBy(sourcepackagenameID=spn.id)
    >>> spr.title
    u'drdsl - 1.2.0-0ubuntu1'
    >>> spr.name
    u'drdsl'
    >>> spr.version
    u'1.2.0-0ubuntu1'
    >>> spr.component.name
    u'multiverse'
    >>> spr.section.name
    u'comm'
    >>> spr.maintainer.displayname
    u'Matthias Klose'
    >>> pprint(sorted([sprf.libraryfile.filename for sprf in spr.files]))
    [u'drdsl_1.2.0-0ubuntu1.diff.gz',
     u'drdsl_1.2.0-0ubuntu1.dsc',
     u'drdsl_1.2.0.orig.tar.gz']
    >>> spr.format.name
    'DPKG'
    >>> spr.urgency.name
    'LOW'
    >>> spr.upload_distroseries.name
    u'breezy-autotest'


Same thing for etherwake:

    >>> spn = SourcePackageName.selectOneBy(name="etherwake")
    >>> spn.name
    u'etherwake'
    >>> spr = SourcePackageRelease.selectOneBy(sourcepackagenameID=spn.id)
    >>> spr.title
    u'etherwake - 1.08-1'
    >>> spr.name
    u'etherwake'
    >>> spr.version
    u'1.08-1'
    >>> spr.component.name
    u'universe'
    >>> spr.section.name
    u'net'
    >>> spr.maintainer.displayname
    u'Alain Schroeder'
    >>> pprint(sorted([sprf.libraryfile.filename for sprf in spr.files]))
    [u'etherwake_1.08-1.diff.gz',
     u'etherwake_1.08-1.dsc',
     u'etherwake_1.08.orig.tar.gz']
    >>> spr.format.name
    'DPKG'
    >>> spr.urgency.name
    'LOW'
    >>> spr.upload_distroseries.name
    u'breezy-autotest'


Check the four uploads all ended up where we expected.

    >>> for i in range(0, 4):
    ...     find_upload_dir_result(i + 1)
    'rejected'
    'failed'

Also check the upload folders contain all the files we uploaded.

# XXX cprov 2006-12-06: hardcoded 'ubuntutest' directory is a hack see
# above around line 313.

    >>> for i, sent_filenames in enumerate(uploads):
    ...     upload_dir = find_upload_dir_path(i + 1)
    ...     if upload_dir is None:
    ...         continue
    ...     distro_upload_dir = os.path.join(upload_dir, 'ubuntutest')
    ...     assert len(os.listdir(distro_upload_dir)) == len(sent_filenames)
    ...     for filename in sent_filenames:
    ...         upload_filename = os.path.join(distro_upload_dir,
    ...                                        os.path.basename(filename))
    ...         assert os.path.isfile(upload_filename)
    ...         assert get_md5(filename) == get_md5(upload_filename)


Now let's see if all of the valid uploads are in the Upload queue marked
as NEW and RELEASE.

    >>> from lp.soyuz.model.queue import PackageUploadSource
    >>> for name in package_names:
    ...     print name
    ...     spn = SourcePackageName.selectOneBy(name=name)
    ...     spr = SourcePackageRelease.selectOneBy(sourcepackagenameID=spn.id)
    ...     us = PackageUploadSource.selectOneBy(
    ...         sourcepackagereleaseID=spr.id)
    ...     assert us.packageupload.status.name == 'NEW'
    ...     assert us.packageupload.pocket.name == 'RELEASE'
    drdsl
    etherwake


Processing NEW Items
----------------------

The processing of NEW-queue-entries checks the integrity of uploads
candidates and promote them to ACCEPTED, the failures are kept
as NEW

    >>> from lp.registry.interfaces.distribution import IDistributionSet
    >>> from lp.soyuz.enums import PackageUploadStatus
    >>> from lp.soyuz.interfaces.queue import (
    ...     QueueInconsistentStateError)

Since we landed correct security adapters for Upload,
we need to perform further actions logged in as an admins, which have
launchpad.Edit on the records:

    >>> from lp.testing import login
    >>> login("foo.bar@canonical.com")

    >>> distro = getUtility(IDistributionSet).getByName('ubuntutest')
    >>> series = distro['breezy-autotest']

We use getPackageUploads to inspect the current NEW queue and accept items.

    >>> queue_items = series.getPackageUploads(status=PackageUploadStatus.NEW)
    >>> L = []
    >>> for queue_item in queue_items:
    ...      try:
    ...          queue_item.setAccepted()
    ...      except QueueInconsistentStateError, e:
    ...          L.append("%s %s" % (queue_item.sourcepackagerelease.name, e))
    ...      else:
    ...          L.append("%s %s" % (queue_item.sourcepackagerelease.name,
    ...                              'ACCEPTED'))
    >>> L.sort()
    >>> print "\n".join(L)
    drdsl ACCEPTED
    etherwake ACCEPTED

Now we process the accepted queue items, one more time.

    >>> transaction.commit()
    >>> script = os.path.join(config.root, "scripts", "process-accepted.py")
    >>> process = subprocess.Popen(
    ...     [sys.executable, script, "ubuntutest", "-q"])
    >>> process.wait()
    0

These packages must now be in the publishing history. Let's check it.

    >>> from lp.soyuz.model.publishing import (
    ...    SourcePackagePublishingHistory as SSPPH)
    >>> package_names.sort()
    >>> for name in package_names:
    ...     spn = SourcePackageName.selectOneBy(name=name)
    ...     spr = SourcePackageRelease.selectOneBy(sourcepackagenameID=spn.id)
    ...     sspph = SSPPH.selectOneBy(sourcepackagereleaseID=spr.id)
    ...     if sspph:
    ...         print name, sspph.status.title
    ...     else:
    ...         print name, 'not Published'
    drdsl Pending
    etherwake Pending


Invoke Publisher script against the 'ubuntutest' distribution:

    >>> script = os.path.join(config.root, "scripts", "publish-distro.py")
    >>> process = subprocess.Popen([sys.executable, script, "-vvCq",
    ...                             "-d", "ubuntutest"],
    ...                            stdout=subprocess.PIPE,
    ...                            stderr=subprocess.PIPE)
    >>> stdout, stderr = process.communicate()
    >>> print stdout
    <BLANKLINE>
    >>> print stderr
    DEBUG Enabled by DEFAULT section
    INFO  Creating lockfile: ...
    DEBUG     Distribution: ubuntutest
    ...
    DEBUG   Added /var/tmp/archive/ubuntutest/pool/universe/e/etherwake/etherwake_1.08.orig.tar.gz from library
    DEBUG   Added /var/tmp/archive/ubuntutest/pool/universe/e/etherwake/etherwake_1.08-1.diff.gz from library
    DEBUG   Added /var/tmp/archive/ubuntutest/pool/universe/e/etherwake/etherwake_1.08-1.dsc from library
    ...

    >>> transaction.commit()

Check if the 'etherwake' source package was correctly published and is
in the filesystem archive, we are looking for the DSC, the gzipped
original source and the gzipped package diff:

    >>> len(os.listdir(
    ...     "/var/tmp/archive/ubuntutest/pool/universe/e/etherwake"))
    3

Check the generation of a correct Sources tag file for the main
component of ubuntutest/breezy-autotest, containing the only the
required entry for 'etherwake':

    >>> sources = open(
    ...    "/var/tmp/archive/ubuntutest/dists/breezy-autotest/universe/source"
    ...    "/Sources").read()
    >>> import re
    >>> sources = re.subn(r'(?sm)^Checksums-.*?(?=^[^ ])', '', sources)[0]
    >>> print sources + '\nEND'
    Package: etherwake
    Binary: etherwake
    Version: 1.08-1
    Section: universe/net
    Maintainer: Alain Schroeder <...@...org>
    Build-Depends: debhelper (>> 2.0)
    Architecture: any
    Standards-Version: 3.5.10.0
    Format: 1.0
    Directory: pool/universe/e/etherwake
    Files:
     f13711c5b8261fbb77b43ae0e8ba9360 566 etherwake_1.08-1.dsc
     c2dc10f98bac012b900fd0b46721fc80 4455 etherwake_1.08.orig.tar.gz
     95c1e89e3ad7bc8740793bdf7aeb7334 4145 etherwake_1.08-1.diff.gz
    <BLANKLINE>
    <BLANKLINE>
    END

# XXX: maxb 2010-04-15 bug=563503
# The regex munging above can be removed once the tests no longer need to pass
# on Karmic and earlier.

Now we invoke changeOverride on just published etherwake, moving it to
component 'multiverse'.

    >>> ubuntutest = getUtility(IDistributionSet)['ubuntutest']
    >>> breezy_autotest = ubuntutest['breezy-autotest']
    >>> etherwake = breezy_autotest.getSourcePackage('etherwake')
    >>> etherwake_drspr = etherwake.currentrelease
    >>> override = etherwake_drspr.current_published.changeOverride(
    ...     new_component=getUtility(IComponentSet)['multiverse'])

Check if we have new pending publishing record as expected

    >>> for pub in SSPPH.selectBy(
    ...    sourcepackagereleaseID=etherwake_drspr.sourcepackagerelease.id,
    ...    orderBy=['id']):
    ...    print pub.status.name, pub.component.name, pub.pocket.name
    PUBLISHED universe RELEASE
    PENDING multiverse RELEASE

Force database changes, so they can be used by the external script properly.

    >>> transaction.commit()

Invoke Publisher script again to land our changes in the archive

    >>> script = os.path.join(config.root, "scripts", "publish-distro.py")
    >>> process = subprocess.Popen([sys.executable, script, "-vvCq",
    ...                             "-d", "ubuntutest"],
    ...                            stdout=subprocess.PIPE,
    ...                            stderr=subprocess.PIPE)
    >>> stdout, stderr = process.communicate()
    >>> process.returncode
    0

Check careful publishing took place, as requested with -C. In careful
publishing mode, publish-distro will attempt to publish files which are
already marked as published in the database and, if the files are
already on disk, verify the contents are as expected.

    >>> print stderr
    DEBUG Enabled by DEFAULT section
    INFO  Creating lockfile: ...
    DEBUG     Distribution: ubuntutest
    ...
    DEBUG   /var/tmp/archive/ubuntutest/pool/universe/e/etherwake/etherwake_1.08.orig.tar.gz is already in pool with the same content.
    DEBUG   /var/tmp/archive/ubuntutest/pool/universe/e/etherwake/etherwake_1.08-1.diff.gz is already in pool with the same content.
    DEBUG   /var/tmp/archive/ubuntutest/pool/universe/e/etherwake/etherwake_1.08-1.dsc is already in pool with the same content.
    ...

Check the publishing history again

    >>> for pub in SSPPH.selectBy(
    ...    sourcepackagereleaseID=etherwake_drspr.sourcepackagerelease.id,
    ...    orderBy=['id']):
    ...    print pub.status.name, pub.component.name, pub.pocket.name
    SUPERSEDED universe RELEASE
    PUBLISHED multiverse RELEASE

Check if the package was moved properly to the component 'multiverse':

    >>> main_sources = open(
    ...     "/var/tmp/archive/ubuntutest/dists/breezy-autotest"
    ...     "/main/source/Sources").read()
    >>> print main_sources + '\nEND'
    <BLANKLINE>
    END

    >>> multiverse_sources = open(
    ...     "/var/tmp/archive/ubuntutest/dists/breezy-autotest"
    ...     "/multiverse/source/Sources").read()
    >>> print multiverse_sources + '\nEND'
    Package: drdsl
    ...
    Package: etherwake
    ...
    END

Release File
------------

The publish-distro.py script will write an appropriate Release file
containing the suite in question and a list of checksums (MD5, SHA1
and SHA256) for each index published.

# XXX cprov 2006-12-13: trailing space on Architectures is a side-effect
# caused by the absence of published binaries in this suite. It should
# no happen in real conditions.

    >>> releasefile_contents = open("/var/tmp/archive/ubuntutest/dists/"
    ...                             "breezy-autotest/Release").read()
    >>> print releasefile_contents + '\nEND' #doctest: -NORMALIZE_WHITESPACE
    Origin: ubuntutest
    Label: ubuntutest
    Suite: breezy-autotest
    Version: 6.6.6
    Codename: breezy-autotest
    Date: ...
    Architectures:
    Components: main restricted universe multiverse
    Description: ubuntutest Breezy Badger Autotest 6.6.6
    MD5Sum:
     a5e5742a193740f17705c998206e18b6              114 main/source/Release
    ...
    SHA1:
     6222b7e616bcc20a32ec227254ad9de8d4bd5557              114 main/source/Release
    ...
    SHA256:
     297125e9b0f5da85552691597c9c4920aafd187e18a4e01d2ba70d8d106a6338              114 main/source/Release
    ...
    END


Nice! That's enough for now.. let's kill the process and clean
everything up.

    >>> import shutil
    >>> shutil.rmtree(temp_dir)

Remove the test archive from filesystem.

    >>> shutil.rmtree("/var/tmp/archive/")
    >>> keyserver.tearDown()


Feito! ;-)


vim:ft=doctest:ts=4:sw=4:et