Merge lp:~james-w/linaro-image-tools/hwpack-builder into lp:linaro-image-tools/11.11

Proposed by James Westby
Status: Merged
Merged at revision: 70
Proposed branch: lp:~james-w/linaro-image-tools/hwpack-builder
Merge into: lp:linaro-image-tools/11.11
Prerequisite: lp:~james-w/linaro-image-tools/package-fetcher-config-manager
Diff against target: 375 lines (+275/-45)
5 files modified
hwpack/builder.py (+42/-0)
hwpack/testing.py (+157/-0)
hwpack/tests/__init__.py (+1/-0)
hwpack/tests/test_builder.py (+74/-0)
hwpack/tests/test_hardwarepack.py (+1/-45)
To merge this branch: bzr merge lp:~james-w/linaro-image-tools/hwpack-builder
Reviewer Review Type Date Requested Status
Zygmunt Krynicki (community) Approve
Review via email: mp+35129@code.launchpad.net

Description of the change

Hi,

A larger branch this time.

This adds the class that orchestrates the different parts in order
to produce the hardware packs. It's fairly simple itself, and the
majority of the additions are test code and test helper code.

Thanks,

James

To post a comment you must log in.
Revision history for this message
Zygmunt Krynicki (zyga) wrote :

I just read the dif, didn't run the code yet. Looks very solid!

I like how matchers work, need to try them out

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'hwpack/builder.py'
--- hwpack/builder.py 1970-01-01 00:00:00 +0000
+++ hwpack/builder.py 2010-09-10 16:11:07 +0000
@@ -0,0 +1,42 @@
1import errno
2
3from hwpack.config import Config
4from hwpack.hardwarepack import HardwarePack, Metadata
5from hwpack.packages import PackageFetcher
6
7
8class ConfigFileMissing(Exception):
9
10 def __init__(self, filename):
11 self.filename = filename
12 super(ConfigFileMissing, self).__init__(
13 "No such config file: '%s'" % self.filename)
14
15
16class HardwarePackBuilder(object):
17
18 def __init__(self, config_path, version):
19 try:
20 with open(config_path) as fp:
21 self.config = Config(fp)
22 except IOError, e:
23 if e.errno == errno.ENOENT:
24 raise ConfigFileMissing(config_path)
25 raise
26 self.config.validate()
27 self.version = version
28
29 def build(self):
30 for architecture in self.config.architectures:
31 metadata = Metadata.from_config(
32 self.config, self.version, architecture)
33 hwpack = HardwarePack(metadata)
34 sources = self.config.sources
35 hwpack.add_apt_sources(sources)
36 fetcher = PackageFetcher(
37 sources.values(), architecture=architecture)
38 with fetcher:
39 packages = fetcher.fetch_packages(self.config.packages)
40 hwpack.add_packages(packages)
41 with open(hwpack.filename(), 'w') as f:
42 hwpack.to_file(f)
043
=== modified file 'hwpack/testing.py'
--- hwpack/testing.py 2010-09-02 15:30:36 +0000
+++ hwpack/testing.py 2010-09-10 16:11:07 +0000
@@ -7,8 +7,10 @@
7import tarfile7import tarfile
88
9from testtools import TestCase9from testtools import TestCase
10from testtools.matchers import Matcher, Mismatch
1011
11from hwpack.better_tarfile import writeable_tarfile12from hwpack.better_tarfile import writeable_tarfile
13from hwpack.tarfile_matchers import TarfileHasFile
12from hwpack.packages import get_packages_file, FetchedPackage14from hwpack.packages import get_packages_file, FetchedPackage
1315
1416
@@ -136,3 +138,158 @@
136 self.addCleanup(fixture.tearDown)138 self.addCleanup(fixture.tearDown)
137 fixture.setUp()139 fixture.setUp()
138 return fixture140 return fixture
141
142
143class ConfigFileFixture(object):
144
145 def __init__(self, contents):
146 self.contents = contents
147 self.filename = None
148
149 def setUp(self):
150 fh, self.filename = tempfile.mkstemp(prefix="hwpack-test-config-")
151 with os.fdopen(fh, 'w') as f:
152 f.write(self.contents)
153
154 def tearDown(self):
155 if self.filename is not None and os.path.exists(self.filename):
156 os.unlink(self.filename)
157
158
159class ChdirToTempdirFixture(object):
160
161 def __init__(self):
162 self._orig_dir = None
163 self.tempdir = None
164
165 def setUp(self):
166 self.tearDown()
167 self._orig_dir = os.getcwd()
168 self.tempdir = tempfile.mkdtemp(prefix="hwpack-tests-")
169 os.chdir(self.tempdir)
170
171 def tearDown(self):
172 if self._orig_dir is not None:
173 os.chdir(self._orig_dir)
174 self._orig_dir = None
175 if self.tempdir is not None and os.path.exists(self.tempdir):
176 shutil.rmtree(self.tempdir)
177 self.tempdir = None
178
179
180class MismatchesAll(Mismatch):
181 """A mismatch with many child mismatches."""
182
183 def __init__(self, mismatches):
184 self.mismatches = mismatches
185
186 def describe(self):
187 descriptions = ["Differences: ["]
188 for mismatch in self.mismatches:
189 descriptions.append(mismatch.describe())
190 descriptions.append("]\n")
191 return '\n'.join(descriptions)
192
193
194class MatchesAll(object):
195
196 def __init__(self, *matchers):
197 self.matchers = matchers
198
199 def __str__(self):
200 return 'MatchesAll(%s)' % ', '.join(map(str, self.matchers))
201
202 def match(self, matchee):
203 results = []
204 for matcher in self.matchers:
205 mismatch = matcher.match(matchee)
206 if mismatch is not None:
207 results.append(mismatch)
208 if results:
209 return MismatchesAll(results)
210 else:
211 return None
212
213
214class HardwarePackHasFile(TarfileHasFile):
215 """A subclass of TarfileHasFile specific to hardware packs.
216
217 We default to a set of attributes expected for files in a hardware
218 pack.
219 """
220
221 def __init__(self, path, **kwargs):
222 """Create a HardwarePackHasFile matcher.
223
224 The kwargs are the keyword arguments taken by TarfileHasFile.
225 If they are not given then defaults will be checked:
226 - The type should be a regular file
227 - If the content is given then the size will be checked
228 to ensure it indicates the length of the content
229 correctly.
230 - the mode is appropriate for the type. If the type is
231 regular file this is 0644, otherwise if it is
232 a directory then it is 0755.
233 - the linkname should be the empty string.
234 - the uid and gid should be 1000
235 - the uname and gname should be "user" and "group"
236 respectively.
237
238 :param path: the path that should be present.
239 :type path: str
240 """
241 kwargs.setdefault("type", tarfile.REGTYPE)
242 if "content" in kwargs:
243 kwargs.setdefault("size", len(kwargs["content"]))
244 if kwargs["type"] == tarfile.DIRTYPE:
245 kwargs.setdefault("mode", 0755)
246 else:
247 kwargs.setdefault("mode", 0644)
248 kwargs.setdefault("linkname", "")
249 kwargs.setdefault("uid", 1000)
250 kwargs.setdefault("gid", 1000)
251 kwargs.setdefault("uname", "user")
252 kwargs.setdefault("gname", "group")
253 # TODO: mtime checking
254 super(HardwarePackHasFile, self).__init__(path, **kwargs)
255
256
257class IsHardwarePack(Matcher):
258
259 def __init__(self, metadata, packages, sources):
260 self.metadata = metadata
261 self.packages = packages
262 self.sources = sources
263
264 def match(self, path):
265 tf = tarfile.open(name=path, mode="r:gz")
266 try:
267 matchers = []
268 matchers.append(HardwarePackHasFile("FORMAT", content="1.0\n"))
269 matchers.append(HardwarePackHasFile(
270 "metadata", content=str(self.metadata)))
271 manifest = ""
272 for package in self.packages:
273 manifest += "%s=%s\n" % (package.name, package.version)
274 matchers.append(HardwarePackHasFile("manifest", content=manifest))
275 matchers.append(HardwarePackHasFile("pkgs", type=tarfile.DIRTYPE))
276 for package in self.packages:
277 matchers.append(HardwarePackHasFile(
278 "pkgs/%s" % package.filename,
279 content=package.content.read()))
280 matchers.append(HardwarePackHasFile(
281 "pkgs/Packages", content=get_packages_file(self.packages)))
282 matchers.append(HardwarePackHasFile(
283 "sources.list.d", type=tarfile.DIRTYPE))
284 for source_id, sources_entry in self.sources.items():
285 matchers.append(HardwarePackHasFile(
286 "sources.list.d/%s" % source_id,
287 content="deb " + sources_entry + "\n"))
288 matchers.append(HardwarePackHasFile(
289 "sources.list.d.gpg", type=tarfile.DIRTYPE))
290 return MatchesAll(*matchers).match(tf)
291 finally:
292 tf.close()
293
294 def __str__(self):
295 return "Is a valid hardware pack."
139296
=== modified file 'hwpack/tests/__init__.py'
--- hwpack/tests/__init__.py 2010-09-01 20:33:33 +0000
+++ hwpack/tests/__init__.py 2010-09-10 16:11:07 +0000
@@ -3,6 +3,7 @@
3def test_suite():3def test_suite():
4 module_names = ['hwpack.tests.test_config',4 module_names = ['hwpack.tests.test_config',
5 'hwpack.tests.test_better_tarfile',5 'hwpack.tests.test_better_tarfile',
6 'hwpack.tests.test_builder',
6 'hwpack.tests.test_hardwarepack',7 'hwpack.tests.test_hardwarepack',
7 'hwpack.tests.test_packages',8 'hwpack.tests.test_packages',
8 'hwpack.tests.test_tarfile_matchers',9 'hwpack.tests.test_tarfile_matchers',
910
=== added file 'hwpack/tests/test_builder.py'
--- hwpack/tests/test_builder.py 1970-01-01 00:00:00 +0000
+++ hwpack/tests/test_builder.py 2010-09-10 16:11:07 +0000
@@ -0,0 +1,74 @@
1import os
2
3from testtools import TestCase
4
5from hwpack.builder import ConfigFileMissing, HardwarePackBuilder
6from hwpack.config import HwpackConfigError
7from hwpack.hardwarepack import Metadata
8from hwpack.testing import (
9 AptSourceFixture,
10 ChdirToTempdirFixture,
11 ConfigFileFixture,
12 DummyFetchedPackage,
13 IsHardwarePack,
14 TestCaseWithFixtures,
15 )
16
17
18class ConfigFileMissingTests(TestCase):
19
20 def test_str(self):
21 exc = ConfigFileMissing("path")
22 self.assertEqual("No such config file: 'path'", str(exc))
23
24
25class HardwarePackBuilderTests(TestCaseWithFixtures):
26
27 def setUp(self):
28 super(HardwarePackBuilderTests, self).setUp()
29 self.useFixture(ChdirToTempdirFixture())
30
31 def test_raises_on_missing_configuration(self):
32 e = self.assertRaises(
33 ConfigFileMissing, HardwarePackBuilder, "nonexistant", "1.0")
34 self.assertEqual("nonexistant", e.filename)
35
36 def test_validates_configuration(self):
37 config = self.useFixture(ConfigFileFixture(''))
38 self.assertRaises(
39 HwpackConfigError, HardwarePackBuilder, config.filename, "1.0")
40
41 def test_builds_one_pack_per_arch(self):
42 available_package = DummyFetchedPackage("foo", "1.1")
43 source = self.useFixture(AptSourceFixture([available_package]))
44 config = self.useFixture(ConfigFileFixture(
45 '[hwpack]\nname=ahwpack\npackages=foo\narchitectures=i386 armel\n'
46 '\n[ubuntu]\nsources-entry=%s\n' % source.sources_entry))
47 builder = HardwarePackBuilder(config.filename, "1.0")
48 builder.build()
49 self.assertTrue(os.path.isfile("hwpack_ahwpack_1.0_i386.tar.gz"))
50 self.assertTrue(os.path.isfile("hwpack_ahwpack_1.0_armel.tar.gz"))
51
52 def test_builds_correct_contents(self):
53 hwpack_name = "ahwpack"
54 hwpack_version = "1.0"
55 architecture = "armel"
56 package_name = "foo"
57 source_id = "ubuntu"
58 available_package = DummyFetchedPackage(
59 package_name, "1.1", architecture=architecture)
60 source = self.useFixture(AptSourceFixture([available_package]))
61 config = self.useFixture(ConfigFileFixture(
62 '[hwpack]\nname=%s\npackages=%s\narchitectures=%s\n'
63 '\n[%s]\nsources-entry=%s\n'
64 % (hwpack_name, package_name, architecture,
65 source_id, source.sources_entry)))
66 builder = HardwarePackBuilder(config.filename, hwpack_version)
67 builder.build()
68 metadata = Metadata(hwpack_name, hwpack_version, architecture)
69 self.assertThat(
70 "hwpack_%s_%s_%s.tar.gz" % (hwpack_name, hwpack_version,
71 architecture),
72 IsHardwarePack(
73 metadata, [available_package],
74 {source_id: source.sources_entry}))
075
=== modified file 'hwpack/tests/test_hardwarepack.py'
--- hwpack/tests/test_hardwarepack.py 2010-09-10 16:11:07 +0000
+++ hwpack/tests/test_hardwarepack.py 2010-09-10 16:11:07 +0000
@@ -5,8 +5,7 @@
55
6from hwpack.hardwarepack import HardwarePack, Metadata6from hwpack.hardwarepack import HardwarePack, Metadata
7from hwpack.packages import get_packages_file7from hwpack.packages import get_packages_file
8from hwpack.tarfile_matchers import TarfileHasFile8from hwpack.testing import DummyFetchedPackage, HardwarePackHasFile
9from hwpack.testing import DummyFetchedPackage
109
1110
12class MetadataTests(TestCase):11class MetadataTests(TestCase):
@@ -90,49 +89,6 @@
90 self.assertEqual("i386", metadata.architecture)89 self.assertEqual("i386", metadata.architecture)
9190
9291
93class HardwarePackHasFile(TarfileHasFile):
94 """A subclass of TarfileHasFile specific to hardware packs.
95
96 We default to a set of attributes expected for files in a hardware
97 pack.
98 """
99
100 def __init__(self, path, **kwargs):
101 """Create a HardwarePackHasFile matcher.
102
103 The kwargs are the keyword arguments taken by TarfileHasFile.
104 If they are not given then defaults will be checked:
105 - The type should be a regular file
106 - If the content is given then the size will be checked
107 to ensure it indicates the length of the content
108 correctly.
109 - the mode is appropriate for the type. If the type is
110 regular file this is 0644, otherwise if it is
111 a directory then it is 0755.
112 - the linkname should be the empty string.
113 - the uid and gid should be 1000
114 - the uname and gname should be "user" and "group"
115 respectively.
116
117 :param path: the path that should be present.
118 :type path: str
119 """
120 kwargs.setdefault("type", tarfile.REGTYPE)
121 if "content" in kwargs:
122 kwargs.setdefault("size", len(kwargs["content"]))
123 if kwargs["type"] == tarfile.DIRTYPE:
124 kwargs.setdefault("mode", 0755)
125 else:
126 kwargs.setdefault("mode", 0644)
127 kwargs.setdefault("linkname", "")
128 kwargs.setdefault("uid", 1000)
129 kwargs.setdefault("gid", 1000)
130 kwargs.setdefault("uname", "user")
131 kwargs.setdefault("gname", "group")
132 # TODO: mtime checking
133 super(HardwarePackHasFile, self).__init__(path, **kwargs)
134
135
136class HardwarePackTests(TestCase):92class HardwarePackTests(TestCase):
13793
138 def setUp(self):94 def setUp(self):

Subscribers

People subscribed via source and target branches