Merge lp:~jpds/launchpad/fix_517020 into lp:launchpad

Proposed by Jonathan Davies
Status: Merged
Approved by: Michael Nelson
Approved revision: not available
Merged at revision: 10283
Proposed branch: lp:~jpds/launchpad/fix_517020
Merge into: lp:launchpad
Diff against target: 247 lines (+113/-20)
3 files modified
lib/lp/registry/interfaces/distributionmirror.py (+22/-16)
lib/lp/registry/stories/webservice/xx-distribution-mirror.txt (+88/-3)
lib/lp/registry/stories/webservice/xx-distribution.txt (+3/-1)
To merge this branch: bzr merge lp:~jpds/launchpad/fix_517020
Reviewer Review Type Date Requested Status
Brad Crittenden (community) code Approve
Michael Nelson (community) code Approve
Review via email: mp+18608@code.launchpad.net

Commit message

Various fixes to exported() values in the newly exposed distribution_mirror's API.

To post a comment you must log in.
Revision history for this message
Jonathan Davies (jpds) wrote :

= Summary =

This branch fixes various problems with the newly exposed distribution_mirror's as described in bug #517020.

It also exposes various other things whose permissions to access are restricted by Zope's configuration.

Revision history for this message
Michael Nelson (michael.nelson) wrote :
Download full text (7.3 KiB)

Hi Jonathan,

It's great that you're so quick to get in there and fix his. Other than some formatting issues, the only point that I think needs addressing here is testing the writable of the fields you're changing/exporting.

> === modified file 'lib/lp/registry/interfaces/distributionmirror.py'
> --- a/lib/lp/registry/interfaces/distributionmirror.py 2010-02-01 23:17:27 +0000
> +++ b/lib/lp/registry/interfaces/distributionmirror.py 2010-02-04 13:07:13 +0000
> @@ -287,17 +287,19 @@
>
> id = Int(title=_('The unique id'), required=True, readonly=True)
> owner = exported(PublicPersonChoice(
> - title=_('Owner'), required=False, readonly=True,
> - vocabulary='ValidOwner'))
> - reviewer = PublicPersonChoice(
> - title=_('Reviewer'), required=False, readonly=False,
> - vocabulary='ValidPersonOrTeam')
> + title=_('Owner'), description=_("The person who is set as the "
> + "current administrator of this mirror."), required=True,
> + readonly=False, vocabulary='ValidOwner'))

So the owner attribute is no-longer readonly? If that's intentional, we really
should have that tested/documented (ie. who can/can't write to it).

Also, just a few small indentation things looking at
the style guide. It might be easier to format this like:

    title=_('Owner'), description=_(
 "The person who is set as the current administrator of this mirror."),
    required=True, readonly=False, vocabulary='ValidOwner')

or

    title=_('Owner'), required=True, readonly=False, vocabulary='ValidOwner'
    description=_(
 "The person who is set as the current administrator of this mirror."))

> + reviewer = exported(PublicPersonChoice(
> + title=_('Reviewer'), description=_("The person who last reviewed this "
> + "mirror."), required=False, readonly=True,
> + vocabulary='ValidPersonOrTeam'))

So the main change is that readonly is now true. Please ensure this is tested
also.

Similar formatting issues here. Although, I can't see anything in the
style-guide, but I thought that concatenated strings needed to be aligned (if
you have to break them).

> distribution = exported(
> Reference(
> Interface,
> # Really IDistribution, circular import fixed in
> # _schema_circular_imports.
> - title=_("Distribution"), required=True,
> + title=_("Distribution"), required=True, readonly=True,

Again, this needs to be tested (so it doesn't happen again).

> description=_("The distribution that is mirrored")))
> name = exported(DistributionMirrorNameField(
> title=_('Name'), required=True, readonly=False,
> @@ -325,7 +327,7 @@
> description=_('e.g.: rsync://archive.ubuntu.com/ubuntu/')))
> enabled = exported(Bool(
> title=_('This mirror was probed successfully.'),
> - required=False, readonly=False, default=False))
> + required=False, readonly=True, default=False))

And here too.

> speed = exported(Choice(
> title=_('Link Speed'), required=True, readonly=False,
> vocabulary=MirrorSpeed))
> @@ -343,7 +345,8 @@
> title=_('Apply to...

Read more...

review: Needs Fixing (code)
Revision history for this message
Jonathan Davies (jpds) wrote :
Download full text (19.1 KiB)

> === modified file 'lib/lp/registry/interfaces/distributionmirror.py'
> > --- a/lib/lp/registry/interfaces/distributionmirror.py 2010-02-01 23:17:27 +0000
> > +++ b/lib/lp/registry/interfaces/distributionmirror.py 2010-02-04 13:07:13 +0000
> > @@ -287,17 +287,19 @@
> >
> > id = Int(title=_('The unique id'), required=True, readonly=True)
> > owner = exported(PublicPersonChoice(
> > - title=_('Owner'), required=False, readonly=True,
> > - vocabulary='ValidOwner'))
> > - reviewer = PublicPersonChoice(
> > - title=_('Reviewer'), required=False, readonly=False,
> > - vocabulary='ValidPersonOrTeam')
> > + title=_('Owner'), description=_("The person who is set as the "
> > + "current administrator of this mirror."), required=True,
> > + readonly=False, vocabulary='ValidOwner'))
>
> So the owner attribute is no-longer readonly? If that's intentional, we really
> should have that tested/documented (ie. who can/can't write to it).

This is now tested in registry/stories/webservice/xx-distribution-mirror.txt.

> Also, just a few small indentation things looking at
> the style guide. It might be easier to format this like:
>
> title=_('Owner'), description=_(
> "The person who is set as the current administrator of this mirror."),
> required=True, readonly=False, vocabulary='ValidOwner')
>
> or
>
> title=_('Owner'), required=True, readonly=False, vocabulary='ValidOwner'
> description=_(
> "The person who is set as the current administrator of this mirror."))

I've changed all the fields to the latter.

> > + reviewer = exported(PublicPersonChoice(
> > + title=_('Reviewer'), description=_("The person who last reviewed this "
> > + "mirror."), required=False, readonly=True,
> > + vocabulary='ValidPersonOrTeam'))
>
> So the main change is that readonly is now true. Please ensure this is tested
> also.

Also done.

> Similar formatting issues here. Although, I can't see anything in the
> style-guide, but I thought that concatenated strings needed to be aligned (if
> you have to break them).
>
> > distribution = exported(
> > Reference(
> > Interface,
> > # Really IDistribution, circular import fixed in
> > # _schema_circular_imports.
> > - title=_("Distribution"), required=True,
> > + title=_("Distribution"), required=True, readonly=True,
>
> Again, this needs to be tested (so it doesn't happen again).

This is now tested.

> > description=_("The distribution that is mirrored")))
> > name = exported(DistributionMirrorNameField(
> > title=_('Name'), required=True, readonly=False,
> > @@ -325,7 +327,7 @@
> > description=_('e.g.: rsync://archive.ubuntu.com/ubuntu/')))
> > enabled = exported(Bool(
> > title=_('This mirror was probed successfully.'),
> > - required=False, readonly=False, default=False))
> > + required=False, readonly=True, default=False))
>
> And here too.

Likewise.

> > speed = exported(Choice(
> > title=_('Link Speed'), required=True, readonly=False,
> > vocabul...

Revision history for this message
Michael Nelson (michael.nelson) wrote :
Download full text (4.6 KiB)

Thanks for the changes Jonathan. I've approved the review, but do have a couple of things below that I'm interested to hear back on (they could be just oversights on my part).

> Done and full diff since r10278:
> === modified file 'lib/lp/registry/stories/webservice/xx-distribution-
> mirror.txt'
> --- lib/lp/registry/stories/webservice/xx-distribution-mirror.txt
> 2010-02-04 12:22:19 +0000
> +++ lib/lp/registry/stories/webservice/xx-distribution-mirror.txt
> 2010-02-05 11:32:41 +0000
> @@ -57,6 +57,88 @@
> status: u'Official'
> whiteboard: None
>
> += Security checks =
> +
> +People who are not mirror listing admins or the mirrors registrar may not
> +change the owner's of mirrors:
> +
> + >>> from canonical.launchpad.testing.pages import webservice_for_person
> + >>> from canonical.launchpad.webapp.interfaces import OAuthPermission
> + >>> from zope.component import getUtility
> + >>> from lp.registry.interfaces.person import IPersonSet
> + >>> from simplejson import dumps
> + >>> login(ANONYMOUS)
> + >>> karl_db = getUtility(IPersonSet).getByName('karl')
> + >>> test_db = getUtility(IPersonSet).getByName('name12')
> + >>> karl_webservice = webservice_for_person(karl_db,
> + ... permission=OAuthPermission.WRITE_PUBLIC)
> + >>> test_webservice = webservice_for_person(test_db,
> + ... permission=OAuthPermission.WRITE_PUBLIC)
> + >>> logout()
> + >>> karl = webservice.get("/~karl").jsonBody()
> + >>> patch = {
> + ... u'owner_link': karl['self_link']
> + ... }
> +
> +Now trying to set the owner using Sample Person's webservice is not
> authorized.
> +
> + >>> response = test_webservice.patch(
> + ... canonical_archive['self_link'], 'application/json', dumps(patch))
> + >>> print response.getheader('status')
> + 401 Unauthorized
> +
> +But if we use Karl, the mirror listing admin's, webservice, we can update the

s/admin's,/admin's

> owner.
> +
> + >>> response = karl_webservice.patch(
> + ... canonical_archive['self_link'], 'application/json', dumps(patch))
> + >>> print response.getheader('status')
> + 209 Content Returned
> +
> + >>> patched_canonical_archive = response.jsonBody()
> + >>> print patched_canonical_archive['owner_link']
> + http://.../~karl
> +
> +Some attributes are read-only via the API:
> +
> + >>> distros = webservice.get("/distros").jsonBody()
> + >>> distro = distros['entries'][3]
> + >>> debian = webservice.get(distro['self_link']).jsonBody()

I haven't checked, but is the above line necessary? you're getting distro['self_link'] which should be equivalent to distro? (ie. distro == debian?). Actually, you only seem to use debian['self_link'] below anyway (which should be equivalent to distro['self_link'] unless I'm missing something).

> + >>> patch = {
> + ... u'date_reviewed' : u'2010-02-04T17:19:16.424198+00:00',
> + ... u'distribution_link' : debian['self_link'],
> + ... u'enabled' : False,
> + ... u'reviewer_link' : karl['self_link']
> + ... }
> + >>> response = karl_webservice.patch(
> + ... canonical_releases['self_link'], 'application/json',
> dum...

Read more...

review: Approve (code)
Revision history for this message
Jonathan Davies (jpds) wrote :

The branch failed a test on distribution.txt, as I was just testing distribution-mirror.txt. The last commit fixes this and this is the diff:

=== modified file 'lib/lp/registry/stories/webservice/xx-distribution.txt'
--- lib/lp/registry/stories/webservice/xx-distribution.txt 2010-02-01 23:17:27 +0000
+++ lib/lp/registry/stories/webservice/xx-distribution.txt 2010-02-05 18:18:28 +0000
@@ -124,18 +124,20 @@
     >>> pprint_entry(canonical_releases)
     content: u'CD Image'
     date_created: u'2006-10-16T18:31:43.434567+00:00'
+ date_reviewed: None
     description: None
     displayname: None
     distribution_link: u'http://.../ubuntu'
     enabled: True
     ftp_base_url: None
- has_ftp_or_rsync_base_url: False
     http_base_url: u'http://releases.ubuntu.com/'
     name: u'canonical-releases'
     official_candidate: True
     owner_link: u'http://.../~mark'
+ reviewer_link: None
     resource_type_link: u'http://.../#distribution_mirror'
     rsync_base_url: None
     self_link: u'http://.../ubuntu/+mirror/canonical-releases'
     speed: u'100 Mbps'
     status: u'Official'
+ whiteboard: None

Revision history for this message
Jonathan Davies (jpds) wrote :

>> >>> is_official_mirror =
>> webservice.named_get(canonical_releases['self_link'],
>> ... 'isOfficial').jsonBody()
>> >>> print is_official_mirror
>> - True
>> + False
>
> I can't see anything that should have changed the expected value of this test? Why did it
> change here?

For the record:

<noodles> Also, I can't see why the expected value of is_official_mirror changed at the end of your diff?
<jpds> 13:47:01 <jpds> is_o_m> IT changes because I patch it with: u'status' : 'Unofficial'

Revision history for this message
Brad Crittenden (bac) wrote :

Your additional test changes look good. Thanks.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/registry/interfaces/distributionmirror.py'
--- lib/lp/registry/interfaces/distributionmirror.py 2010-02-01 23:17:27 +0000
+++ lib/lp/registry/interfaces/distributionmirror.py 2010-02-05 18:56:19 +0000
@@ -287,17 +287,19 @@
287287
288 id = Int(title=_('The unique id'), required=True, readonly=True)288 id = Int(title=_('The unique id'), required=True, readonly=True)
289 owner = exported(PublicPersonChoice(289 owner = exported(PublicPersonChoice(
290 title=_('Owner'), required=False, readonly=True,290 title=_('Owner'), readonly=False, vocabulary='ValidOwner',
291 vocabulary='ValidOwner'))291 required=True, description=_(
292 reviewer = PublicPersonChoice(292 "The person who is set as the current administrator of this mirror.")))
293 title=_('Reviewer'), required=False, readonly=False,293 reviewer = exported(PublicPersonChoice(
294 vocabulary='ValidPersonOrTeam')294 title=_('Reviewer'), required=False, readonly=True,
295 vocabulary='ValidPersonOrTeam', description=_(
296 "The person who last reviewed this mirror.")))
295 distribution = exported(297 distribution = exported(
296 Reference(298 Reference(
297 Interface,299 Interface,
298 # Really IDistribution, circular import fixed in300 # Really IDistribution, circular import fixed in
299 # _schema_circular_imports.301 # _schema_circular_imports.
300 title=_("Distribution"), required=True,302 title=_("Distribution"), required=True, readonly=True,
301 description=_("The distribution that is mirrored")))303 description=_("The distribution that is mirrored")))
302 name = exported(DistributionMirrorNameField(304 name = exported(DistributionMirrorNameField(
303 title=_('Name'), required=True, readonly=False,305 title=_('Name'), required=True, readonly=False,
@@ -325,7 +327,7 @@
325 description=_('e.g.: rsync://archive.ubuntu.com/ubuntu/')))327 description=_('e.g.: rsync://archive.ubuntu.com/ubuntu/')))
326 enabled = exported(Bool(328 enabled = exported(Bool(
327 title=_('This mirror was probed successfully.'),329 title=_('This mirror was probed successfully.'),
328 required=False, readonly=False, default=False))330 required=False, readonly=True, default=False))
329 speed = exported(Choice(331 speed = exported(Choice(
330 title=_('Link Speed'), required=True, readonly=False,332 title=_('Link Speed'), required=True, readonly=False,
331 vocabulary=MirrorSpeed))333 vocabulary=MirrorSpeed))
@@ -344,7 +346,8 @@
344 required=False, readonly=False, default=True))346 required=False, readonly=False, default=True))
345 status = exported(Choice(347 status = exported(Choice(
346 title=_('Status'), required=True, readonly=False,348 title=_('Status'), required=True, readonly=False,
347 vocabulary=MirrorStatus))349 vocabulary=MirrorStatus,
350 description=_("The current status of a mirror's registration.")))
348351
349 title = Attribute('The title of this mirror')352 title = Attribute('The title of this mirror')
350 cdimage_series = Attribute(353 cdimage_series = Attribute(
@@ -355,17 +358,20 @@
355 last_probe_record = Attribute(358 last_probe_record = Attribute(
356 'The last MirrorProbeRecord for this mirror.')359 'The last MirrorProbeRecord for this mirror.')
357 all_probe_records = Attribute('All MirrorProbeRecords for this mirror.')360 all_probe_records = Attribute('All MirrorProbeRecords for this mirror.')
358 has_ftp_or_rsync_base_url = exported(Bool(361 has_ftp_or_rsync_base_url = Bool(
359 title=_('Does this mirror have a ftp or rsync base URL?')))362 title=_('Does this mirror have a FTP or Rsync base URL?'))
360 base_url = Attribute('The HTTP or FTP base URL of this mirror')363 base_url = Attribute('The HTTP or FTP base URL of this mirror')
361 date_created = exported(Datetime(364 date_created = exported(Datetime(
362 title=_('Date Created'), required=True, readonly=True))365 title=_('Date Created'), required=True, readonly=True,
363 date_reviewed = Datetime(366 description=_("The date on which this mirror was registered.")))
364 title=_('Date reviewed'), required=False, readonly=False)367 date_reviewed = exported(Datetime(
365 whiteboard = Whiteboard(368 title=_('Date reviewed'), required=False, readonly=True,
366 title=_('Whiteboard'), required=False,369 description=_(
370 "The date on which this mirror was last reviewed by a mirror admin.")))
371 whiteboard = exported(Whiteboard(
372 title=_('Whiteboard'), required=False, readonly=False,
367 description=_("Notes on the current status of the mirror (only "373 description=_("Notes on the current status of the mirror (only "
368 "visible to admins and the mirror's registrant)."))374 "visible to admins and the mirror's registrant).")))
369375
370 @invariant376 @invariant
371 def mirrorMustHaveHTTPOrFTPURL(mirror):377 def mirrorMustHaveHTTPOrFTPURL(mirror):
372378
=== modified file 'lib/lp/registry/stories/webservice/xx-distribution-mirror.txt'
--- lib/lp/registry/stories/webservice/xx-distribution-mirror.txt 2010-02-01 23:17:27 +0000
+++ lib/lp/registry/stories/webservice/xx-distribution-mirror.txt 2010-02-05 18:56:19 +0000
@@ -13,21 +13,23 @@
13 >>> pprint_entry(canonical_archive_json)13 >>> pprint_entry(canonical_archive_json)
14 content: u'Archive'14 content: u'Archive'
15 date_created: u'2006-10-16T18:31:43.434567+00:00'15 date_created: u'2006-10-16T18:31:43.434567+00:00'
16 date_reviewed: None
16 description: None17 description: None
17 displayname: None18 displayname: None
18 distribution_link: u'http://.../ubuntu'19 distribution_link: u'http://.../ubuntu'
19 enabled: True20 enabled: True
20 ftp_base_url: None21 ftp_base_url: None
21 has_ftp_or_rsync_base_url: False
22 http_base_url: u'http://archive.ubuntu.com/ubuntu/'22 http_base_url: u'http://archive.ubuntu.com/ubuntu/'
23 name: u'canonical-archive'23 name: u'canonical-archive'
24 official_candidate: True24 official_candidate: True
25 owner_link: u'http://.../~mark'25 owner_link: u'http://.../~mark'
26 resource_type_link: u'http://.../#distribution_mirror'26 resource_type_link: u'http://.../#distribution_mirror'
27 reviewer_link: None
27 rsync_base_url: None28 rsync_base_url: None
28 self_link: u'http://.../ubuntu/+mirror/canonical-archive'29 self_link: u'http://.../ubuntu/+mirror/canonical-archive'
29 speed: u'100 Mbps'30 speed: u'100 Mbps'
30 status: u'Official'31 status: u'Official'
32 whiteboard: None
3133
32And CD image mirrors:34And CD image mirrors:
3335
@@ -37,21 +39,104 @@
37 >>> pprint_entry(canonical_releases_json)39 >>> pprint_entry(canonical_releases_json)
38 content: u'CD Image'40 content: u'CD Image'
39 date_created: u'2006-10-16T18:31:43.434567+00:00'41 date_created: u'2006-10-16T18:31:43.434567+00:00'
42 date_reviewed: None
40 description: None43 description: None
41 displayname: None44 displayname: None
42 distribution_link: u'http://.../ubuntu'45 distribution_link: u'http://.../ubuntu'
43 enabled: True46 enabled: True
44 ftp_base_url: None47 ftp_base_url: None
45 has_ftp_or_rsync_base_url: False
46 http_base_url: u'http://releases.ubuntu.com/'48 http_base_url: u'http://releases.ubuntu.com/'
47 name: u'canonical-releases'49 name: u'canonical-releases'
48 official_candidate: True50 official_candidate: True
49 owner_link: u'http://.../~mark'51 owner_link: u'http://.../~mark'
50 resource_type_link: u'http://.../#distribution_mirror'52 resource_type_link: u'http://.../#distribution_mirror'
53 reviewer_link: None
51 rsync_base_url: None54 rsync_base_url: None
52 self_link: u'http://.../ubuntu/+mirror/canonical-releases'55 self_link: u'http://.../ubuntu/+mirror/canonical-releases'
53 speed: u'100 Mbps'56 speed: u'100 Mbps'
54 status: u'Official'57 status: u'Official'
58 whiteboard: None
59
60= Security checks =
61
62People who are not mirror listing admins or the mirrors registrar may not
63change the owner's of mirrors:
64
65 >>> from canonical.launchpad.testing.pages import webservice_for_person
66 >>> from canonical.launchpad.webapp.interfaces import OAuthPermission
67 >>> from zope.component import getUtility
68 >>> from lp.registry.interfaces.person import IPersonSet
69 >>> from simplejson import dumps
70 >>> login(ANONYMOUS)
71 >>> karl_db = getUtility(IPersonSet).getByName('karl')
72 >>> test_db = getUtility(IPersonSet).getByName('name12')
73 >>> karl_webservice = webservice_for_person(karl_db,
74 ... permission=OAuthPermission.WRITE_PUBLIC)
75 >>> test_webservice = webservice_for_person(test_db,
76 ... permission=OAuthPermission.WRITE_PUBLIC)
77 >>> logout()
78 >>> karl = webservice.get("/~karl").jsonBody()
79 >>> patch = {
80 ... u'owner_link': karl['self_link']
81 ... }
82
83Now trying to set the owner using Sample Person's webservice is not authorized.
84
85 >>> response = test_webservice.patch(
86 ... canonical_archive['self_link'], 'application/json', dumps(patch))
87 >>> print response.getheader('status')
88 401 Unauthorized
89
90But if we use Karl, the mirror listing admin's, webservice, we can update the owner.
91
92 >>> response = karl_webservice.patch(
93 ... canonical_archive['self_link'], 'application/json', dumps(patch))
94 >>> print response.getheader('status')
95 209 Content Returned
96
97 >>> patched_canonical_archive = response.jsonBody()
98 >>> print patched_canonical_archive['owner_link']
99 http://.../~karl
100
101Some attributes are read-only via the API:
102
103 >>> distros = webservice.get("/distros").jsonBody()
104 >>> debian_distro = distros['entries'][3]
105 >>> patch = {
106 ... u'date_reviewed' : u'2010-02-04T17:19:16.424198+00:00',
107 ... u'distribution_link' : debian_distro['self_link'],
108 ... u'enabled' : False,
109 ... u'reviewer_link' : karl['self_link']
110 ... }
111 >>> response = karl_webservice.patch(
112 ... canonical_releases['self_link'], 'application/json', dumps(patch))
113 >>> print response
114 HTTP/1.1 400 Bad Request
115 Status: 400 Bad Request
116 ...
117 enabled: You tried to modify a read-only attribute.
118 date_reviewed: You tried to modify a read-only attribute.
119 reviewer_link: You tried to modify a read-only attribute.
120 distribution_link: You tried to modify a read-only attribute.
121
122While others can be set with the appropriate authorization:
123
124 >>> patch = {
125 ... u'status' : 'Unofficial',
126 ... u'whiteboard' : u'This mirror is too shiny to be true'
127 ... }
128 >>> response = test_webservice.patch(
129 ... canonical_releases['self_link'], 'application/json', dumps(patch))
130 >>> print response.getheader('status')
131 401 Unauthorized
132
133 >>> response = karl_webservice.patch(
134 ... canonical_releases['self_link'], 'application/json', dumps(patch)).jsonBody()
135 >>> pprint_entry(response)
136 content: u'CD Image'
137 ...
138 status: u'Unofficial'
139 whiteboard: u'This mirror is too shiny to be true'
55140
56= Distribution Mirror Custom Operations =141= Distribution Mirror Custom Operations =
57142
@@ -63,7 +148,7 @@
63 >>> is_official_mirror = webservice.named_get(canonical_releases['self_link'],148 >>> is_official_mirror = webservice.named_get(canonical_releases['self_link'],
64 ... 'isOfficial').jsonBody()149 ... 'isOfficial').jsonBody()
65 >>> print is_official_mirror150 >>> print is_official_mirror
66 True151 False
67152
68"getOverallFreshness" returns the freshness of the mirror determined by the153"getOverallFreshness" returns the freshness of the mirror determined by the
69mirror prober from the mirror's last probe.154mirror prober from the mirror's last probe.
70155
=== modified file 'lib/lp/registry/stories/webservice/xx-distribution.txt'
--- lib/lp/registry/stories/webservice/xx-distribution.txt 2010-02-01 23:17:27 +0000
+++ lib/lp/registry/stories/webservice/xx-distribution.txt 2010-02-05 18:56:19 +0000
@@ -124,18 +124,20 @@
124 >>> pprint_entry(canonical_releases)124 >>> pprint_entry(canonical_releases)
125 content: u'CD Image'125 content: u'CD Image'
126 date_created: u'2006-10-16T18:31:43.434567+00:00'126 date_created: u'2006-10-16T18:31:43.434567+00:00'
127 date_reviewed: None
127 description: None128 description: None
128 displayname: None129 displayname: None
129 distribution_link: u'http://.../ubuntu'130 distribution_link: u'http://.../ubuntu'
130 enabled: True131 enabled: True
131 ftp_base_url: None132 ftp_base_url: None
132 has_ftp_or_rsync_base_url: False
133 http_base_url: u'http://releases.ubuntu.com/'133 http_base_url: u'http://releases.ubuntu.com/'
134 name: u'canonical-releases'134 name: u'canonical-releases'
135 official_candidate: True135 official_candidate: True
136 owner_link: u'http://.../~mark'136 owner_link: u'http://.../~mark'
137 resource_type_link: u'http://.../#distribution_mirror'137 resource_type_link: u'http://.../#distribution_mirror'
138 reviewer_link: None
138 rsync_base_url: None139 rsync_base_url: None
139 self_link: u'http://.../ubuntu/+mirror/canonical-releases'140 self_link: u'http://.../ubuntu/+mirror/canonical-releases'
140 speed: u'100 Mbps'141 speed: u'100 Mbps'
141 status: u'Official'142 status: u'Official'
143 whiteboard: None