Merge lp:~cjohnston/ubuntu-ci-services-itself/ts-project-write-api into lp:ubuntu-ci-services-itself
- ts-project-write-api
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Ursula Junque |
Approved revision: | 39 |
Merged at revision: | 42 |
Proposed branch: | lp:~cjohnston/ubuntu-ci-services-itself/ts-project-write-api |
Merge into: | lp:ubuntu-ci-services-itself |
Diff against target: |
299 lines (+132/-53) 4 files modified
docs/components/ticket-system.rst (+33/-0) ticket_system/people/tests.py (+1/-1) ticket_system/project/api.py (+7/-4) ticket_system/project/tests.py (+91/-48) |
To merge this branch: | bzr merge lp:~cjohnston/ubuntu-ci-services-itself/ts-project-write-api |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Francis Ginther | Approve | ||
Ursula Junque (community) | Needs Information | ||
Review via email: mp+199173@code.launchpad.net |
Commit message
Add a write API to the TS project app
Description of the change
L52/53 is just changing the name to better describe the test
Ursula Junque (ursinha) wrote : | # |
> A binary package may not be associated with a source package, it may just
> refer to a binary package that is available in the archive, but not otherwise
> seeded in the base image.
>
> The changes to the binary package list should only become permanent after the
> ticket is successfully processed. Once the ticket is completed, they can then
> be integrated into the package list for future builds.
I believe we have a different understanding of what this binary package list is. In my understanding for phase 0 this would be a list of seeded/relevant binary packages that should be tested when building and testing images, but as dep8 tests are part of the sources this list would be used only as input to an external script that should come up with the sources list, hence we storing only the source packages list, not binary (as described in the docs).
This is only the initial implementation of what used to be the "project manager" component (similar to cu2d-config). In your scenario, this list would change per ticket? It seems to me that this is completely different than what was understood when we discussed the tests manager/project manager/ticket system split.
>
> For example:
>
> After a few successful builds, the binary package list contains unity8, mir
> and libmir. The most recently built package will contain these packages.
>
> A new request is made to build a source package from the autopilot source
> package and add python3-autopilot and python3 to the binary package list. When
> this builds, python3-autopilot and python3 needs to be included in the image,
> but it only gets promoted to the master binary package list after a successful
> build and test.
>
> With two different API calls to add binary and source packages, it's unclear
> to me how a build request with source and binary package updates would be
> atomic.
Chris Johnston (cjohnston) wrote : | # |
On Mon, Dec 16, 2013 at 8:28 PM, Ursula Junque wrote:
> Review: Needs Information
>
> > A binary package may not be associated with a source package, it may just
> > refer to a binary package that is available in the archive, but not
> otherwise
> > seeded in the base image.
> >
> > The changes to the binary package list should only become permanent
> after the
> > ticket is successfully processed. Once the ticket is completed, they can
> then
> > be integrated into the package list for future builds.
>
>
> I believe we have a different understanding of what this binary package
> list is. In my understanding for phase 0 this would be a list of
> seeded/relevant binary packages that should be tested when building and
> testing images, but as dep8 tests are part of the sources this list would
> be used only as input to an external script that should come up with the
> sources list, hence we storing only the source packages list, not binary
> (as described in the docs).
>
> This is only the initial implementation of what used to be the "project
> manager" component (similar to cu2d-config). In your scenario, this list
> would change per ticket? It seems to me that this is completely different
> than what was understood when we discussed the tests manager/project
> manager/ticket system split.
>
>
>
I had the same understanding that Ursula did.
> >
> > For example:
> >
> > After a few successful builds, the binary package list contains unity8,
> mir
> > and libmir. The most recently built package will contain these packages.
> >
> > A new request is made to build a source package from the autopilot source
> > package and add python3-autopilot and python3 to the binary package
> list. When
> > this builds, python3-autopilot and python3 needs to be included in the
> image,
> > but it only gets promoted to the master binary package list after a
> successful
> > build and test.
>
>
> >
> > With two different API calls to add binary and source packages, it's
> unclear
> > to me how a build request with source and binary package updates would be
> > atomic.
>
>
Francis Ginther (fginther) wrote : | # |
Based on our conversation, this is ok to go.
Preview Diff
1 | === modified file 'docs/components/ticket-system.rst' |
2 | --- docs/components/ticket-system.rst 2013-12-12 16:53:14 +0000 |
3 | +++ docs/components/ticket-system.rst 2013-12-16 19:00:41 +0000 |
4 | @@ -219,6 +219,39 @@ |
5 | curl --dump-header - http://localhost:8000/api/v1/person/?is_team=True |
6 | curl --dump-header - http://localhost:8000/api/v1/person/?is_team=False |
7 | |
8 | +Project |
9 | +------- |
10 | + |
11 | +get_source_package |
12 | +~~~~~~~~~~~~~~~~~~ |
13 | + |
14 | +*return all binary packages* |
15 | + |
16 | +:: |
17 | + curl --dump-header - http://localhost:8000/api/v1/sourcepackage/ |
18 | + |
19 | +add_source_package |
20 | +~~~~~~~~~~~~~~~~~~ |
21 | + |
22 | +:: |
23 | + |
24 | + curl --dump-header - -H "Content-Type: application/json" -X POST --data '{"name": "my-package"}' http://localhost:8000/api/v1/sourcepackage/ |
25 | + |
26 | + get_binary_package |
27 | +~~~~~~~~~~~~~~~~~~ |
28 | + |
29 | +*return all binary packages* |
30 | + |
31 | +:: |
32 | + curl --dump-header - http://localhost:8000/api/v1/binarypackage/ |
33 | + |
34 | +add_binary_package |
35 | +~~~~~~~~~~~~~~~~~~ |
36 | + |
37 | +:: |
38 | + |
39 | + curl --dump-header - -H "Content-Type: application/json" -X POST --data '{"name": "my-package", "sourcepackage": "/api/v1/sourcepackage/1/", "seeded": "False"}' http://localhost:8000/api/v1/binarypackage/ |
40 | + |
41 | Models |
42 | ****** |
43 | |
44 | |
45 | === modified file 'ticket_system/people/tests.py' |
46 | --- ticket_system/people/tests.py 2013-12-12 16:53:14 +0000 |
47 | +++ ticket_system/people/tests.py 2013-12-16 19:00:41 +0000 |
48 | @@ -90,7 +90,7 @@ |
49 | # Verify a new one has been added. |
50 | self.assertEqual(Person.objects.filter(is_team=True).count(), 1) |
51 | |
52 | - def test_put_detail(self): |
53 | + def test_patch_detail(self): |
54 | # Grab the current data & modify it slightly. |
55 | original_data = self.getResource(self.detail_url) |
56 | new_data = original_data.copy() |
57 | |
58 | === modified file 'ticket_system/project/api.py' |
59 | --- ticket_system/project/api.py 2013-12-09 19:35:22 +0000 |
60 | +++ ticket_system/project/api.py 2013-12-16 19:00:41 +0000 |
61 | @@ -14,6 +14,7 @@ |
62 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
63 | |
64 | from tastypie import fields |
65 | +from tastypie.authorization import Authorization |
66 | from tastypie.resources import ModelResource |
67 | from tastypie.constants import ALL |
68 | from project.models import SourcePackage, BinaryPackage |
69 | @@ -23,9 +24,10 @@ |
70 | |
71 | class Meta: |
72 | queryset = SourcePackage.objects.all() |
73 | - allowed_methods = ['get'] |
74 | + allowed_methods = ['get', 'post', 'patch'] |
75 | + authorization = Authorization() |
76 | filtering = { |
77 | - "name": ('exact', 'startswith'), |
78 | + "name": ('exact', 'iexact', 'startswith', 'istartswith'), |
79 | } |
80 | |
81 | |
82 | @@ -35,8 +37,9 @@ |
83 | |
84 | class Meta: |
85 | queryset = BinaryPackage.objects.all() |
86 | - allowed_methods = ['get'] |
87 | + allowed_methods = ['get', 'post', 'patch'] |
88 | + authorization = Authorization() |
89 | filtering = { |
90 | - "name": ('exact', 'startswith'), |
91 | + "name": ('exact', 'iexact', 'startswith', 'istartswith'), |
92 | "seeded": ALL, |
93 | } |
94 | |
95 | === modified file 'ticket_system/project/tests.py' |
96 | --- ticket_system/project/tests.py 2013-12-10 18:29:34 +0000 |
97 | +++ ticket_system/project/tests.py 2013-12-16 19:00:41 +0000 |
98 | @@ -14,105 +14,122 @@ |
99 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
100 | |
101 | from django.test import TestCase |
102 | +from model_mommy import mommy |
103 | from ci_utils.tastypie.test import TastypieTestCase |
104 | from project.models import SourcePackage, BinaryPackage |
105 | |
106 | |
107 | -def create_sourcepackage(name="awesome-package"): |
108 | - package = SourcePackage() |
109 | - package.name = name |
110 | - package.save() |
111 | - return package |
112 | - |
113 | - |
114 | -def create_binarypackage(name="awesome-binary", sourcepackage=None, |
115 | - seeded=False): |
116 | - binary = BinaryPackage() |
117 | - binary.name = name |
118 | - binary.sourcepackage = sourcepackage |
119 | - binary.seeded = seeded |
120 | - binary.save() |
121 | - return binary |
122 | - |
123 | - |
124 | class SourcePackageModelTest(TestCase): |
125 | |
126 | - def test_creating_a_sourcepackage(self): |
127 | - package = create_sourcepackage() |
128 | + def setUp(self): |
129 | + self.sourcepackage = mommy.make('SourcePackage') |
130 | + |
131 | + def test_creating_sourcepackage(self): |
132 | package_in_database = SourcePackage.objects.all() |
133 | self.assertEquals(len(package_in_database), 1) |
134 | only_package_in_database = package_in_database[0] |
135 | - self.assertEquals(only_package_in_database, package) |
136 | + self.assertEquals(only_package_in_database, self.sourcepackage) |
137 | |
138 | - self.assertEquals(only_package_in_database.name, "awesome-package") |
139 | + self.assertEquals(only_package_in_database.name, |
140 | + self.sourcepackage.name) |
141 | |
142 | |
143 | class BinaryPackageModelTest(TestCase): |
144 | |
145 | def setUp(self): |
146 | - self.sourcepackage = create_sourcepackage() |
147 | - self.binarypackage = create_binarypackage( |
148 | - sourcepackage=self.sourcepackage) |
149 | + self.sourcepackage = mommy.make('SourcePackage') |
150 | + self.binarypackage = mommy.make('BinaryPackage', |
151 | + sourcepackage=self.sourcepackage) |
152 | |
153 | - def test_creating_a_binarypackage(self): |
154 | + def test_creating_binarypackage(self): |
155 | binarypackage_in_database = BinaryPackage.objects.all() |
156 | self.assertEquals(len(binarypackage_in_database), 1) |
157 | only_binarypackage_in_database = binarypackage_in_database[0] |
158 | self.assertEquals(only_binarypackage_in_database, self.binarypackage) |
159 | |
160 | self.assertEquals(only_binarypackage_in_database.name, |
161 | - "awesome-binary") |
162 | + self.binarypackage.name) |
163 | |
164 | |
165 | class SourcePackageResourceTest(TastypieTestCase): |
166 | |
167 | def setUp(self): |
168 | super(SourcePackageResourceTest, self).setUp('/api/v1') |
169 | - create_sourcepackage() |
170 | - self.sourcepackage_1 = SourcePackage.objects.get( |
171 | - name='awesome-package') |
172 | - self.detail_url = 'sourcepackage/{0}/'.format(self.sourcepackage_1.pk) |
173 | + self.sourcepackage = mommy.make('SourcePackage') |
174 | + self.detail_url = 'sourcepackage/{0}/'.format(self.sourcepackage.pk) |
175 | + self.post_sourcepackage_data = { |
176 | + 'name': 'ubuntu-ci', |
177 | + } |
178 | |
179 | def test_get_sourcepackage_list_json(self): |
180 | obj = self.getResource('sourcepackage/') |
181 | self.assertEqual(obj['objects'][0], { |
182 | - u'id': self.sourcepackage_1.pk, |
183 | - u'name': u'awesome-package', |
184 | + u'id': self.sourcepackage.pk, |
185 | + u'name': unicode(self.sourcepackage.name), |
186 | u'resource_uri': u'/api/v1/sourcepackage/{0}/'.format( |
187 | - self.sourcepackage_1.pk) |
188 | + self.sourcepackage.pk) |
189 | }) |
190 | |
191 | def test_get_sourcepackage_detail_json(self): |
192 | obj = self.getResource(self.detail_url) |
193 | # We use ``assertKeys`` here to just verify the keys, not all the data. |
194 | self.assertKeys(obj, ['id', 'name', 'resource_uri']) |
195 | - self.assertEqual(obj['name'], 'awesome-package') |
196 | + self.assertEqual(obj['name'], self.sourcepackage.name) |
197 | + |
198 | + def test_post_sourcepackage(self): |
199 | + # Check how many are there first. |
200 | + self.assertEqual(SourcePackage.objects.count(), 1) |
201 | + self.post(resource='sourcepackage/', |
202 | + params=self.post_sourcepackage_data) |
203 | + # Verify a new one has been added. |
204 | + self.assertEqual(SourcePackage.objects.count(), 2) |
205 | + |
206 | + def test_patch_detail(self): |
207 | + # Grab the current data & modify it slightly. |
208 | + original_data = self.getResource(self.detail_url) |
209 | + new_data = original_data.copy() |
210 | + new_data['name'] = 'ci-ubuntu' |
211 | + |
212 | + self.assertEqual(SourcePackage.objects.count(), 1) |
213 | + self.patch(resource=self.detail_url, params=new_data) |
214 | + # Make sure the count hasn't changed & we did an update. |
215 | + self.assertEqual(SourcePackage.objects.count(), 1) |
216 | + # Check for updated data. |
217 | + self.assertEqual(SourcePackage.objects.get( |
218 | + pk=self.sourcepackage.pk).name, new_data['name']) |
219 | + |
220 | + def test_delete_sourcepackage_not_allowed(self): |
221 | + resp = self.delete(resource=self.detail_url) |
222 | + self.assertHttpMethodNotAllowed(resp) |
223 | |
224 | |
225 | class BinaryPackageResourceTest(TastypieTestCase): |
226 | |
227 | def setUp(self): |
228 | super(BinaryPackageResourceTest, self).setUp('/api/v1') |
229 | - create_sourcepackage() |
230 | - self.sourcepackage_1 = SourcePackage.objects.get( |
231 | - name='awesome-package') |
232 | - create_binarypackage(sourcepackage=self.sourcepackage_1) |
233 | - self.binarypackage_1 = BinaryPackage.objects.get(name='awesome-binary') |
234 | - self.detail_url = 'binarypackage/{0}/'.format(self.binarypackage_1.pk) |
235 | + self.sourcepackage = mommy.make('SourcePackage') |
236 | + self.binarypackage = mommy.make('BinaryPackage', |
237 | + sourcepackage=self.sourcepackage) |
238 | + self.detail_url = 'binarypackage/{0}/'.format(self.binarypackage.pk) |
239 | + self.post_binarypackage_data = { |
240 | + 'name': 'ubuntu-ci', |
241 | + 'sourcepackage': '/api/v1/sourcepackage/{0}/'.format( |
242 | + self.sourcepackage.pk) |
243 | + } |
244 | |
245 | def test_get_binarypackage_list_json(self): |
246 | obj = self.getResource('binarypackage/') |
247 | self.assertEqual(obj['objects'][0], { |
248 | - u'id': self.binarypackage_1.pk, |
249 | - u'name': u'awesome-binary', |
250 | - u'seeded': False, |
251 | + u'id': self.binarypackage.pk, |
252 | + u'name': unicode(self.binarypackage.name), |
253 | + u'seeded': self.binarypackage.seeded, |
254 | u'sourcepackage': { |
255 | - u'id': self.sourcepackage_1.pk, |
256 | - u'name': u'awesome-package', |
257 | + u'id': self.sourcepackage.pk, |
258 | + u'name': unicode(self.sourcepackage.name), |
259 | u'resource_uri': u'/api/v1/sourcepackage/{0}/'.format( |
260 | - self.sourcepackage_1.pk)}, |
261 | + self.sourcepackage.pk)}, |
262 | u'resource_uri': u'/api/v1/binarypackage/{0}/'.format( |
263 | - self.binarypackage_1.pk) |
264 | + self.binarypackage.pk) |
265 | }) |
266 | |
267 | def test_get_binarypackage_detail_json(self): |
268 | @@ -121,4 +138,30 @@ |
269 | # We use ``assertKeys`` here to just verify the keys, not all the data. |
270 | self.assertKeys(obj, ['id', 'name', 'resource_uri', 'seeded', |
271 | 'sourcepackage']) |
272 | - self.assertEqual(obj['name'], 'awesome-binary') |
273 | + self.assertEqual(obj['name'], self.binarypackage.name) |
274 | + |
275 | + def test_post_binarypackage(self): |
276 | + # Check how many are there first. |
277 | + self.assertEqual(BinaryPackage.objects.count(), 1) |
278 | + self.post(resource='binarypackage/', |
279 | + params=self.post_binarypackage_data) |
280 | + # Verify a new one has been added. |
281 | + self.assertEqual(BinaryPackage.objects.count(), 2) |
282 | + |
283 | + def test_patch_detail(self): |
284 | + # Grab the current data & modify it slightly. |
285 | + original_data = self.getResource(self.detail_url) |
286 | + new_data = original_data.copy() |
287 | + new_data['name'] = 'ci-ubuntu' |
288 | + |
289 | + self.assertEqual(BinaryPackage.objects.count(), 1) |
290 | + self.patch(resource=self.detail_url, params=new_data) |
291 | + # Make sure the count hasn't changed & we did an update. |
292 | + self.assertEqual(BinaryPackage.objects.count(), 1) |
293 | + # Check for updated data. |
294 | + self.assertEqual(BinaryPackage.objects.get( |
295 | + pk=self.binarypackage.pk).name, new_data['name']) |
296 | + |
297 | + def test_delete_binarypackage_not_allowed(self): |
298 | + resp = self.delete(resource=self.detail_url) |
299 | + self.assertHttpMethodNotAllowed(resp) |
A binary package may not be associated with a source package, it may just refer to a binary package that is available in the archive, but not otherwise seeded in the base image.
The changes to the binary package list should only become permanent after the ticket is successfully processed. Once the ticket is completed, they can then be integrated into the package list for future builds.
For example:
After a few successful builds, the binary package list contains unity8, mir and libmir. The most recently built package will contain these packages.
A new request is made to build a source package from the autopilot source package and add python3-autopilot and python3 to the binary package list. When this builds, python3-autopilot and python3 needs to be included in the image, but it only gets promoted to the master binary package list after a successful build and test.
With two different API calls to add binary and source packages, it's unclear to me how a build request with source and binary package updates would be atomic.