Merge lp:~pwlars/maas/core-simplestreams-rebased into lp:~maas-committers/maas/trunk

Proposed by Paul Larson
Status: Merged
Merge reported by: Andres Rodriguez
Merged at revision: not available
Proposed branch: lp:~pwlars/maas/core-simplestreams-rebased
Merge into: lp:~maas-committers/maas/trunk
Prerequisite: lp:~pwlars/maas/ubuntu-core-rename
Diff against target: 274 lines (+181/-18)
4 files modified
src/maasserver/bootresources.py (+22/-2)
src/maasserver/tests/test_bootresources.py (+93/-0)
src/provisioningserver/import_images/download_descriptions.py (+27/-15)
src/provisioningserver/import_images/tests/test_download_descriptions.py (+39/-1)
To merge this branch: bzr merge lp:~pwlars/maas/core-simplestreams-rebased
Reviewer Review Type Date Requested Status
Paul Larson (community) Needs Resubmitting
Blake Rouse (community) Needs Fixing
Review via email: mp+322538@code.launchpad.net

This proposal supersedes a proposal from 2017-04-13.

Commit message

Support for importing ubuntu-core images through simple streams

Description of the change

Here's a version of the changes to support ubuntu-core with simplestreams rebased on top of the rename patch, in order to clean up the history a bit. This replaces lp:~pwlars/maas/core-simplestreams

To use this, you'll need the image at http://people.canonical.com/~rharper/ubuntu-core/20170331/curtin-ubuntu-core-16-pc-amd64.img.xz

You'll also need to create the simplestreams data using https://code.launchpad.net/~ltrager/maas-images/create/+merge/321213
Filetype should be root-dd.xz and the image filename should also be root-dd.xz

Using these, and the existing support for ubuntu core in the image, I was able to get it to import and provision with the provided image.

To post a comment you must log in.
5978. By Paul Larson

Add some unit tests for ubuntu-core simplestreams

Revision history for this message
Paul Larson (pwlars) wrote :

Added a few unit tests

review: Needs Resubmitting
Revision history for this message
Blake Rouse (blake-rouse) wrote :

Looks good, but still missing tests for the changes in the provisioningserver.

review: Needs Fixing
5979. By Paul Larson

Add unit tests for ubuntu core specific items in download_descriptions.py

Revision history for this message
Paul Larson (pwlars) wrote :

> Looks good, but still missing tests for the changes in the provisioningserver.
Thanks, I've added a couple of tests to ensure that the weird settings we use for ubuntu-core are set up correctly when os=='ubuntu-core'. Specifically, kflavor should be the kernel_snap and release should be {release}-{gadget}.

review: Needs Resubmitting
Revision history for this message
Andres Rodriguez (andreserl) wrote :

Marking this branch as merged provided it landed on bzr6015

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/maasserver/bootresources.py'
2--- src/maasserver/bootresources.py 2017-03-31 19:38:34 +0000
3+++ src/maasserver/bootresources.py 2017-04-18 21:57:03 +0000
4@@ -35,7 +35,10 @@
5 StreamingHttpResponse,
6 )
7 from django.shortcuts import get_object_or_404
8-from maasserver import locks
9+from maasserver import (
10+ __version__,
11+ locks,
12+)
13 from maasserver.bootsources import (
14 cache_boot_sources,
15 ensure_boot_source_definition,
16@@ -485,7 +488,13 @@
17 arch = product['arch']
18 kflavor = product.get('kflavor')
19 bootloader_type = product.get('bootloader-type')
20- if bootloader_type is None:
21+ if os == 'ubuntu-core':
22+ kflavor = product.get('kernel_snap', 'generic')
23+ release = product['release']
24+ gadget = product['gadget_snap']
25+ architecture = '%s/generic' % arch
26+ series = "%s-%s" % (release, gadget)
27+ elif bootloader_type is None:
28 # The rack controller assumes the subarch is the kernel. We need to
29 # include the kflavor in the subarch otherwise the rack will
30 # overwrite the generic kernel with each kernel flavor.
31@@ -1042,6 +1051,8 @@
32 # Compile a regex to validate Ubuntu product names. This only allows
33 # V2 and V3 Ubuntu products.
34 self.ubuntu_regex = re.compile('.*:v[23]:.*', re.IGNORECASE)
35+ # Compile a regex to validate Ubuntu Core. This only allows v4
36+ self.ubuntucore_regex = re.compile('.*:v4:.*', re.IGNORECASE)
37
38 def load_products(self, path=None, content_id=None):
39 """Overridable from `BasicMirrorWriter`."""
40@@ -1057,6 +1068,15 @@
41
42 def _validate_ubuntu(self, data, product_name):
43 osystem = data.get('os')
44+ if osystem == 'ubuntu-core':
45+ maas_version = tuple(int(x) for x in __version__.split('.'))
46+ supported_version = tuple(
47+ int(x) for x in data.get('maas_supported').split('.'))
48+ if (self.ubuntucore_regex.search(product_name) and
49+ maas_version >= supported_version):
50+ return True
51+ else:
52+ return False
53 if 'ubuntu' not in osystem.lower():
54 # It's not an Ubuntu product, nothing to validate.
55 return True
56
57=== modified file 'src/maasserver/tests/test_bootresources.py'
58--- src/maasserver/tests/test_bootresources.py 2017-03-31 20:18:23 +0000
59+++ src/maasserver/tests/test_bootresources.py 2017-04-18 21:57:03 +0000
60@@ -2342,3 +2342,96 @@
61 mock_insert = self.patch(boot_resource_repo_writer.store, 'insert')
62 boot_resource_repo_writer.insert_item(data, src, None, pedigree, None)
63 self.assertThat(mock_insert, MockNotCalled())
64+
65+
66+class TestBootResourceRepoWriterCore(MAASServerTestCase):
67+ """Tests for `BootResourceRepoWriter` on ubuntu-core images."""
68+
69+ def create_simplestream(self, ftypes, stream_version=None):
70+ arch = 'amd64'
71+ if stream_version is None:
72+ stream_version = 'v4'
73+ release = '16'
74+ gadget_snap = 'pc'
75+ channel = 'stable'
76+ product = "com.ubuntu.maas.daily:%s:%s:%s:%s:%s" % (
77+ stream_version, release, arch, gadget_snap, channel)
78+ version = datetime.now().date().strftime('%Y%m%d.0')
79+ versions = {
80+ version: {
81+ 'items': {
82+ ftype: {
83+ 'sha256': factory.make_name('sha256'),
84+ 'path': factory.make_name('path'),
85+ 'ftype': ftype,
86+ 'size': random.randint(0, 2**64),
87+ } for ftype in ftypes
88+ }
89+ }
90+ }
91+ products = {
92+ product: {
93+ 'label': 'daily',
94+ 'os': 'ubuntu-core',
95+ 'os_title': 'Ubuntu Core',
96+ 'arch': arch,
97+ 'brand_id': 'canonical',
98+ 'channel': channel,
99+ 'gadget_snap': gadget_snap,
100+ 'gadget_title': 'PC',
101+ 'kernel_snap': 'pc-kernel',
102+ 'maas_supported': '2.2',
103+ 'model_name': 'pc-amd64',
104+ 'release': release,
105+ 'release_title': '16',
106+ 'versions': versions,
107+ }
108+ }
109+ src = {
110+ 'datatype': 'image-downloads',
111+ 'format': 'products:1.0',
112+ 'updated': format_datetime(datetime.now()),
113+ 'products': products,
114+ 'content_id': 'com.ubuntu.maas:daily:v4:download'
115+ }
116+ return src, product, version
117+
118+ def test_insert_ubuntu_core_allows_ddxz(self):
119+ boot_resource_repo_writer = BootResourceRepoWriter(
120+ BootResourceStore(), None)
121+ src, product, version = self.create_simplestream([
122+ BOOT_RESOURCE_FILE_TYPE.ROOT_DDXZ
123+ ])
124+ data = src['products'][product]['versions'][version]['items'][
125+ BOOT_RESOURCE_FILE_TYPE.ROOT_DDXZ]
126+ pedigree = (product, version, BOOT_RESOURCE_FILE_TYPE.ROOT_DDXZ)
127+ mock_insert = self.patch(boot_resource_repo_writer.store, 'insert')
128+ boot_resource_repo_writer.insert_item(data, src, None, pedigree, None)
129+ self.assertThat(mock_insert, MockCalledOnce())
130+
131+ def test_ubuntu_core_requires_v4_stream(self):
132+ boot_resource_repo_writer = BootResourceRepoWriter(
133+ BootResourceStore(), None)
134+ src, product, version = self.create_simplestream([
135+ BOOT_RESOURCE_FILE_TYPE.ROOT_DDXZ
136+ ], 'v3')
137+ data = src['products'][product]['versions'][version]['items'][
138+ BOOT_RESOURCE_FILE_TYPE.ROOT_DDXZ]
139+ pedigree = (product, version, BOOT_RESOURCE_FILE_TYPE.ROOT_DDXZ)
140+ mock_insert = self.patch(boot_resource_repo_writer.store, 'insert')
141+ boot_resource_repo_writer.insert_item(data, src, None, pedigree, None)
142+ self.assertThat(mock_insert, MockNotCalled())
143+
144+ def test_ubuntu_core_checks_maas_supported_field(self):
145+ boot_resource_repo_writer = BootResourceRepoWriter(
146+ BootResourceStore(), None)
147+ src, product, version = self.create_simplestream([
148+ BOOT_RESOURCE_FILE_TYPE.ROOT_DDXZ
149+ ])
150+ src['products'][product]['maas_supported'] = '999.999'
151+ data = src['products'][product]['versions'][version]['items'][
152+ BOOT_RESOURCE_FILE_TYPE.ROOT_DDXZ]
153+ pedigree = (product, version, BOOT_RESOURCE_FILE_TYPE.ROOT_DDXZ)
154+ mock_insert = self.patch(boot_resource_repo_writer.store, 'insert')
155+ boot_resource_repo_writer.insert_item(data, src, None, pedigree, None)
156+ self.assertThat(mock_insert, MockNotCalled())
157
158=== modified file 'src/provisioningserver/import_images/download_descriptions.py'
159--- src/provisioningserver/import_images/download_descriptions.py 2017-01-28 00:51:47 +0000
160+++ src/provisioningserver/import_images/download_descriptions.py 2017-04-18 21:57:03 +0000
161@@ -91,24 +91,36 @@
162 label = item['label']
163 base_image = ImageSpec(os, arch, None, kflavor, release, label)
164 compact_item = clean_up_repo_item(item)
165- for subarch in subarches.split(','):
166+
167+ if os == 'ubuntu-core':
168+ # For Ubuntu Core we only want one entry per release/arch/gadget
169+ subarch = item.get('gadget_snap', 'generic')
170+ kflavor = item.get('kernel_snap', 'generic')
171+ release = "%s-%s" % (release, subarch)
172 self.boot_images_dict.setdefault(
173+ base_image._replace(
174+ subarch=subarch, kflavor=kflavor,
175+ release=release), compact_item)
176+ else:
177+ for subarch in subarches.split(','):
178+ self.boot_images_dict.setdefault(
179+ base_image._replace(subarch=subarch), compact_item)
180+
181+ # HWE resources need to map to a specfic resource, and not just to
182+ # any of the supported subarchitectures for that resource.
183+ subarch = item.get('subarch', 'generic')
184+ self.boot_images_dict.set(
185 base_image._replace(subarch=subarch), compact_item)
186
187- # HWE resources need to map to a specfic resource, and not just to
188- # any of the supported subarchitectures for that resource.
189- subarch = item.get('subarch', 'generic')
190- self.boot_images_dict.set(
191- base_image._replace(subarch=subarch), compact_item)
192-
193- if os == 'ubuntu' and item.get('version') is not None:
194- # HWE resources with generic, should map to the HWE that ships with
195- # that release. Starting with Xenial kernels changed from using the
196- # naming format hwe-<letter> to ga-<version>. Look for both.
197- hwe_archs = ["ga-%s" % item['version'], "hwe-%s" % release[0]]
198- if subarch in hwe_archs and 'generic' in subarches:
199- self.boot_images_dict.set(
200- base_image._replace(subarch='generic'), compact_item)
201+ if os == 'ubuntu' and item.get('version') is not None:
202+ # HWE resources with generic, should map to the HWE that ships
203+ # with that release. Starting with Xenial kernels changed from
204+ # using the naming format hwe-<letter> to ga-<version>. Look
205+ # for both.
206+ hwe_archs = ["ga-%s" % item['version'], "hwe-%s" % release[0]]
207+ if subarch in hwe_archs and 'generic' in subarches:
208+ self.boot_images_dict.set(
209+ base_image._replace(subarch='generic'), compact_item)
210
211 def sync(self, reader, path):
212 try:
213
214=== modified file 'src/provisioningserver/import_images/tests/test_download_descriptions.py'
215--- src/provisioningserver/import_images/tests/test_download_descriptions.py 2017-01-28 00:51:47 +0000
216+++ src/provisioningserver/import_images/tests/test_download_descriptions.py 2017-04-18 21:57:03 +0000
217@@ -236,7 +236,7 @@
218
219 def make_item(self, os=None, release=None, version=None, arch=None,
220 subarch=None, subarches=None, label=None,
221- bootloader_type=None):
222+ bootloader_type=None, gadget_snap=None, kernel_snap=None):
223 if os is None:
224 os = factory.make_name('os')
225 if release is None:
226@@ -266,10 +266,48 @@
227 'subarches': ','.join(subarches),
228 'label': label,
229 }
230+ if gadget_snap:
231+ item['gadget_snap'] = gadget_snap
232+ if kernel_snap:
233+ item['kernel_snap'] = kernel_snap
234 if bootloader_type is not None:
235 item['bootloader-type'] = bootloader_type
236 return item, clean_up_repo_item(item)
237
238+ def test_insert_item_ubuntu_core_kflavor_is_ksnap(self):
239+ boot_images_dict = BootImageMapping()
240+ kernel_snap = 'pc-kernel'
241+ dumper = RepoDumper(boot_images_dict)
242+ item, _ = self.make_item(
243+ os='ubuntu-core', release='16', gadget_snap='pc',
244+ kernel_snap=kernel_snap)
245+ self.patch(
246+ download_descriptions, 'products_exdata').return_value = item
247+ dumper.insert_item(
248+ sentinel.data, sentinel.src, sentinel.target,
249+ sentinel.pedigree, sentinel.contentsource)
250+ image_spec = list(boot_images_dict.mapping.keys())[0]
251+ self.assertEqual(kernel_snap, image_spec.kflavor)
252+
253+ def test_insert_item_ubuntu_core_release_combines_gadget(self):
254+ boot_images_dict = BootImageMapping()
255+ dumper = RepoDumper(boot_images_dict)
256+ gadget_snap = 'pc'
257+ release = '16'
258+ item, _ = self.make_item(
259+ os='ubuntu-core', release=release, gadget_snap=gadget_snap,
260+ kernel_snap='pc-kernel')
261+ # For ubuntu-core, the release should be a combination of the
262+ # gadget snap and the release
263+ expected_release = '%s-%s' % (release, gadget_snap)
264+ self.patch(
265+ download_descriptions, 'products_exdata').return_value = item
266+ dumper.insert_item(
267+ sentinel.data, sentinel.src, sentinel.target,
268+ sentinel.pedigree, sentinel.contentsource)
269+ image_spec = list(boot_images_dict.mapping.keys())[0]
270+ self.assertEqual(expected_release, image_spec.release)
271+
272 def test_insert_item_adds_item_per_subarch(self):
273 boot_images_dict = BootImageMapping()
274 dumper = RepoDumper(boot_images_dict)