Merge lp:~cjwatson/launchpad/upgrade-keystoneclient-swiftclient into lp:launchpad
- upgrade-keystoneclient-swiftclient
- Merge into devel
Status: | Merged |
---|---|
Merged at revision: | 18553 |
Proposed branch: | lp:~cjwatson/launchpad/upgrade-keystoneclient-swiftclient |
Merge into: | lp:launchpad |
Diff against target: |
435 lines (+123/-43) 9 files modified
constraints.txt (+7/-5) lib/lp/services/librarianserver/librariangc.py (+9/-5) lib/lp/services/librarianserver/storage.py (+3/-3) lib/lp/services/librarianserver/swift.py (+40/-7) lib/lp/services/librarianserver/tests/test_gc.py (+2/-1) lib/lp/services/librarianserver/tests/test_swift.py (+26/-7) lib/lp/testing/swift/fakeswift.py (+28/-12) lib/lp/testing/swift/tests/test_fixture.py (+4/-3) lib/lp_sitecustomize.py (+4/-0) |
To merge this branch: | bzr merge lp:~cjwatson/launchpad/upgrade-keystoneclient-swiftclient |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
William Grant | code | Approve | |
Review via email: mp+335391@code.launchpad.net |
Commit message
Upgrade to python-
Description of the change
These were the latest versions in Icehouse, to go with keystone 2014.1.5 and
swift 1.13.1 currently on the server. Upgrading to these allows us to use a
more recent version of pbr that is compatible with other dependencies we'd
like to upgrade.
We need a few tweaks to make everything work properly:
* quieten overly-noisy logging (needed until python-swiftclient 3.2.0);
* explicitly restrict HashStream to the desired segment size, since
otherwise the client will read past the end of the segment and we can end
up with corrupted hashes;
* advertise a keystone endpoint in the fake Swift fixture;
* cope with slightly different exception types due to the client now using
requests.
Colin Watson (cjwatson) wrote : | # |
OK, I eventually managed to get Icehouse Keystone and Swift deployed locally, and tested this against them. librarian-
Preview Diff
1 | === modified file 'constraints.txt' | |||
2 | --- constraints.txt 2017-12-22 09:59:33 +0000 | |||
3 | +++ constraints.txt 2018-02-13 16:28:38 +0000 | |||
4 | @@ -228,6 +228,7 @@ | |||
5 | 228 | auditor==0.0.3 | 228 | auditor==0.0.3 |
6 | 229 | auditorclient==0.0.4 | 229 | auditorclient==0.0.4 |
7 | 230 | auditorfixture==0.0.7 | 230 | auditorfixture==0.0.7 |
8 | 231 | Babel==2.5.1 | ||
9 | 231 | backports.lzma==0.0.3 | 232 | backports.lzma==0.0.3 |
10 | 232 | BeautifulSoup==3.2.1 | 233 | BeautifulSoup==3.2.1 |
11 | 233 | billiard==3.3.0.20 | 234 | billiard==3.3.0.20 |
12 | @@ -256,7 +257,7 @@ | |||
13 | 256 | httplib2==0.8 | 257 | httplib2==0.8 |
14 | 257 | importlib==1.0.2 | 258 | importlib==1.0.2 |
15 | 258 | ipython==0.13.2 | 259 | ipython==0.13.2 |
17 | 259 | iso8601==0.1.4 | 260 | iso8601==0.1.12 |
18 | 260 | jsautobuild==0.2 | 261 | jsautobuild==0.2 |
19 | 261 | keyring==0.6.2 | 262 | keyring==0.6.2 |
20 | 262 | kombu==3.0.30 | 263 | kombu==3.0.30 |
21 | @@ -283,6 +284,7 @@ | |||
22 | 283 | meliae==0.2.0.final.0 | 284 | meliae==0.2.0.final.0 |
23 | 284 | mock==1.0.1 | 285 | mock==1.0.1 |
24 | 285 | mocker==1.1.1 | 286 | mocker==1.1.1 |
25 | 287 | netaddr==0.7.19 | ||
26 | 286 | oauth==1.0 | 288 | oauth==1.0 |
27 | 287 | oops==0.0.13 | 289 | oops==0.0.13 |
28 | 288 | oops-amqp==0.0.8b1 | 290 | oops-amqp==0.0.8b1 |
29 | @@ -291,9 +293,9 @@ | |||
30 | 291 | oops-twisted==0.0.7 | 293 | oops-twisted==0.0.7 |
31 | 292 | oops-wsgi==0.0.8 | 294 | oops-wsgi==0.0.8 |
32 | 293 | ordereddict==1.1 | 295 | ordereddict==1.1 |
34 | 294 | oslo.config==1.1.1 | 296 | oslo.config==1.3.0 |
35 | 295 | paramiko==1.7.7.2 | 297 | paramiko==1.7.7.2 |
37 | 296 | pbr==0.5.20 | 298 | pbr==0.11.1 |
38 | 297 | pgbouncer==0.0.8 | 299 | pgbouncer==0.0.8 |
39 | 298 | prettytable==0.7.2 | 300 | prettytable==0.7.2 |
40 | 299 | psycopg2==2.6.1 | 301 | psycopg2==2.6.1 |
41 | @@ -306,14 +308,14 @@ | |||
42 | 306 | pystache==0.5.3 | 308 | pystache==0.5.3 |
43 | 307 | python-dateutil==1.5 | 309 | python-dateutil==1.5 |
44 | 308 | python-debian==0.1.23 | 310 | python-debian==0.1.23 |
46 | 309 | python-keystoneclient==0.3.1 | 311 | python-keystoneclient==0.7.1 |
47 | 310 | python-memcached==1.58 | 312 | python-memcached==1.58 |
48 | 311 | python-mimeparse==0.1.4 | 313 | python-mimeparse==0.1.4 |
49 | 312 | # XXX: deryck 2012-08-10 | 314 | # XXX: deryck 2012-08-10 |
50 | 313 | # See lp:~deryck/python-openid/python-openid-fix1034376 which | 315 | # See lp:~deryck/python-openid/python-openid-fix1034376 which |
51 | 314 | # reapplied a patch from wgrant to get codehosting going again. | 316 | # reapplied a patch from wgrant to get codehosting going again. |
52 | 315 | python-openid==2.2.5-fix1034376 | 317 | python-openid==2.2.5-fix1034376 |
54 | 316 | python-swiftclient==1.5.0 | 318 | python-swiftclient==2.0.3 |
55 | 317 | PyYAML==3.10 | 319 | PyYAML==3.10 |
56 | 318 | rabbitfixture==0.3.6 | 320 | rabbitfixture==0.3.6 |
57 | 319 | requests==2.7.0 | 321 | requests==2.7.0 |
58 | 320 | 322 | ||
59 | === modified file 'lib/lp/services/librarianserver/librariangc.py' | |||
60 | --- lib/lp/services/librarianserver/librariangc.py 2017-04-02 01:17:50 +0000 | |||
61 | +++ lib/lp/services/librarianserver/librariangc.py 2018-02-13 16:28:38 +0000 | |||
62 | @@ -1,4 +1,4 @@ | |||
64 | 1 | # Copyright 2009 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2009-2017 Canonical Ltd. This software is licensed under the |
65 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
66 | 3 | 3 | ||
67 | 4 | """Librarian garbage collection routines""" | 4 | """Librarian garbage collection routines""" |
68 | @@ -55,7 +55,8 @@ | |||
69 | 55 | swift_connection = swift.connection_pool.get() | 55 | swift_connection = swift.connection_pool.get() |
70 | 56 | container, name = swift.swift_location(content_id) | 56 | container, name = swift.swift_location(content_id) |
71 | 57 | try: | 57 | try: |
73 | 58 | swift_connection.head_object(container, name) | 58 | swift.quiet_swiftclient( |
74 | 59 | swift_connection.head_object, container, name) | ||
75 | 59 | return True | 60 | return True |
76 | 60 | except swiftclient.ClientException as x: | 61 | except swiftclient.ClientException as x: |
77 | 61 | if x.http_status != 404: | 62 | if x.http_status != 404: |
78 | @@ -79,7 +80,8 @@ | |||
79 | 79 | try: | 80 | try: |
80 | 80 | swift_connection = swift.connection_pool.get() | 81 | swift_connection = swift.connection_pool.get() |
81 | 81 | container, name = swift.swift_location(content_id) | 82 | container, name = swift.swift_location(content_id) |
83 | 82 | chunks = swift_connection.get_object( | 83 | chunks = swift.quiet_swiftclient( |
84 | 84 | swift_connection.get_object, | ||
85 | 83 | container, name, resp_chunk_size=STREAM_CHUNK_SIZE)[1] | 85 | container, name, resp_chunk_size=STREAM_CHUNK_SIZE)[1] |
86 | 84 | return swift.SwiftStream(swift_connection, chunks) | 86 | return swift.SwiftStream(swift_connection, chunks) |
87 | 85 | except swiftclient.ClientException as x: | 87 | except swiftclient.ClientException as x: |
88 | @@ -562,7 +564,8 @@ | |||
89 | 562 | container, name = swift.swift_location(content_id) | 564 | container, name = swift.swift_location(content_id) |
90 | 563 | with swift.connection() as swift_connection: | 565 | with swift.connection() as swift_connection: |
91 | 564 | try: | 566 | try: |
93 | 565 | swift_connection.delete_object(container, name) | 567 | swift.quiet_swiftclient( |
94 | 568 | swift_connection.delete_object, container, name) | ||
95 | 566 | removed.append('Swift') | 569 | removed.append('Swift') |
96 | 567 | except swiftclient.ClientException as x: | 570 | except swiftclient.ClientException as x: |
97 | 568 | if x.http_status != 404: | 571 | if x.http_status != 404: |
98 | @@ -740,7 +743,8 @@ | |||
99 | 740 | container = swift.SWIFT_CONTAINER_PREFIX + str(container_num) | 743 | container = swift.SWIFT_CONTAINER_PREFIX + str(container_num) |
100 | 741 | try: | 744 | try: |
101 | 742 | names = sorted( | 745 | names = sorted( |
103 | 743 | swift_connection.get_container( | 746 | swift.quiet_swiftclient( |
104 | 747 | swift_connection.get_container, | ||
105 | 744 | container, full_listing=True)[1], | 748 | container, full_listing=True)[1], |
106 | 745 | key=lambda x: map(int, x['name'].split('/'))) | 749 | key=lambda x: map(int, x['name'].split('/'))) |
107 | 746 | for name in names: | 750 | for name in names: |
108 | 747 | 751 | ||
109 | === modified file 'lib/lp/services/librarianserver/storage.py' | |||
110 | --- lib/lp/services/librarianserver/storage.py 2016-09-19 13:53:42 +0000 | |||
111 | +++ lib/lp/services/librarianserver/storage.py 2018-02-13 16:28:38 +0000 | |||
112 | @@ -1,4 +1,4 @@ | |||
114 | 1 | # Copyright 2009-2013 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2009-2017 Canonical Ltd. This software is licensed under the |
115 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
116 | 3 | 3 | ||
117 | 4 | __metaclass__ = type | 4 | __metaclass__ = type |
118 | @@ -10,9 +10,9 @@ | |||
119 | 10 | import tempfile | 10 | import tempfile |
120 | 11 | 11 | ||
121 | 12 | from swiftclient import client as swiftclient | 12 | from swiftclient import client as swiftclient |
122 | 13 | from twisted.python import log | ||
123 | 14 | from twisted.internet import defer | 13 | from twisted.internet import defer |
124 | 15 | from twisted.internet.threads import deferToThread | 14 | from twisted.internet.threads import deferToThread |
125 | 15 | from twisted.python import log | ||
126 | 16 | from twisted.web.static import StaticProducer | 16 | from twisted.web.static import StaticProducer |
127 | 17 | 17 | ||
128 | 18 | from lp.registry.model.product import Product | 18 | from lp.registry.model.product import Product |
129 | @@ -124,7 +124,7 @@ | |||
130 | 124 | swift_connection = swift.connection_pool.get() | 124 | swift_connection = swift.connection_pool.get() |
131 | 125 | try: | 125 | try: |
132 | 126 | headers, chunks = yield deferToThread( | 126 | headers, chunks = yield deferToThread( |
134 | 127 | swift_connection.get_object, | 127 | swift.quiet_swiftclient, swift_connection.get_object, |
135 | 128 | container, name, resp_chunk_size=self.CHUNK_SIZE) | 128 | container, name, resp_chunk_size=self.CHUNK_SIZE) |
136 | 129 | swift_stream = TxSwiftStream(swift_connection, chunks) | 129 | swift_stream = TxSwiftStream(swift_connection, chunks) |
137 | 130 | defer.returnValue(swift_stream) | 130 | defer.returnValue(swift_stream) |
138 | 131 | 131 | ||
139 | === modified file 'lib/lp/services/librarianserver/swift.py' | |||
140 | --- lib/lp/services/librarianserver/swift.py 2015-02-16 00:09:14 +0000 | |||
141 | +++ lib/lp/services/librarianserver/swift.py 2018-02-13 16:28:38 +0000 | |||
142 | @@ -1,12 +1,17 @@ | |||
144 | 1 | # Copyright 2013 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2013-2017 Canonical Ltd. This software is licensed under the |
145 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
146 | 3 | 3 | ||
147 | 4 | """Move files from Librarian disk storage into Swift.""" | 4 | """Move files from Librarian disk storage into Swift.""" |
148 | 5 | 5 | ||
149 | 6 | __metaclass__ = type | 6 | __metaclass__ = type |
150 | 7 | __all__ = [ | 7 | __all__ = [ |
153 | 8 | 'to_swift', 'filesystem_path', 'swift_location', | 8 | 'SWIFT_CONTAINER_PREFIX', |
154 | 9 | 'connection', 'connection_pool', 'SWIFT_CONTAINER_PREFIX', | 9 | 'connection', |
155 | 10 | 'connection_pool', | ||
156 | 11 | 'filesystem_path', | ||
157 | 12 | 'quiet_swiftclient', | ||
158 | 13 | 'swift_location', | ||
159 | 14 | 'to_swift', | ||
160 | 10 | ] | 15 | ] |
161 | 11 | 16 | ||
162 | 12 | from contextlib import contextmanager | 17 | from contextlib import contextmanager |
163 | @@ -29,6 +34,24 @@ | |||
164 | 29 | ONE_DAY = 24 * 60 * 60 | 34 | ONE_DAY = 24 * 60 * 60 |
165 | 30 | 35 | ||
166 | 31 | 36 | ||
167 | 37 | def quiet_swiftclient(func, *args, **kwargs): | ||
168 | 38 | # XXX cjwatson 2018-01-02: swiftclient has some very rude logging | ||
169 | 39 | # practices: the low-level API calls `logger.exception` when a request | ||
170 | 40 | # fails, without considering whether the caller might handle it and | ||
171 | 41 | # recover. This was introduced in 1.6.0 and removed in 3.2.0; until | ||
172 | 42 | # we're on a new enough version not to need to worry about this, we shut | ||
173 | 43 | # up the noisy logging around calls whose failure we can handle. | ||
174 | 44 | # Messier still, logging.getLogger('swiftclient') doesn't necessarily | ||
175 | 45 | # refer to the Logger instance actually being used by swiftclient, so we | ||
176 | 46 | # have to use swiftclient.logger directly. | ||
177 | 47 | old_disabled = swiftclient.logger.disabled | ||
178 | 48 | try: | ||
179 | 49 | swiftclient.logger.disabled = True | ||
180 | 50 | return func(*args, **kwargs) | ||
181 | 51 | finally: | ||
182 | 52 | swiftclient.logger.disabled = old_disabled | ||
183 | 53 | |||
184 | 54 | |||
185 | 32 | def to_swift(log, start_lfc_id=None, end_lfc_id=None, remove_func=False): | 55 | def to_swift(log, start_lfc_id=None, end_lfc_id=None, remove_func=False): |
186 | 33 | '''Copy a range of Librarian files from disk into Swift. | 56 | '''Copy a range of Librarian files from disk into Swift. |
187 | 34 | 57 | ||
188 | @@ -118,7 +141,7 @@ | |||
189 | 118 | container, obj_name = swift_location(lfc) | 141 | container, obj_name = swift_location(lfc) |
190 | 119 | 142 | ||
191 | 120 | try: | 143 | try: |
193 | 121 | swift_connection.head_container(container) | 144 | quiet_swiftclient(swift_connection.head_container, container) |
194 | 122 | log.debug2('{0} container already exists'.format(container)) | 145 | log.debug2('{0} container already exists'.format(container)) |
195 | 123 | except swiftclient.ClientException as x: | 146 | except swiftclient.ClientException as x: |
196 | 124 | if x.http_status != 404: | 147 | if x.http_status != 404: |
197 | @@ -127,7 +150,8 @@ | |||
198 | 127 | swift_connection.put_container(container) | 150 | swift_connection.put_container(container) |
199 | 128 | 151 | ||
200 | 129 | try: | 152 | try: |
202 | 130 | headers = swift_connection.head_object(container, obj_name) | 153 | headers = quiet_swiftclient( |
203 | 154 | swift_connection.head_object, container, obj_name) | ||
204 | 131 | log.debug( | 155 | log.debug( |
205 | 132 | "{0} already exists in Swift({1}, {2})".format( | 156 | "{0} already exists in Swift({1}, {2})".format( |
206 | 133 | lfc, container, obj_name)) | 157 | lfc, container, obj_name)) |
207 | @@ -188,7 +212,7 @@ | |||
208 | 188 | assert segment <= 9999, 'Insane number of segments' | 212 | assert segment <= 9999, 'Insane number of segments' |
209 | 189 | seg_name = '%s/%04d' % (obj_name, segment) | 213 | seg_name = '%s/%04d' % (obj_name, segment) |
210 | 190 | seg_size = min(fs_size - fs_file.tell(), MAX_SWIFT_OBJECT_SIZE) | 214 | seg_size = min(fs_size - fs_file.tell(), MAX_SWIFT_OBJECT_SIZE) |
212 | 191 | md5_stream = HashStream(fs_file) | 215 | md5_stream = HashStream(fs_file, length=seg_size) |
213 | 192 | swift_md5_hash = swift_connection.put_object( | 216 | swift_md5_hash = swift_connection.put_object( |
214 | 193 | container, seg_name, md5_stream, seg_size) | 217 | container, seg_name, md5_stream, seg_size) |
215 | 194 | segment_md5_hash = md5_stream.hash.hexdigest() | 218 | segment_md5_hash = md5_stream.hash.hexdigest() |
216 | @@ -304,13 +328,20 @@ | |||
217 | 304 | 328 | ||
218 | 305 | class HashStream: | 329 | class HashStream: |
219 | 306 | """Read a file while calculating a checksum as we go.""" | 330 | """Read a file while calculating a checksum as we go.""" |
221 | 307 | def __init__(self, stream, hash_factory=hashlib.md5): | 331 | def __init__(self, stream, length=None, hash_factory=hashlib.md5): |
222 | 308 | self._stream = stream | 332 | self._stream = stream |
223 | 333 | self._length = self._remaining = length | ||
224 | 309 | self.hash_factory = hash_factory | 334 | self.hash_factory = hash_factory |
225 | 310 | self.hash = hash_factory() | 335 | self.hash = hash_factory() |
226 | 311 | 336 | ||
227 | 312 | def read(self, size=-1): | 337 | def read(self, size=-1): |
228 | 338 | if self._remaining is not None: | ||
229 | 339 | if self._remaining <= 0: | ||
230 | 340 | return '' | ||
231 | 341 | size = min(size, self._remaining) | ||
232 | 313 | chunk = self._stream.read(size) | 342 | chunk = self._stream.read(size) |
233 | 343 | if self._remaining is not None: | ||
234 | 344 | self._remaining -= len(chunk) | ||
235 | 314 | self.hash.update(chunk) | 345 | self.hash.update(chunk) |
236 | 315 | return chunk | 346 | return chunk |
237 | 316 | 347 | ||
238 | @@ -320,6 +351,8 @@ | |||
239 | 320 | def seek(self, offset): | 351 | def seek(self, offset): |
240 | 321 | """Seek to offset, and reset the hash.""" | 352 | """Seek to offset, and reset the hash.""" |
241 | 322 | self.hash = self.hash_factory() | 353 | self.hash = self.hash_factory() |
242 | 354 | if self._remaining is not None: | ||
243 | 355 | self._remaining = self._length - offset | ||
244 | 323 | return self._stream.seek(offset) | 356 | return self._stream.seek(offset) |
245 | 324 | 357 | ||
246 | 325 | 358 | ||
247 | 326 | 359 | ||
248 | === modified file 'lib/lp/services/librarianserver/tests/test_gc.py' | |||
249 | --- lib/lp/services/librarianserver/tests/test_gc.py 2018-01-02 16:10:26 +0000 | |||
250 | +++ lib/lp/services/librarianserver/tests/test_gc.py 2018-02-13 16:28:38 +0000 | |||
251 | @@ -728,7 +728,8 @@ | |||
252 | 728 | name += suffix | 728 | name += suffix |
253 | 729 | with swift.connection() as swift_connection: | 729 | with swift.connection() as swift_connection: |
254 | 730 | try: | 730 | try: |
256 | 731 | swift_connection.head_object(container, name) | 731 | swift.quiet_swiftclient( |
257 | 732 | swift_connection.head_object, container, name) | ||
258 | 732 | return True | 733 | return True |
259 | 733 | except swiftclient.ClientException as x: | 734 | except swiftclient.ClientException as x: |
260 | 734 | if x.http_status == 404: | 735 | if x.http_status == 404: |
261 | 735 | 736 | ||
262 | === modified file 'lib/lp/services/librarianserver/tests/test_swift.py' | |||
263 | --- lib/lp/services/librarianserver/tests/test_swift.py 2018-01-02 16:10:26 +0000 | |||
264 | +++ lib/lp/services/librarianserver/tests/test_swift.py 2018-02-13 16:28:38 +0000 | |||
265 | @@ -1,4 +1,4 @@ | |||
267 | 1 | # Copyright 2013 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2013-2017 Canonical Ltd. This software is licensed under the |
268 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
269 | 3 | 3 | ||
270 | 4 | """Librarian disk to Swift storage tests.""" | 4 | """Librarian disk to Swift storage tests.""" |
271 | @@ -19,14 +19,17 @@ | |||
272 | 19 | from lp.services.features.testing import FeatureFixture | 19 | from lp.services.features.testing import FeatureFixture |
273 | 20 | from lp.services.librarian.client import LibrarianClient | 20 | from lp.services.librarian.client import LibrarianClient |
274 | 21 | from lp.services.librarian.model import LibraryFileAlias | 21 | from lp.services.librarian.model import LibraryFileAlias |
275 | 22 | from lp.services.librarianserver import swift | ||
276 | 22 | from lp.services.librarianserver.storage import LibrarianStorage | 23 | from lp.services.librarianserver.storage import LibrarianStorage |
277 | 23 | from lp.services.log.logger import BufferLogger | 24 | from lp.services.log.logger import BufferLogger |
278 | 24 | from lp.testing import TestCase | 25 | from lp.testing import TestCase |
280 | 25 | from lp.testing.layers import BaseLayer, LaunchpadZopelessLayer, LibrarianLayer | 26 | from lp.testing.layers import ( |
281 | 27 | BaseLayer, | ||
282 | 28 | LaunchpadZopelessLayer, | ||
283 | 29 | LibrarianLayer, | ||
284 | 30 | ) | ||
285 | 26 | from lp.testing.swift.fixture import SwiftFixture | 31 | from lp.testing.swift.fixture import SwiftFixture |
286 | 27 | 32 | ||
287 | 28 | from lp.services.librarianserver import swift | ||
288 | 29 | |||
289 | 30 | 33 | ||
290 | 31 | class TestFeedSwift(TestCase): | 34 | class TestFeedSwift(TestCase): |
291 | 32 | layer = LaunchpadZopelessLayer | 35 | layer = LaunchpadZopelessLayer |
292 | @@ -272,8 +275,8 @@ | |||
293 | 272 | _, obj2 = swift_client.get_object(container, '{0}/0001'.format(name)) | 275 | _, obj2 = swift_client.get_object(container, '{0}/0001'.format(name)) |
294 | 273 | _, obj3 = swift_client.get_object(container, '{0}/0002'.format(name)) | 276 | _, obj3 = swift_client.get_object(container, '{0}/0002'.format(name)) |
295 | 274 | self.assertRaises( | 277 | self.assertRaises( |
298 | 275 | swiftclient.ClientException, swift_client.get_object, | 278 | swiftclient.ClientException, swift.quiet_swiftclient, |
299 | 276 | container, '{0}/0003'.format(name)) | 279 | swift_client.get_object, container, '{0}/0003'.format(name)) |
300 | 277 | 280 | ||
301 | 278 | # Our object round tripped | 281 | # Our object round tripped |
302 | 279 | self.assertEqual(obj1 + obj2 + obj3, expected_content) | 282 | self.assertEqual(obj1 + obj2 + obj3, expected_content) |
303 | @@ -293,7 +296,8 @@ | |||
304 | 293 | 296 | ||
305 | 294 | def test_partial_read(self): | 297 | def test_partial_read(self): |
306 | 295 | empty_sha1 = 'da39a3ee5e6b4b0d3255bfef95601890afd80709' | 298 | empty_sha1 = 'da39a3ee5e6b4b0d3255bfef95601890afd80709' |
308 | 296 | s = swift.HashStream(StringIO('make me another coffee'), hashlib.sha1) | 299 | s = swift.HashStream( |
309 | 300 | StringIO('make me another coffee'), hash_factory=hashlib.sha1) | ||
310 | 297 | self.assertEqual(s.hash.hexdigest(), empty_sha1) | 301 | self.assertEqual(s.hash.hexdigest(), empty_sha1) |
311 | 298 | chunk = s.read(4) | 302 | chunk = s.read(4) |
312 | 299 | self.assertEqual(chunk, 'make') | 303 | self.assertEqual(chunk, 'make') |
313 | @@ -304,6 +308,21 @@ | |||
314 | 304 | self.assertEqual(s.hash.hexdigest(), | 308 | self.assertEqual(s.hash.hexdigest(), |
315 | 305 | '8c826e573016ce05f3968044f82507b46fd2aa93') | 309 | '8c826e573016ce05f3968044f82507b46fd2aa93') |
316 | 306 | 310 | ||
317 | 311 | def test_limited_length(self): | ||
318 | 312 | base_stream = StringIO('make me a coffee') | ||
319 | 313 | s = swift.HashStream(base_stream, length=8) | ||
320 | 314 | chunk = s.read(4) | ||
321 | 315 | self.assertEqual(chunk, 'make') | ||
322 | 316 | self.assertEqual(s.hash.hexdigest(), | ||
323 | 317 | '099dafc678df7d266c25f95ccf6cde22') | ||
324 | 318 | chunk = s.read(8) | ||
325 | 319 | self.assertEqual(chunk, ' me ') | ||
326 | 320 | self.assertEqual(s.hash.hexdigest(), | ||
327 | 321 | '10a0334e435b75f35b1923842bd87f81') | ||
328 | 322 | self.assertEqual(s.read(), '') | ||
329 | 323 | self.assertEqual(s.tell(), 8) | ||
330 | 324 | self.assertEqual(base_stream.tell(), 8) | ||
331 | 325 | |||
332 | 307 | def test_tell(self): | 326 | def test_tell(self): |
333 | 308 | s = swift.HashStream(StringIO('hurry up with that coffee')) | 327 | s = swift.HashStream(StringIO('hurry up with that coffee')) |
334 | 309 | self.assertEqual(s.tell(), 0) | 328 | self.assertEqual(s.tell(), 0) |
335 | 310 | 329 | ||
336 | === modified file 'lib/lp/testing/swift/fakeswift.py' | |||
337 | --- lib/lp/testing/swift/fakeswift.py 2017-12-07 12:05:13 +0000 | |||
338 | +++ lib/lp/testing/swift/fakeswift.py 2018-02-13 16:28:38 +0000 | |||
339 | @@ -557,17 +557,33 @@ | |||
340 | 557 | """Compute service catalog for the given request and tenant.""" | 557 | """Compute service catalog for the given request and tenant.""" |
341 | 558 | port = request.transport.socket.getsockname()[1] | 558 | port = request.transport.socket.getsockname()[1] |
342 | 559 | tenant_id = self.tenants[tenant]["id"] | 559 | tenant_id = self.tenants[tenant]["id"] |
344 | 560 | base_url = "http://%s:%d/swift/v1" % (self.hostname, port) | 560 | base_url = "http://%s:%d" % (self.hostname, port) |
345 | 561 | keystone_base_url = "%s/keystone/v2.0" % base_url | ||
346 | 562 | swift_base_url = "%s/swift/v1" % base_url | ||
347 | 561 | catalog = [ | 563 | catalog = [ |
359 | 562 | {"endpoints": [ | 564 | { |
360 | 563 | {"adminURL": base_url, | 565 | "endpoints": [{ |
361 | 564 | "id": uuid.uuid4().hex, | 566 | "adminURL": keystone_base_url, |
362 | 565 | "internalURL": base_url + "/AUTH_" + tenant_id, | 567 | "id": uuid.uuid4().hex, |
363 | 566 | "publicURL": base_url + "/AUTH_" + tenant_id, | 568 | "internalURL": keystone_base_url, |
364 | 567 | "region": DEFAULT_REGION} | 569 | "publicURL": keystone_base_url, |
365 | 568 | ], | 570 | "region": DEFAULT_REGION, |
366 | 569 | "endpoints_links": [], | 571 | }], |
367 | 570 | "name": "swift", | 572 | "endpoints_links": [], |
368 | 571 | "type": "object-store" | 573 | "name": "keystone", |
369 | 572 | }] | 574 | "type": "identity", |
370 | 575 | }, | ||
371 | 576 | { | ||
372 | 577 | "endpoints": [{ | ||
373 | 578 | "adminURL": swift_base_url, | ||
374 | 579 | "id": uuid.uuid4().hex, | ||
375 | 580 | "internalURL": swift_base_url + "/AUTH_" + tenant_id, | ||
376 | 581 | "publicURL": swift_base_url + "/AUTH_" + tenant_id, | ||
377 | 582 | "region": DEFAULT_REGION, | ||
378 | 583 | }], | ||
379 | 584 | "endpoints_links": [], | ||
380 | 585 | "name": "swift", | ||
381 | 586 | "type": "object-store", | ||
382 | 587 | }, | ||
383 | 588 | ] | ||
384 | 573 | return catalog | 589 | return catalog |
385 | 574 | 590 | ||
386 | === modified file 'lib/lp/testing/swift/tests/test_fixture.py' | |||
387 | --- lib/lp/testing/swift/tests/test_fixture.py 2018-01-02 16:10:26 +0000 | |||
388 | +++ lib/lp/testing/swift/tests/test_fixture.py 2018-02-13 16:28:38 +0000 | |||
389 | @@ -8,9 +8,10 @@ | |||
390 | 8 | 8 | ||
391 | 9 | from datetime import datetime | 9 | from datetime import datetime |
392 | 10 | from hashlib import md5 | 10 | from hashlib import md5 |
393 | 11 | import httplib | ||
394 | 12 | 11 | ||
395 | 12 | from requests.exceptions import ConnectionError | ||
396 | 13 | from swiftclient import client as swiftclient | 13 | from swiftclient import client as swiftclient |
397 | 14 | from swiftclient.exceptions import ClientException | ||
398 | 14 | from testtools.matchers import ( | 15 | from testtools.matchers import ( |
399 | 15 | GreaterThan, | 16 | GreaterThan, |
400 | 16 | LessThan, | 17 | LessThan, |
401 | @@ -200,14 +201,14 @@ | |||
402 | 200 | # authenticated. | 201 | # authenticated. |
403 | 201 | self.swift_fixture.shutdown() | 202 | self.swift_fixture.shutdown() |
404 | 202 | self.assertRaises( | 203 | self.assertRaises( |
406 | 203 | httplib.HTTPException, | 204 | ConnectionError, |
407 | 204 | client.get_object, "size", str(size)) | 205 | client.get_object, "size", str(size)) |
408 | 205 | 206 | ||
409 | 206 | # And even if we bring it back up, existing connections | 207 | # And even if we bring it back up, existing connections |
410 | 207 | # continue to fail | 208 | # continue to fail |
411 | 208 | self.swift_fixture.startup() | 209 | self.swift_fixture.startup() |
412 | 209 | self.assertRaises( | 210 | self.assertRaises( |
414 | 210 | httplib.HTTPException, | 211 | ClientException, |
415 | 211 | client.get_object, "size", str(size)) | 212 | client.get_object, "size", str(size)) |
416 | 212 | 213 | ||
417 | 213 | # But fresh connections are fine. | 214 | # But fresh connections are fine. |
418 | 214 | 215 | ||
419 | === modified file 'lib/lp_sitecustomize.py' | |||
420 | --- lib/lp_sitecustomize.py 2017-12-19 12:32:57 +0000 | |||
421 | +++ lib/lp_sitecustomize.py 2018-02-13 16:28:38 +0000 | |||
422 | @@ -91,9 +91,13 @@ | |||
423 | 91 | Logger, which is unfortunate as we disable them in the handler. Not | 91 | Logger, which is unfortunate as we disable them in the handler. Not |
424 | 92 | only does swiftclient then emit lots of noise, but it also turns | 92 | only does swiftclient then emit lots of noise, but it also turns |
425 | 93 | keystoneclient debugging on. | 93 | keystoneclient debugging on. |
426 | 94 | |||
427 | 95 | keystoneclient logs credentials at DEBUG. | ||
428 | 94 | """ | 96 | """ |
429 | 95 | swiftclient_logger = logging.getLogger('swiftclient') | 97 | swiftclient_logger = logging.getLogger('swiftclient') |
430 | 96 | swiftclient_logger.setLevel(logging.INFO) | 98 | swiftclient_logger.setLevel(logging.INFO) |
431 | 99 | keystoneclient_logger = logging.getLogger('keystoneclient') | ||
432 | 100 | keystoneclient_logger.setLevel(logging.INFO) | ||
433 | 97 | 101 | ||
434 | 98 | 102 | ||
435 | 99 | def silence_zcml_logger(): | 103 | def silence_zcml_logger(): |
We'll probably need to tweak staging configs a bit to effectively test this. Have you verified in any depth against a non-prod Swift?