Merge lp:~blake-rouse/maas/simplestreams-os-field into lp:~maas-committers/maas/trunk
- simplestreams-os-field
- Merge into trunk
Proposed by
Blake Rouse
Status: | Merged |
---|---|
Approved by: | Blake Rouse |
Approved revision: | no longer in the source branch. |
Merged at revision: | 2798 |
Proposed branch: | lp:~blake-rouse/maas/simplestreams-os-field |
Merge into: | lp:~maas-committers/maas/trunk |
Diff against target: |
654 lines (+143/-61) 14 files modified
src/provisioningserver/boot/tests/test_tftppath.py (+5/-1) src/provisioningserver/boot/tftppath.py (+1/-0) src/provisioningserver/config.py (+1/-0) src/provisioningserver/import_images/boot_image_mapping.py (+32/-15) src/provisioningserver/import_images/download_descriptions.py (+17/-12) src/provisioningserver/import_images/download_resources.py (+10/-6) src/provisioningserver/import_images/helpers.py (+1/-0) src/provisioningserver/import_images/testing/factory.py (+8/-0) src/provisioningserver/import_images/tests/test_boot_image_mapping.py (+18/-7) src/provisioningserver/import_images/tests/test_boot_resources.py (+15/-7) src/provisioningserver/import_images/tests/test_download_descriptions.py (+25/-11) src/provisioningserver/import_images/tests/test_download_resources.py (+1/-2) src/provisioningserver/tests/test_config.py (+2/-0) src/provisioningserver/utils/__init__.py (+7/-0) |
To merge this branch: | bzr merge lp:~blake-rouse/maas/simplestreams-os-field |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jason Hobbs (community) | Approve | ||
Review via email: mp+231611@code.launchpad.net |
Commit message
Added support for os field on product from simplestreams endpoint.
Description of the change
To post a comment you must log in.
Revision history for this message
Blake Rouse (blake-rouse) : | # |
Revision history for this message
Blake Rouse (blake-rouse) wrote : | # |
Have updated the code. Could you give it another look.
Revision history for this message
Jason Hobbs (jason-hobbs) wrote : | # |
Looks good, thanks.
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'src/provisioningserver/boot/tests/test_tftppath.py' |
2 | --- src/provisioningserver/boot/tests/test_tftppath.py 2014-08-13 21:49:35 +0000 |
3 | +++ src/provisioningserver/boot/tests/test_tftppath.py 2014-08-22 21:52:07 +0000 |
4 | @@ -122,6 +122,7 @@ |
5 | |
6 | def make_meta_file(self, image_params, image_resource, tftproot): |
7 | image = ImageSpec( |
8 | + os=image_params["osystem"], |
9 | arch=image_params["architecture"], |
10 | subarch=image_params["subarchitecture"], |
11 | release=image_params["release"], label=image_params["label"]) |
12 | @@ -349,6 +350,7 @@ |
13 | # and subarch vs subarchitecture means I can't just do a simple |
14 | # dict parameter expansion here. |
15 | params = { |
16 | + "osystem": image.os, |
17 | "architecture": image.arch, |
18 | "subarchitecture": image.subarch, |
19 | "release": image.release, |
20 | @@ -372,6 +374,7 @@ |
21 | # and subarch vs subarchitecture means I can't just do a simple |
22 | # dict parameter expansion here. |
23 | params = { |
24 | + "osystem": image.os, |
25 | "architecture": image.arch, |
26 | "subarchitecture": image.subarch, |
27 | "release": image.release, |
28 | @@ -459,7 +462,8 @@ |
29 | |
30 | # Create some maas.meta content. |
31 | image = ImageSpec( |
32 | - arch=arch, subarch=subarch, release=release, label=label) |
33 | + os=osystem, arch=arch, subarch=subarch, release=release, |
34 | + label=label) |
35 | image_resource = dict(subarches=factory.make_name("subarches")) |
36 | mapping = BootImageMapping() |
37 | mapping.setdefault(image, image_resource) |
38 | |
39 | === modified file 'src/provisioningserver/boot/tftppath.py' |
40 | --- src/provisioningserver/boot/tftppath.py 2014-08-13 21:49:35 +0000 |
41 | +++ src/provisioningserver/boot/tftppath.py 2014-08-22 21:52:07 +0000 |
42 | @@ -132,6 +132,7 @@ |
43 | mapping = BootImageMapping.load_json(metadata) |
44 | |
45 | image = ImageSpec( |
46 | + os=params["osystem"], |
47 | arch=params["architecture"], |
48 | subarch=params["subarchitecture"], |
49 | release=params["release"], |
50 | |
51 | === modified file 'src/provisioningserver/config.py' |
52 | --- src/provisioningserver/config.py 2014-08-13 21:49:35 +0000 |
53 | +++ src/provisioningserver/config.py 2014-08-22 21:52:07 +0000 |
54 | @@ -174,6 +174,7 @@ |
55 | |
56 | if_key_missing = None |
57 | |
58 | + os = String(if_missing="*") |
59 | release = String(if_missing="*") |
60 | arches = Set(if_missing=["*"]) |
61 | subarches = Set(if_missing=['*']) |
62 | |
63 | === modified file 'src/provisioningserver/import_images/boot_image_mapping.py' |
64 | --- src/provisioningserver/import_images/boot_image_mapping.py 2014-05-19 05:27:29 +0000 |
65 | +++ src/provisioningserver/import_images/boot_image_mapping.py 2014-08-22 21:52:07 +0000 |
66 | @@ -19,6 +19,20 @@ |
67 | import json |
68 | |
69 | from provisioningserver.import_images.helpers import ImageSpec |
70 | +from provisioningserver.utils import dict_depth |
71 | + |
72 | + |
73 | +def gen_image_spec_with_resource(os, data): |
74 | + """Generate image and resource for given operating system and data.""" |
75 | + for arch in data: |
76 | + for subarch in data[arch]: |
77 | + for release in data[arch][subarch]: |
78 | + for label in data[arch][subarch][release]: |
79 | + image = ImageSpec( |
80 | + os=os, arch=arch, subarch=subarch, |
81 | + release=release, label=label) |
82 | + resource = data[arch][subarch][release][label] |
83 | + yield image, resource |
84 | |
85 | |
86 | class BootImageMapping: |
87 | @@ -57,11 +71,12 @@ |
88 | # Keep that format. |
89 | data = {} |
90 | for image, resource in self.items(): |
91 | - arch, subarch, release, label = image |
92 | - data.setdefault(arch, {}) |
93 | - data[arch].setdefault(subarch, {}) |
94 | - data[arch][subarch].setdefault(release, {}) |
95 | - data[arch][subarch][release][label] = resource |
96 | + os, arch, subarch, release, label = image |
97 | + data.setdefault(os, {}) |
98 | + data[os].setdefault(arch, {}) |
99 | + data[os][arch].setdefault(subarch, {}) |
100 | + data[os][arch][subarch].setdefault(release, {}) |
101 | + data[os][arch][subarch][release][label] = resource |
102 | return json.dumps(data, sort_keys=True) |
103 | |
104 | @staticmethod |
105 | @@ -79,14 +94,16 @@ |
106 | except ValueError: |
107 | return mapping |
108 | |
109 | - for arch in data: |
110 | - for subarch in data[arch]: |
111 | - for release in data[arch][subarch]: |
112 | - for label in data[arch][subarch][release]: |
113 | - image = ImageSpec( |
114 | - arch=arch, subarch=subarch, release=release, |
115 | - label=label) |
116 | - resource = data[arch][subarch][release][label] |
117 | - mapping.setdefault(image, resource) |
118 | - |
119 | + depth = dict_depth(data) |
120 | + if depth == 5: |
121 | + # Support for older data. This has no operating system, then |
122 | + # it is ubuntu. |
123 | + for image, resource in gen_image_spec_with_resource( |
124 | + "ubuntu", data): |
125 | + mapping.setdefault(image, resource) |
126 | + elif depth == 6: |
127 | + for os in data: |
128 | + for image, resource in gen_image_spec_with_resource( |
129 | + os, data[os]): |
130 | + mapping.setdefault(image, resource) |
131 | return mapping |
132 | |
133 | === modified file 'src/provisioningserver/import_images/download_descriptions.py' |
134 | --- src/provisioningserver/import_images/download_descriptions.py 2014-08-13 21:49:35 +0000 |
135 | +++ src/provisioningserver/import_images/download_descriptions.py 2014-08-22 21:52:07 +0000 |
136 | @@ -26,6 +26,7 @@ |
137 | BootImageMapping, |
138 | ) |
139 | from provisioningserver.import_images.helpers import ( |
140 | + get_os_from_product, |
141 | get_signing_policy, |
142 | ImageSpec, |
143 | maaslog, |
144 | @@ -77,10 +78,11 @@ |
145 | def insert_item(self, data, src, target, pedigree, contentsource): |
146 | """Overridable from `BasicMirrorWriter`.""" |
147 | item = products_exdata(src, pedigree) |
148 | + os = get_os_from_product(item) |
149 | arch, subarches = item['arch'], item['subarches'] |
150 | release = item['release'] |
151 | label = item['label'] |
152 | - base_image = ImageSpec(arch, None, release, label) |
153 | + base_image = ImageSpec(os, arch, None, release, label) |
154 | compact_item = clean_up_repo_item(item) |
155 | for subarch in subarches.split(','): |
156 | self.boot_images_dict.setdefault( |
157 | @@ -105,13 +107,14 @@ |
158 | return filter_value in ('*', property_value) |
159 | |
160 | |
161 | -def image_passes_filter(filters, arch, subarch, release, label): |
162 | +def image_passes_filter(filters, os, arch, subarch, release, label): |
163 | """Filter a boot image against configured import filters. |
164 | |
165 | :param filters: A list of dicts describing the filters, as in `boot_merge`. |
166 | If the list is empty, or `None`, any image matches. Any entry in a |
167 | filter may be a string containing just an asterisk (`*`) to denote that |
168 | the entry will match any value. |
169 | + :param os: The given boot image's operating system. |
170 | :param arch: The given boot image's architecture. |
171 | :param subarch: The given boot image's subarchitecture. |
172 | :param release: The given boot image's OS release. |
173 | @@ -122,6 +125,7 @@ |
174 | return True |
175 | for filter_dict in filters: |
176 | item_matches = ( |
177 | + value_passes_filter(filter_dict['os'], os) and |
178 | value_passes_filter(filter_dict['release'], release) and |
179 | value_passes_filter_list(filter_dict['arches'], arch) and |
180 | value_passes_filter_list(filter_dict['subarches'], subarch) and |
181 | @@ -143,21 +147,22 @@ |
182 | in-place. |
183 | :param additions: A second `BootImageMapping`, which will be used as a |
184 | source of additional entries. |
185 | - :param filters: List of dicts, each of which contains 'arch', 'subarch', |
186 | - and 'release' keys. If given, entries are only considered for copying |
187 | - from `additions` to `destination` if they match at least one of the |
188 | - filters. Entries in the filter may be the string `*` (or for entries |
189 | - that are lists, may contain the string `*`) to make them match any |
190 | - value. |
191 | + :param filters: List of dicts, each of which contains 'os', arch', |
192 | + 'subarch', 'release', and 'label' keys. If given, entries are only |
193 | + considered for copying from `additions` to `destination` if they match |
194 | + at least one of the filters. Entries in the filter may be the string |
195 | + `*` (or for entries that are lists, may contain the string `*`) to make |
196 | + them match any value. |
197 | """ |
198 | for image, resource in additions.items(): |
199 | - arch, subarch, release, label = image |
200 | - if image_passes_filter(filters, arch, subarch, release, label): |
201 | + os, arch, subarch, release, label = image |
202 | + if image_passes_filter( |
203 | + filters, os, arch, subarch, release, label): |
204 | maaslog.debug( |
205 | "Merging boot resource for %s/%s/%s/%s.", |
206 | arch, subarch, release, label) |
207 | # Do not override an existing entry with the same |
208 | - # arch/subarch/release/label: the first entry found takes |
209 | + # os/arch/subarch/release/label: the first entry found takes |
210 | # precedence. |
211 | destination.setdefault(image, resource) |
212 | |
213 | @@ -188,6 +193,6 @@ |
214 | boot = BootImageMapping() |
215 | for source in sources: |
216 | repo_boot = download_image_descriptions( |
217 | - source['url'], keyring=source['keyring']) |
218 | + source['url'], keyring=source.get('keyring', None)) |
219 | boot_merge(boot, repo_boot, source['selections']) |
220 | return boot |
221 | |
222 | === modified file 'src/provisioningserver/import_images/download_resources.py' |
223 | --- src/provisioningserver/import_images/download_resources.py 2014-08-13 21:49:35 +0000 |
224 | +++ src/provisioningserver/import_images/download_resources.py 2014-08-22 21:52:07 +0000 |
225 | @@ -21,6 +21,7 @@ |
226 | import os.path |
227 | |
228 | from provisioningserver.import_images.helpers import ( |
229 | + get_os_from_product, |
230 | get_signing_policy, |
231 | maaslog, |
232 | ) |
233 | @@ -121,7 +122,8 @@ |
234 | return [(root_image_path, 'root-image'), (root_tgz_path, 'root-tgz')] |
235 | |
236 | |
237 | -def link_resources(snapshot_path, links, arch, release, label, subarches): |
238 | +def link_resources(snapshot_path, links, osystem, arch, release, label, |
239 | + subarches): |
240 | """Hardlink entries in the snapshot directory to resources in the cache. |
241 | |
242 | This creates file entries in the snapshot directory for boot resources |
243 | @@ -132,6 +134,7 @@ |
244 | the cache. Each link is described as a tuple of (path, logical |
245 | name). The path points to a file in the cache directory. The logical |
246 | name will be link's filename, without path. |
247 | + :param osystem: Operating system with this boot image supports. |
248 | :param arch: Architecture which this boot image supports. |
249 | :param release: OS release of which this boot image is a part. |
250 | :param label: OS release label of which this boot image is a part, e.g. |
251 | @@ -143,7 +146,8 @@ |
252 | for older Ubuntu releases. |
253 | """ |
254 | for subarch in subarches: |
255 | - directory = os.path.join(snapshot_path, arch, subarch, release, label) |
256 | + directory = os.path.join( |
257 | + snapshot_path, osystem, arch, subarch, release, label) |
258 | if not os.path.exists(directory): |
259 | os.makedirs(directory) |
260 | for cached_file, logical_name in links: |
261 | @@ -194,11 +198,12 @@ |
262 | links = insert_file( |
263 | self.store, ftype, tag, checksums, size, contentsource) |
264 | |
265 | + os = get_os_from_product(item) |
266 | subarches = self.product_mapping.get(item) |
267 | link_resources( |
268 | snapshot_path=self.root_path, links=links, |
269 | - arch=item['arch'], release=item['release'], label=item['label'], |
270 | - subarches=subarches) |
271 | + osystem=os, arch=item['arch'], release=item['release'], |
272 | + label=item['label'], subarches=subarches) |
273 | |
274 | |
275 | def download_boot_resources(path, store, snapshot_path, product_mapping, |
276 | @@ -262,7 +267,6 @@ |
277 | """ |
278 | storage_path = os.path.abspath(storage_path) |
279 | snapshot_path = compose_snapshot_path(storage_path) |
280 | - ubuntu_path = os.path.join(snapshot_path, 'ubuntu') |
281 | # Use a FileStore as our ObjectStore implementation. It will write to the |
282 | # cache directory. |
283 | if store is None: |
284 | @@ -273,7 +277,7 @@ |
285 | |
286 | for source in sources: |
287 | download_boot_resources( |
288 | - source['url'], store, ubuntu_path, product_mapping, |
289 | + source['url'], store, snapshot_path, product_mapping, |
290 | keyring_file=source.get('keyring')), |
291 | |
292 | return snapshot_path |
293 | |
294 | === modified file 'src/provisioningserver/import_images/helpers.py' |
295 | --- src/provisioningserver/import_images/helpers.py 2014-08-15 04:03:34 +0000 |
296 | +++ src/provisioningserver/import_images/helpers.py 2014-08-22 21:52:07 +0000 |
297 | @@ -27,6 +27,7 @@ |
298 | |
299 | # A tuple of the items that together select a boot image. |
300 | ImageSpec = namedtuple(b'ImageSpec', [ |
301 | + 'os', |
302 | 'arch', |
303 | 'subarch', |
304 | 'release', |
305 | |
306 | === modified file 'src/provisioningserver/import_images/testing/factory.py' |
307 | --- src/provisioningserver/import_images/testing/factory.py 2014-05-19 02:45:04 +0000 |
308 | +++ src/provisioningserver/import_images/testing/factory.py 2014-08-22 21:52:07 +0000 |
309 | @@ -16,6 +16,7 @@ |
310 | 'make_boot_resource', |
311 | 'make_image_spec', |
312 | 'make_maas_meta', |
313 | + 'make_maas_meta_without_os', |
314 | 'set_resource', |
315 | ] |
316 | |
317 | @@ -31,6 +32,12 @@ |
318 | def make_maas_meta(): |
319 | """Return fake maas.meta data.""" |
320 | return dedent("""\ |
321 | + {"ubuntu": {"amd64": {"generic": {"precise": {"release": {"content_id": "com.ubuntu.maas:v2:download", "path": "precise/amd64/20140410/raring/generic/boot-kernel", "product_name": "com.ubuntu.maas:v2:boot:12.04:amd64:hwe-r", "subarches": "generic,hwe-p,hwe-q,hwe-r", "version_name": "20140410"}}, "trusty": {"release": {"content_id": "com.ubuntu.maas:v2:download", "path": "trusty/amd64/20140416.1/root-image.gz", "product_name": "com.ubuntu.maas:v2:boot:14.04:amd64:hwe-t", "subarches": "generic,hwe-p,hwe-q,hwe-r,hwe-s,hwe-t", "version_name": "20140416.1"}}}, "hwe-s": {"precise": {"release": {"content_id": "com.ubuntu.maas:v2:download", "path": "precise/amd64/20140410/saucy/generic/boot-kernel", "product_name": "com.ubuntu.maas:v2:boot:12.04:amd64:hwe-s", "subarches": "generic,hwe-p,hwe-q,hwe-r,hwe-s", "version_name": "20140410"}}}}}}""") # NOQA |
322 | + |
323 | + |
324 | +def make_maas_meta_without_os(): |
325 | + """Return fake maas.meta data, without the os field.""" |
326 | + return dedent("""\ |
327 | {"amd64": {"generic": {"precise": {"release": {"content_id": "com.ubuntu.maas:v2:download", "path": "precise/amd64/20140410/raring/generic/boot-kernel", "product_name": "com.ubuntu.maas:v2:boot:12.04:amd64:hwe-r", "subarches": "generic,hwe-p,hwe-q,hwe-r", "version_name": "20140410"}}, "trusty": {"release": {"content_id": "com.ubuntu.maas:v2:download", "path": "trusty/amd64/20140416.1/root-image.gz", "product_name": "com.ubuntu.maas:v2:boot:14.04:amd64:hwe-t", "subarches": "generic,hwe-p,hwe-q,hwe-r,hwe-s,hwe-t", "version_name": "20140416.1"}}}, "hwe-s": {"precise": {"release": {"content_id": "com.ubuntu.maas:v2:download", "path": "precise/amd64/20140410/saucy/generic/boot-kernel", "product_name": "com.ubuntu.maas:v2:boot:12.04:amd64:hwe-s", "subarches": "generic,hwe-p,hwe-q,hwe-r,hwe-s", "version_name": "20140410"}}}}}""") # NOQA |
328 | |
329 | |
330 | @@ -46,6 +53,7 @@ |
331 | def make_image_spec(): |
332 | """Return an `ImageSpec` with random values.""" |
333 | return ImageSpec( |
334 | + factory.make_name('os'), |
335 | factory.make_name('arch'), |
336 | factory.make_name('subarch'), |
337 | factory.make_name('release'), |
338 | |
339 | === modified file 'src/provisioningserver/import_images/tests/test_boot_image_mapping.py' |
340 | --- src/provisioningserver/import_images/tests/test_boot_image_mapping.py 2014-05-19 05:27:29 +0000 |
341 | +++ src/provisioningserver/import_images/tests/test_boot_image_mapping.py 2014-08-22 21:52:07 +0000 |
342 | @@ -24,6 +24,7 @@ |
343 | from provisioningserver.import_images.testing.factory import ( |
344 | make_image_spec, |
345 | make_maas_meta, |
346 | + make_maas_meta_without_os, |
347 | set_resource, |
348 | ) |
349 | |
350 | @@ -78,9 +79,11 @@ |
351 | image_dict = set_resource(image_spec=image, resource=resource) |
352 | self.assertEqual( |
353 | { |
354 | - image.arch: { |
355 | - image.subarch: { |
356 | - image.release: {image.label: resource}, |
357 | + image.os: { |
358 | + image.arch: { |
359 | + image.subarch: { |
360 | + image.release: {image.label: resource}, |
361 | + }, |
362 | }, |
363 | }, |
364 | }, |
365 | @@ -97,10 +100,12 @@ |
366 | image_dict, image._replace(release=other_release), resource2) |
367 | self.assertEqual( |
368 | { |
369 | - image.arch: { |
370 | - image.subarch: { |
371 | - image.release: {image.label: resource1}, |
372 | - other_release: {image.label: resource2}, |
373 | + image.os: { |
374 | + image.arch: { |
375 | + image.subarch: { |
376 | + image.release: {image.label: resource1}, |
377 | + other_release: {image.label: resource2}, |
378 | + }, |
379 | }, |
380 | }, |
381 | }, |
382 | @@ -114,6 +119,12 @@ |
383 | dumped = mapping.dump_json() |
384 | self.assertEqual(test_meta_file_content, dumped) |
385 | |
386 | + def test_load_json_result_of_old_data_uses_ubuntu_as_os(self): |
387 | + test_meta_file_content = make_maas_meta_without_os() |
388 | + mapping = BootImageMapping.load_json(test_meta_file_content) |
389 | + os = {image.os for image, _ in mapping.items()}.pop() |
390 | + self.assertEqual('ubuntu', os) |
391 | + |
392 | def test_load_json_returns_empty_mapping_for_invalid_json(self): |
393 | bad_json = "" |
394 | mapping = BootImageMapping.load_json(bad_json) |
395 | |
396 | === modified file 'src/provisioningserver/import_images/tests/test_boot_resources.py' |
397 | --- src/provisioningserver/import_images/tests/test_boot_resources.py 2014-08-13 21:49:35 +0000 |
398 | +++ src/provisioningserver/import_images/tests/test_boot_resources.py 2014-08-22 21:52:07 +0000 |
399 | @@ -114,7 +114,8 @@ |
400 | self.patch( |
401 | provisioningserver.config, 'BOOT_RESOURCES_STORAGE', self.storage) |
402 | self.image = make_image_spec() |
403 | - self.arch, self.subarch, self.release, self.label = self.image |
404 | + self.os, self.arch, self.subarch, \ |
405 | + self.release, self.label = self.image |
406 | self.repo = self.make_simplestreams_repo(self.image) |
407 | |
408 | def patch_maaslog(self): |
409 | @@ -212,6 +213,7 @@ |
410 | 'subarches': [image_spec.subarch], |
411 | 'release': image_spec.release, |
412 | 'arch': image_spec.arch, |
413 | + 'os': image_spec.os, |
414 | }, |
415 | }, |
416 | } |
417 | @@ -255,6 +257,7 @@ |
418 | 'url': self.repo, |
419 | 'selections': [ |
420 | { |
421 | + 'os': self.os, |
422 | 'release': self.release, |
423 | 'arches': [self.arch], |
424 | 'subarches': [self.subarch], |
425 | @@ -287,6 +290,7 @@ |
426 | self.patch(boot_method, 'install_bootloader') |
427 | |
428 | args = self.make_working_args() |
429 | + osystem = self.os |
430 | arch = self.arch |
431 | subarch = self.subarch |
432 | release = self.release |
433 | @@ -305,16 +309,18 @@ |
434 | self.assertThat(os.path.join(current, 'maas.tgt'), FileExists()) |
435 | self.assertThat( |
436 | os.path.join( |
437 | - current, 'ubuntu', arch, subarch, self.release, self.label), |
438 | + current, osystem, arch, subarch, self.release, self.label), |
439 | DirExists()) |
440 | |
441 | # Verify the contents of the "meta" file. |
442 | with open(os.path.join(current, 'maas.meta'), 'rb') as meta_file: |
443 | meta_data = json.load(meta_file) |
444 | - self.assertEqual([arch], meta_data.keys()) |
445 | - self.assertEqual([subarch], meta_data[arch].keys()) |
446 | - self.assertEqual([release], meta_data[arch][subarch].keys()) |
447 | - self.assertEqual([label], meta_data[arch][subarch][release].keys()) |
448 | + self.assertEqual([osystem], meta_data.keys()) |
449 | + self.assertEqual([arch], meta_data[osystem].keys()) |
450 | + self.assertEqual([subarch], meta_data[osystem][arch].keys()) |
451 | + self.assertEqual([release], meta_data[osystem][arch][subarch].keys()) |
452 | + self.assertEqual( |
453 | + [label], meta_data[osystem][arch][subarch][release].keys()) |
454 | self.assertItemsEqual( |
455 | [ |
456 | 'content_id', |
457 | @@ -323,7 +329,7 @@ |
458 | 'version_name', |
459 | 'subarches', |
460 | ], |
461 | - meta_data[arch][subarch][release][label].keys()) |
462 | + meta_data[osystem][arch][subarch][release][label].keys()) |
463 | |
464 | def test_warns_if_no_sources_selected(self): |
465 | self.patch_maaslog() |
466 | @@ -442,6 +448,7 @@ |
467 | 'url': factory.make_name("something"), |
468 | 'selections': [ |
469 | { |
470 | + 'os': factory.make_name("os"), |
471 | 'release': factory.make_name("release"), |
472 | 'arches': [factory.make_name("arch")], |
473 | 'subarches': [factory.make_name("subarch")], |
474 | @@ -477,6 +484,7 @@ |
475 | 'url': factory.make_name("something"), |
476 | 'selections': [ |
477 | { |
478 | + 'os': factory.make_name("os"), |
479 | 'release': factory.make_name("release"), |
480 | 'arches': [factory.make_name("arch")], |
481 | 'subarches': [factory.make_name("subarch")], |
482 | |
483 | === modified file 'src/provisioningserver/import_images/tests/test_download_descriptions.py' |
484 | --- src/provisioningserver/import_images/tests/test_download_descriptions.py 2014-08-13 21:49:35 +0000 |
485 | +++ src/provisioningserver/import_images/tests/test_download_descriptions.py 2014-08-22 21:52:07 +0000 |
486 | @@ -92,6 +92,7 @@ |
487 | if image_spec is None: |
488 | image_spec = make_image_spec() |
489 | return { |
490 | + 'os': image_spec.os, |
491 | 'arches': [image_spec.arch], |
492 | 'subarches': [image_spec.subarch], |
493 | 'release': image_spec.release, |
494 | @@ -99,30 +100,32 @@ |
495 | } |
496 | |
497 | def test_any_image_passes_none_filter(self): |
498 | - arch, subarch, release, label = make_image_spec() |
499 | + os, arch, subarch, release, label = make_image_spec() |
500 | self.assertTrue( |
501 | download_descriptions.image_passes_filter( |
502 | - None, arch, subarch, release, label)) |
503 | + None, os, arch, subarch, release, label)) |
504 | |
505 | def test_any_image_passes_empty_filter(self): |
506 | - arch, subarch, release, label = make_image_spec() |
507 | + os, arch, subarch, release, label = make_image_spec() |
508 | self.assertTrue( |
509 | download_descriptions.image_passes_filter( |
510 | - [], arch, subarch, release, label)) |
511 | + [], os, arch, subarch, release, label)) |
512 | |
513 | def test_image_passes_matching_filter(self): |
514 | image = make_image_spec() |
515 | self.assertTrue( |
516 | download_descriptions.image_passes_filter( |
517 | [self.make_filter_from_image(image)], |
518 | - image.arch, image.subarch, image.release, image.label)) |
519 | + image.os, image.arch, image.subarch, |
520 | + image.release, image.label)) |
521 | |
522 | def test_image_does_not_pass_nonmatching_filter(self): |
523 | image = make_image_spec() |
524 | self.assertFalse( |
525 | download_descriptions.image_passes_filter( |
526 | [self.make_filter_from_image()], |
527 | - image.arch, image.subarch, image.release, image.label)) |
528 | + image.os, image.arch, image.subarch, |
529 | + image.release, image.label)) |
530 | |
531 | def test_image_passes_if_one_filter_matches(self): |
532 | image = make_image_spec() |
533 | @@ -132,7 +135,9 @@ |
534 | self.make_filter_from_image(), |
535 | self.make_filter_from_image(image), |
536 | self.make_filter_from_image(), |
537 | - ], image.arch, image.subarch, image.release, image.label)) |
538 | + ], |
539 | + image.os, image.arch, image.subarch, |
540 | + image.release, image.label)) |
541 | |
542 | def test_filter_checks_release(self): |
543 | image = make_image_spec() |
544 | @@ -141,7 +146,9 @@ |
545 | [ |
546 | self.make_filter_from_image(image._replace( |
547 | release=factory.make_name('other-release'))) |
548 | - ], image.arch, image.subarch, image.release, image.label)) |
549 | + ], |
550 | + image.os, image.arch, image.subarch, |
551 | + image.release, image.label)) |
552 | |
553 | def test_filter_checks_arches(self): |
554 | image = make_image_spec() |
555 | @@ -150,7 +157,9 @@ |
556 | [ |
557 | self.make_filter_from_image(image._replace( |
558 | arch=factory.make_name('other-arch'))) |
559 | - ], image.arch, image.subarch, image.release, image.label)) |
560 | + ], |
561 | + image.os, image.arch, image.subarch, |
562 | + image.release, image.label)) |
563 | |
564 | def test_filter_checks_subarches(self): |
565 | image = make_image_spec() |
566 | @@ -159,7 +168,9 @@ |
567 | [ |
568 | self.make_filter_from_image(image._replace( |
569 | subarch=factory.make_name('other-subarch'))) |
570 | - ], image.arch, image.subarch, image.release, image.label)) |
571 | + ], |
572 | + image.os, image.arch, image.subarch, |
573 | + image.release, image.label)) |
574 | |
575 | def test_filter_checks_labels(self): |
576 | image = make_image_spec() |
577 | @@ -168,7 +179,9 @@ |
578 | [ |
579 | self.make_filter_from_image(image._replace( |
580 | label=factory.make_name('other-label'))) |
581 | - ], image.arch, image.subarch, image.release, image.label)) |
582 | + ], |
583 | + image.os, image.arch, image.subarch, |
584 | + image.release, image.label)) |
585 | |
586 | |
587 | class TestBootMerge(MAASTestCase): |
588 | @@ -187,6 +200,7 @@ |
589 | def test_obeys_filters(self): |
590 | filters = [ |
591 | { |
592 | + 'os': factory.make_name('os'), |
593 | 'arches': [factory.make_name('other-arch')], |
594 | 'subarches': [factory.make_name('other-subarch')], |
595 | 'release': factory.make_name('other-release'), |
596 | |
597 | === modified file 'src/provisioningserver/import_images/tests/test_download_resources.py' |
598 | --- src/provisioningserver/import_images/tests/test_download_resources.py 2014-06-10 14:45:14 +0000 |
599 | +++ src/provisioningserver/import_images/tests/test_download_resources.py 2014-08-22 21:52:07 +0000 |
600 | @@ -55,7 +55,6 @@ |
601 | storage_path = self.make_dir() |
602 | snapshot_path = download_resources.compose_snapshot_path( |
603 | storage_path) |
604 | - ubuntu_path = os.path.join(snapshot_path, 'ubuntu') |
605 | cache_path = os.path.join(storage_path, 'cache') |
606 | file_store = FileStore(cache_path) |
607 | source = { |
608 | @@ -70,7 +69,7 @@ |
609 | self.assertThat( |
610 | fake, |
611 | MockCalledWith( |
612 | - source['url'], file_store, ubuntu_path, product_mapping, |
613 | + source['url'], file_store, snapshot_path, product_mapping, |
614 | keyring_file=source['keyring'])) |
615 | |
616 | |
617 | |
618 | === modified file 'src/provisioningserver/tests/test_config.py' |
619 | --- src/provisioningserver/tests/test_config.py 2014-07-18 17:05:57 +0000 |
620 | +++ src/provisioningserver/tests/test_config.py 2014-08-22 21:52:07 +0000 |
621 | @@ -437,6 +437,7 @@ |
622 | 'keyring_data': None, |
623 | 'selections': [ |
624 | { |
625 | + 'os': '*', |
626 | 'release': '*', |
627 | 'labels': ['*'], |
628 | 'arches': ['*'], |
629 | @@ -452,6 +453,7 @@ |
630 | 'keyring': factory.make_name('keyring'), |
631 | 'keyring_data': factory.make_string(), |
632 | 'selections': [{ |
633 | + 'os': factory.make_name('os'), |
634 | 'release': factory.make_name('release'), |
635 | 'labels': [factory.make_name('label')], |
636 | 'arches': [factory.make_name('arch')], |
637 | |
638 | === modified file 'src/provisioningserver/utils/__init__.py' |
639 | --- src/provisioningserver/utils/__init__.py 2014-08-13 21:49:35 +0000 |
640 | +++ src/provisioningserver/utils/__init__.py 2014-08-22 21:52:07 +0000 |
641 | @@ -143,6 +143,13 @@ |
642 | } |
643 | |
644 | |
645 | +def dict_depth(d, depth=0): |
646 | + """Returns the max depth of a dictionary.""" |
647 | + if not isinstance(d, dict) or not d: |
648 | + return depth |
649 | + return max(dict_depth(v, depth + 1) for _, v in d.iteritems()) |
650 | + |
651 | + |
652 | def split_lines(input, separator): |
653 | """Split each item from `input` into a key/value pair.""" |
654 | return (line.split(separator, 1) for line in input if line.strip() != '') |
Blake, just a few comments and questions below.