Merge lp:~mvo/ubuntu-system-image/lp1271684 into lp:~registry/ubuntu-system-image/client

Proposed by Michael Vogt
Status: Merged
Approved by: Barry Warsaw
Approved revision: 259
Merge reported by: Barry Warsaw
Merged at revision: not available
Proposed branch: lp:~mvo/ubuntu-system-image/lp1271684
Merge into: lp:~registry/ubuntu-system-image/client
Diff against target: 157 lines (+58/-10)
5 files modified
README (+15/-0)
systemimage/gpg.py (+8/-5)
systemimage/helpers.py (+15/-0)
systemimage/state.py (+3/-3)
systemimage/tests/test_helpers.py (+17/-2)
To merge this branch: bzr merge lp:~mvo/ubuntu-system-image/lp1271684
Reviewer Review Type Date Requested Status
Barry Warsaw (community) Approve
Review via email: mp+221677@code.launchpad.net

Description of the change

This branch makes system-image more memory efficient when calculating the file signatures. This fixes lp:1271684.

Its a bit of a drive-by branch, I stumbled over the bug and it looked easy enough to get familiar with the code.

Note that I added a README to help new developers getting into the code, I had trouble running the full testsuite, I'm happy to replace the "???" in the README with the proper command. I tried "python3 -m nose2 -v" but that fails early in find_dbus_process on my utopic system.

To post a comment you must log in.
Revision history for this message
Barry Warsaw (barry) wrote :

Hi Michael - thanks for this branch! Somehow I missed this bug. The branch looks good, I'll merge this into trunk for s-i 2.3. Thanks!

FWIW, you can just run `tox` at the command line to run the test suite. That has the side-effect of building the environment to run the tests. `python3 -m nose2 -v` *can* work, but it may not have everything set up. I'll update the README with details.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'README'
--- README 1970-01-01 00:00:00 +0000
+++ README 2014-06-02 08:52:41 +0000
@@ -0,0 +1,15 @@
1System Image Updater
2====================
3
4Tools for system image based upgrades. For more details, see:
5https://wiki.ubuntu.com/ImageBasedUpgrades
6
7
8Testing
9-------
10
11To test locally run:
12
13 python3 -m nose2 -v
14
15
016
=== modified file 'systemimage/gpg.py'
--- systemimage/gpg.py 2014-02-13 21:44:16 +0000
+++ systemimage/gpg.py 2014-06-02 08:52:41 +0000
@@ -28,7 +28,10 @@
2828
29from contextlib import ExitStack29from contextlib import ExitStack
30from systemimage.config import config30from systemimage.config import config
31from systemimage.helpers import temporary_directory31from systemimage.helpers import (
32 calculate_signature,
33 temporary_directory
34)
3235
3336
34class SignatureError(Exception):37class SignatureError(Exception):
@@ -54,19 +57,19 @@
54 # want to be able to quickly and easily compare the file on disk57 # want to be able to quickly and easily compare the file on disk
55 # against the file on the server.58 # against the file on the server.
56 with open(self.signature_path, 'rb') as fp:59 with open(self.signature_path, 'rb') as fp:
57 self.signature_checksum = hashlib.md5(fp.read()).hexdigest()60 self.signature_checksum = calculate_signature(fp, hashlib.md5)
58 with open(self.data_path, 'rb') as fp:61 with open(self.data_path, 'rb') as fp:
59 self.data_checksum = hashlib.md5(fp.read()).hexdigest()62 self.data_checksum = calculate_signature(fp, hashlib.md5)
60 self.keyring_checksums = []63 self.keyring_checksums = []
61 for path in self.keyrings:64 for path in self.keyrings:
62 with open(path, 'rb') as fp:65 with open(path, 'rb') as fp:
63 checksum = hashlib.md5(fp.read()).hexdigest()66 checksum = calculate_signature(fp, hashlib.md5)
64 self.keyring_checksums.append(checksum)67 self.keyring_checksums.append(checksum)
65 if self.blacklist is None:68 if self.blacklist is None:
66 self.blacklist_checksum = None69 self.blacklist_checksum = None
67 else:70 else:
68 with open(self.blacklist, 'rb') as fp:71 with open(self.blacklist, 'rb') as fp:
69 self.blacklist_checksum = hashlib.md5(fp.read()).hexdigest()72 self.blacklist_checksum = calculate_signature(fp, hashlib.md5)
7073
71 def __str__(self):74 def __str__(self):
72 if self.blacklist is None:75 if self.blacklist is None:
7376
=== modified file 'systemimage/helpers.py'
--- systemimage/helpers.py 2014-02-13 18:16:17 +0000
+++ systemimage/helpers.py 2014-06-02 08:52:41 +0000
@@ -49,6 +49,21 @@
49LAST_UPDATE_FILE = '/userdata/.last_update'49LAST_UPDATE_FILE = '/userdata/.last_update'
50UNIQUE_MACHINE_ID_FILE = '/var/lib/dbus/machine-id'50UNIQUE_MACHINE_ID_FILE = '/var/lib/dbus/machine-id'
51DEFAULT_DIRMODE = 0o0270051DEFAULT_DIRMODE = 0o02700
52SIGNATURE_CHUNK_SIZE = 1*1024*1024
53
54
55def calculate_signature(fp, hash_class):
56 """
57 Calculate the hexdigest hash signature for the open file 'fp' using
58 the given hash_class in a memory efficient way
59 """
60 hash = hash_class()
61 while True:
62 chunk = fp.read(SIGNATURE_CHUNK_SIZE)
63 if not chunk:
64 break
65 hash.update(chunk)
66 return hash.hexdigest()
5267
5368
54def safe_remove(path):69def safe_remove(path):
5570
=== modified file 'systemimage/state.py'
--- systemimage/state.py 2014-05-28 20:18:42 +0000
+++ systemimage/state.py 2014-06-02 08:52:41 +0000
@@ -39,7 +39,7 @@
39from systemimage.download import DBusDownloadManager, Record39from systemimage.download import DBusDownloadManager, Record
40from systemimage.gpg import Context, SignatureError40from systemimage.gpg import Context, SignatureError
41from systemimage.helpers import (41from systemimage.helpers import (
42 atomic, makedirs, safe_remove, temporary_directory)42 atomic, calculate_signature, makedirs, safe_remove, temporary_directory)
43from systemimage.index import Index43from systemimage.index import Index
44from systemimage.keyring import KeyringError, get_keyring44from systemimage.keyring import KeyringError, get_keyring
45from urllib.parse import urljoin45from urllib.parse import urljoin
@@ -69,7 +69,7 @@
69 if checksum is None:69 if checksum is None:
70 return True70 return True
71 with open(txt, 'rb') as fp:71 with open(txt, 'rb') as fp:
72 got = hashlib.sha256(fp.read()).hexdigest()72 got = calculate_signature(fp, hashlib.sha256)
73 return got == checksum73 return got == checksum
7474
7575
@@ -508,7 +508,7 @@
508 # Verify the checksums.508 # Verify the checksums.
509 for dst, checksum in checksums:509 for dst, checksum in checksums:
510 with open(dst, 'rb') as fp:510 with open(dst, 'rb') as fp:
511 got = hashlib.sha256(fp.read()).hexdigest()511 got = calculate_signature(fp, hashlib.sha256)
512 if got != checksum:512 if got != checksum:
513 raise ChecksumError(dst, got, checksum)513 raise ChecksumError(dst, got, checksum)
514 # Everything is fine so nothing needs to be cleared.514 # Everything is fine so nothing needs to be cleared.
515515
=== modified file 'systemimage/tests/test_helpers.py'
--- systemimage/tests/test_helpers.py 2014-02-13 18:16:17 +0000
+++ systemimage/tests/test_helpers.py 2014-06-02 08:52:41 +0000
@@ -24,15 +24,18 @@
2424
2525
26import os26import os
27import hashlib
27import logging28import logging
29import tempfile
28import unittest30import unittest
2931
30from contextlib import ExitStack32from contextlib import ExitStack
31from datetime import datetime, timedelta33from datetime import datetime, timedelta
32from systemimage.config import Configuration, config34from systemimage.config import Configuration, config
33from systemimage.helpers import (35from systemimage.helpers import (
34 Bag, as_loglevel, as_object, as_timedelta, last_update_date,36 Bag, as_loglevel, as_object, as_timedelta, calculate_signature,
35 phased_percentage, temporary_directory, version_detail)37 last_update_date, phased_percentage, SIGNATURE_CHUNK_SIZE,
38 temporary_directory, version_detail)
36from systemimage.testing.helpers import configuration, data_path, touch_build39from systemimage.testing.helpers import configuration, data_path, touch_build
37from unittest.mock import patch40from unittest.mock import patch
3841
@@ -255,3 +258,15 @@
255 self.assertEqual(phased_percentage(reset=True), 81)258 self.assertEqual(phased_percentage(reset=True), 81)
256 # The next one will have a different value.259 # The next one will have a different value.
257 self.assertEqual(phased_percentage(), 17)260 self.assertEqual(phased_percentage(), 17)
261
262
263class TestSignature(unittest.TestCase):
264 def test_calculcate_signature(self):
265 with tempfile.TemporaryFile() as fp:
266 # ensure the file is bigger than chunk size
267 fp.write(b"\0" * (SIGNATURE_CHUNK_SIZE+1))
268 fp.seek(0)
269 hash1 = calculate_signature(fp, hashlib.sha256)
270 fp.seek(0)
271 hash2 = hashlib.sha256(fp.read()).hexdigest()
272 self.assertEqual(hash1, hash2)

Subscribers

People subscribed via source and target branches