← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~stub/launchpad/trivial into lp:launchpad

 

Stuart Bishop has proposed merging lp:~stub/launchpad/trivial into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #951401 in Launchpad itself: "parse-ppa-apache-logs failing (missing files)"
  https://bugs.launchpad.net/launchpad/+bug/951401
  Bug #1263002 in Launchpad itself: "Twisted feature flag support fails typecasting when updating"
  https://bugs.launchpad.net/launchpad/+bug/1263002

For more details, see:
https://code.launchpad.net/~stub/launchpad/trivial/+merge/245822

Update to using python-swiftclient 2.3.1.

We now can call the close() method explicitly, although at first glance this appears to be a noop with the default HTTPConnection implementation. We still hope this fixes leaks though, as the new library also switches to using the requests library.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~stub/launchpad/trivial into lp:launchpad.
=== modified file 'lib/lp/services/librarianserver/storage.py'
--- lib/lp/services/librarianserver/storage.py	2014-10-17 09:21:23 +0000
+++ lib/lp/services/librarianserver/storage.py	2015-01-08 08:50:52 +0000
@@ -100,9 +100,11 @@
             defer.returnValue(swift_stream)
         except swiftclient.ClientException as x:
             if x.http_status != 404:
+                swift_connection.close()
                 self.swift_download_fails += 1
                 log.err(x)
         except Exception as x:
+            swift_connection.close()
             self.swift_download_fails += 1
             log.err(x)
 

=== modified file 'lib/lp/services/librarianserver/swift.py'
--- lib/lp/services/librarianserver/swift.py	2014-12-16 09:20:15 +0000
+++ lib/lp/services/librarianserver/swift.py	2015-01-08 08:50:52 +0000
@@ -122,6 +122,7 @@
                 log.debug2('{0} container already exists'.format(container))
             except swiftclient.ClientException as x:
                 if x.http_status != 404:
+                    swift_connection.close()
                     raise
                 log.info('Creating {0} container'.format(container))
                 swift_connection.put_container(container)
@@ -138,6 +139,7 @@
                         '{0} has incorrect size in Swift'.format(lfc))
             except swiftclient.ClientException as x:
                 if x.http_status != 404:
+                    swift_connection.close()
                     raise
                 log.info('Putting {0} into Swift ({1}, {2})'.format(
                     lfc, container, obj_name))
@@ -189,8 +191,13 @@
             seg_name = '%s/%04d' % (obj_name, segment)
             seg_size = min(fs_size - fs_file.tell(), MAX_SWIFT_OBJECT_SIZE)
             md5_stream = HashStream(fs_file)
+            start_position = md5_stream.tell()
             swift_md5_hash = swift_connection.put_object(
-                container, seg_name, md5_stream, seg_size)
+                container, seg_name,
+                LimitedStream(md5_stream, seg_size), seg_size)
+            end_position = md5_stream.tell()
+            assert end_position - start_position == seg_size, 'Read {}'.format(
+                end_position - start_position)
             segment_md5_hash = md5_stream.hash.hexdigest()
             assert swift_md5_hash == segment_md5_hash, (
                 "LibraryFileContent({0}) segment {1} upload corrupted".format(
@@ -290,6 +297,7 @@
 
     def close(self):
         self.closed = True
+        self._swift_connection.close()
         self._swift_connection = None
 
     def seek(self, offset):
@@ -323,6 +331,31 @@
         return self._stream.seek(offset)
 
 
+class LimitedStream:
+    """Limit the amount of data that can be read from a stream.
+
+    It seems the underlying requests library reads in fixed sized chunks,
+    rather than honoring the content length and only reading what it needs,
+    so we need to enforce the segment length cuttoff ourselves. If more data
+    is read than necessary from a HashStream, then it will calculate an
+    unexpected hash.
+    """
+    def __init__(self, stream, content_length):
+        self._stream = stream
+        self._content_length = content_length
+        self._bytes_read = 0
+
+    def read(self, size=-1):
+        remaining = self._content_length - self._bytes_read
+        if size == -1:
+            size = remaining
+        size = min(size, remaining)
+        chunk = self._stream.read(size)
+        self._bytes_read += len(chunk)
+        assert self._bytes_read <= self._content_length
+        return chunk
+
+
 class ConnectionPool:
     MAX_POOL_SIZE = 10
 
@@ -349,7 +382,8 @@
         if swift_connection not in self._pool:
             self._pool.append(swift_connection)
             while len(self._pool) > self.MAX_POOL_SIZE:
-                self._pool.pop(0)
+                c = self._pool.pop(0)
+                c.close()
 
     def _new_connection(self):
         return swiftclient.Connection(

=== modified file 'lib/lp/testing/swift/tests/test_fixture.py'
--- lib/lp/testing/swift/tests/test_fixture.py	2013-12-12 08:17:53 +0000
+++ lib/lp/testing/swift/tests/test_fixture.py	2015-01-08 08:50:52 +0000
@@ -8,6 +8,7 @@
 
 import httplib
 
+import requests.exceptions
 from s4 import hollow
 from swiftclient import client as swiftclient
 
@@ -61,14 +62,14 @@
         # authenticated.
         self.swift_fixture.shutdown()
         self.assertRaises(
-            httplib.HTTPException,
+            requests.exceptions.ConnectionError,
             client.get_object, "size", str(size))
 
         # And even if we bring it back up, existing connections
         # continue to fail
         self.swift_fixture.startup()
         self.assertRaises(
-            httplib.HTTPException,
+            swiftclient.ClientException,
             client.get_object, "size", str(size))
 
         # But fresh connections are fine.

=== modified file 'lib/lp_sitecustomize.py'
--- lib/lp_sitecustomize.py	2014-08-29 05:52:23 +0000
+++ lib/lp_sitecustomize.py	2015-01-08 08:50:52 +0000
@@ -88,14 +88,9 @@
 
 
 def silence_swiftclient_logger():
-    """Remove debug noise generated by swiftclient and keystoneclient.
-
-    swiftclient explicitly checks if debug messages are enabled on its
-    Logger, which is unfortunate as we disable them in the handler. Not
-    only does swiftclient then emit lots of noise, but it also turns
-    keystoneclient debugging on.
-    """
-    swiftclient.logger.setLevel(logging.INFO)
+    """Install the NullHandler on the swiftclient logger to silence noise."""
+    swiftclient.logger.addHandler(logging.NullHandler())
+    swiftclient.logger.propagate = False
 
 
 def silence_zcml_logger():

=== modified file 'versions.cfg'
--- versions.cfg	2014-12-08 02:23:41 +0000
+++ versions.cfg	2015-01-08 08:50:52 +0000
@@ -33,6 +33,8 @@
 feedvalidator = 0.0.0DEV-r1049
 fixtures = 0.3.9
 funkload = 1.16.1
+# Required by six
+futures = 2.2.0
 grokcore.component = 1.6
 html5browser = 0.0.9
 httplib2 = 0.8
@@ -102,7 +104,7 @@
 # reapplied a patch from wgrant to get codehosting going again.
 python-openid = 2.2.5-fix1034376
 python-subunit = 0.0.8beta
-python-swiftclient = 1.5.0
+python-swiftclient = 2.3.1
 rabbitfixture = 0.3.5
 requests = 1.2.3
 s4 = 0.1.2
@@ -110,7 +112,7 @@
 setuptools-git = 1.0
 simplejson = 3.1.3
 SimpleTAL = 4.3
-six = 1.3.0
+six = 1.9.0
 soupmatchers = 0.2
 sourcecodegen = 0.6.14
 # lp:~launchpad-committers/storm/with-without-datetime


Follow ups