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
1=== added file 'README'
2--- README 1970-01-01 00:00:00 +0000
3+++ README 2014-06-02 08:52:41 +0000
4@@ -0,0 +1,15 @@
5+System Image Updater
6+====================
7+
8+Tools for system image based upgrades. For more details, see:
9+https://wiki.ubuntu.com/ImageBasedUpgrades
10+
11+
12+Testing
13+-------
14+
15+To test locally run:
16+
17+ python3 -m nose2 -v
18+
19+
20
21=== modified file 'systemimage/gpg.py'
22--- systemimage/gpg.py 2014-02-13 21:44:16 +0000
23+++ systemimage/gpg.py 2014-06-02 08:52:41 +0000
24@@ -28,7 +28,10 @@
25
26 from contextlib import ExitStack
27 from systemimage.config import config
28-from systemimage.helpers import temporary_directory
29+from systemimage.helpers import (
30+ calculate_signature,
31+ temporary_directory
32+)
33
34
35 class SignatureError(Exception):
36@@ -54,19 +57,19 @@
37 # want to be able to quickly and easily compare the file on disk
38 # against the file on the server.
39 with open(self.signature_path, 'rb') as fp:
40- self.signature_checksum = hashlib.md5(fp.read()).hexdigest()
41+ self.signature_checksum = calculate_signature(fp, hashlib.md5)
42 with open(self.data_path, 'rb') as fp:
43- self.data_checksum = hashlib.md5(fp.read()).hexdigest()
44+ self.data_checksum = calculate_signature(fp, hashlib.md5)
45 self.keyring_checksums = []
46 for path in self.keyrings:
47 with open(path, 'rb') as fp:
48- checksum = hashlib.md5(fp.read()).hexdigest()
49+ checksum = calculate_signature(fp, hashlib.md5)
50 self.keyring_checksums.append(checksum)
51 if self.blacklist is None:
52 self.blacklist_checksum = None
53 else:
54 with open(self.blacklist, 'rb') as fp:
55- self.blacklist_checksum = hashlib.md5(fp.read()).hexdigest()
56+ self.blacklist_checksum = calculate_signature(fp, hashlib.md5)
57
58 def __str__(self):
59 if self.blacklist is None:
60
61=== modified file 'systemimage/helpers.py'
62--- systemimage/helpers.py 2014-02-13 18:16:17 +0000
63+++ systemimage/helpers.py 2014-06-02 08:52:41 +0000
64@@ -49,6 +49,21 @@
65 LAST_UPDATE_FILE = '/userdata/.last_update'
66 UNIQUE_MACHINE_ID_FILE = '/var/lib/dbus/machine-id'
67 DEFAULT_DIRMODE = 0o02700
68+SIGNATURE_CHUNK_SIZE = 1*1024*1024
69+
70+
71+def calculate_signature(fp, hash_class):
72+ """
73+ Calculate the hexdigest hash signature for the open file 'fp' using
74+ the given hash_class in a memory efficient way
75+ """
76+ hash = hash_class()
77+ while True:
78+ chunk = fp.read(SIGNATURE_CHUNK_SIZE)
79+ if not chunk:
80+ break
81+ hash.update(chunk)
82+ return hash.hexdigest()
83
84
85 def safe_remove(path):
86
87=== modified file 'systemimage/state.py'
88--- systemimage/state.py 2014-05-28 20:18:42 +0000
89+++ systemimage/state.py 2014-06-02 08:52:41 +0000
90@@ -39,7 +39,7 @@
91 from systemimage.download import DBusDownloadManager, Record
92 from systemimage.gpg import Context, SignatureError
93 from systemimage.helpers import (
94- atomic, makedirs, safe_remove, temporary_directory)
95+ atomic, calculate_signature, makedirs, safe_remove, temporary_directory)
96 from systemimage.index import Index
97 from systemimage.keyring import KeyringError, get_keyring
98 from urllib.parse import urljoin
99@@ -69,7 +69,7 @@
100 if checksum is None:
101 return True
102 with open(txt, 'rb') as fp:
103- got = hashlib.sha256(fp.read()).hexdigest()
104+ got = calculate_signature(fp, hashlib.sha256)
105 return got == checksum
106
107
108@@ -508,7 +508,7 @@
109 # Verify the checksums.
110 for dst, checksum in checksums:
111 with open(dst, 'rb') as fp:
112- got = hashlib.sha256(fp.read()).hexdigest()
113+ got = calculate_signature(fp, hashlib.sha256)
114 if got != checksum:
115 raise ChecksumError(dst, got, checksum)
116 # Everything is fine so nothing needs to be cleared.
117
118=== modified file 'systemimage/tests/test_helpers.py'
119--- systemimage/tests/test_helpers.py 2014-02-13 18:16:17 +0000
120+++ systemimage/tests/test_helpers.py 2014-06-02 08:52:41 +0000
121@@ -24,15 +24,18 @@
122
123
124 import os
125+import hashlib
126 import logging
127+import tempfile
128 import unittest
129
130 from contextlib import ExitStack
131 from datetime import datetime, timedelta
132 from systemimage.config import Configuration, config
133 from systemimage.helpers import (
134- Bag, as_loglevel, as_object, as_timedelta, last_update_date,
135- phased_percentage, temporary_directory, version_detail)
136+ Bag, as_loglevel, as_object, as_timedelta, calculate_signature,
137+ last_update_date, phased_percentage, SIGNATURE_CHUNK_SIZE,
138+ temporary_directory, version_detail)
139 from systemimage.testing.helpers import configuration, data_path, touch_build
140 from unittest.mock import patch
141
142@@ -255,3 +258,15 @@
143 self.assertEqual(phased_percentage(reset=True), 81)
144 # The next one will have a different value.
145 self.assertEqual(phased_percentage(), 17)
146+
147+
148+class TestSignature(unittest.TestCase):
149+ def test_calculcate_signature(self):
150+ with tempfile.TemporaryFile() as fp:
151+ # ensure the file is bigger than chunk size
152+ fp.write(b"\0" * (SIGNATURE_CHUNK_SIZE+1))
153+ fp.seek(0)
154+ hash1 = calculate_signature(fp, hashlib.sha256)
155+ fp.seek(0)
156+ hash2 = hashlib.sha256(fp.read()).hexdigest()
157+ self.assertEqual(hash1, hash2)

Subscribers

People subscribed via source and target branches