Merge lp:~stub/launchpad/mock-swift into lp:launchpad

Proposed by Stuart Bishop
Status: Merged
Merged at revision: 16762
Proposed branch: lp:~stub/launchpad/mock-swift
Merge into: lp:launchpad
Diff against target: 335 lines (+230/-0)
6 files modified
lib/lp/testing/layers.py (+18/-0)
lib/lp/testing/swift/fixture.py (+90/-0)
lib/lp/testing/swift/hollow.tac (+33/-0)
lib/lp/testing/swift/tests/test_fixture.py (+75/-0)
setup.py (+3/-0)
versions.cfg (+11/-0)
To merge this branch: bzr merge lp:~stub/launchpad/mock-swift
Reviewer Review Type Date Requested Status
William Grant code Approve
Canonical Launchpad Engineering Pending
Review via email: mp+178047@code.launchpad.net

Description of the change

Swift test infrastructure

To post a comment you must log in.
Revision history for this message
Stuart Bishop (stub) wrote :

Blocked landing for now, as there is still one dependency to add to lp-sourcedeps (s4 has not yet been publicly released).

Revision history for this message
Stuart Bishop (stub) wrote :

I've added the s4 tarball to the dependencies branch.

Revision history for this message
William Grant (wgrant) wrote :

154 +import os
155 +import sys
156 +import logging
157 +
158 +from OpenSSL import crypto

os, sys and crypto are unused.

133 + def startup(self):
134 + self.setUp()
135 +
136 + def shutdown(self):
137 + self.cleanUp()
138 + while self._hasDaemonStarted():
139 + time.sleep(0.1)

Are these only here for the shutdown test? I'm not sure it's necessarily valuable to have them factored out, but it might also be worth pushing them up to TacTestSetup.

264 === modified file 'setup.py'

There should only be two new deps here; why are the indirect ones included directly? I imagine it might be for the setuptools-git pre_requires mess, but it's probably better to patch that out, as it's dreadfully fragile when there's no network access, and a security hole when there is network access.

review: Needs Fixing (code)
Revision history for this message
Stuart Bishop (stub) wrote :

I have fixed the imports.

Per IRC, the wait-until-really-shutdown is needed for tests confirming things work correctly when Swift is dead. I'll submit a bug report on txfixtures - it can be argued this behavior belongs in the base fixture's cleanUp, although for general use that might need to be combined with a timeout fixture.

For the setup.py dependencies: s4 is needed by the test suite. mock is unused and I have removed it from setup.py and versions.cfg. python-swiftclient is needed by the new Librarian code. python-keystoneclient is needed because we are using v2.0 authentication, and python-swiftclient bombs out at runtime if you attempt to do this without the keystoneclient module installed.

Instead of using tarballs for python-swiftclient and python-keystone client I built .egg files locally and put them in the dependency branch. Buildout uses the .egg directly, skipping the step building the .egg from the tarball and avoiding network access.

Revision history for this message
Stuart Bishop (stub) wrote :

Bug #1222711 for the txfixtures issue.

Revision history for this message
William Grant (wgrant) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/testing/layers.py'
--- lib/lp/testing/layers.py 2013-06-20 05:50:00 +0000
+++ lib/lp/testing/layers.py 2013-09-09 10:28:33 +0000
@@ -38,6 +38,7 @@
38 'LibrarianLayer',38 'LibrarianLayer',
39 'PageTestLayer',39 'PageTestLayer',
40 'RabbitMQLayer',40 'RabbitMQLayer',
41 'SwiftLayer',
41 'TwistedAppServerLayer',42 'TwistedAppServerLayer',
42 'TwistedLaunchpadZopelessLayer',43 'TwistedLaunchpadZopelessLayer',
43 'TwistedLayer',44 'TwistedLayer',
@@ -147,6 +148,7 @@
147 reset_logging,148 reset_logging,
148 )149 )
149from lp.testing.pgsql import PgTestSetup150from lp.testing.pgsql import PgTestSetup
151from lp.testing.swift.fixture import SwiftFixture
150from lp.testing.smtpd import SMTPController152from lp.testing.smtpd import SMTPController
151153
152154
@@ -822,6 +824,22 @@
822 return cls._db_fixture.dropDb()824 return cls._db_fixture.dropDb()
823825
824826
827class SwiftLayer(BaseLayer):
828 @classmethod
829 @profiled
830 def setUp(cls):
831 cls.swift_fixture = SwiftFixture()
832 cls.swift_fixture.setUp()
833
834 @classmethod
835 @profiled
836 def tearDown(cls):
837 swift = cls.swift_fixture
838 if swift is not None:
839 cls.swift_fixture = None
840 swift.cleanUp()
841
842
825class LibrarianLayer(DatabaseLayer):843class LibrarianLayer(DatabaseLayer):
826 """Provides tests access to a Librarian instance.844 """Provides tests access to a Librarian instance.
827845
828846
=== added directory 'lib/lp/testing/swift'
=== added file 'lib/lp/testing/swift/__init__.py'
=== added file 'lib/lp/testing/swift/fixture.py'
--- lib/lp/testing/swift/fixture.py 1970-01-01 00:00:00 +0000
+++ lib/lp/testing/swift/fixture.py 2013-09-09 10:28:33 +0000
@@ -0,0 +1,90 @@
1# Copyright 2013 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Mock Swift test fixture."""
5
6__metaclass__ = type
7__all__ = ['SwiftFixture']
8
9import os.path
10import shutil
11import socket
12import tempfile
13import time
14
15from fixtures import FunctionFixture
16from s4 import hollow
17from swiftclient import client as swiftclient
18import testtools.content
19import testtools.content_type
20from txfixtures.tachandler import TacTestFixture
21
22from lp.services.config import config
23
24
25class SwiftFixture(TacTestFixture):
26
27 tacfile = os.path.join(os.path.dirname(__file__), 'hollow.tac')
28 pidfile = None
29 logfile = None
30 root = None
31 daemon_port = None
32
33 def setUp(self, spew=False, umask=None):
34 # Pick a random, free port.
35 if self.daemon_port is None:
36 sock = socket.socket()
37 sock.bind(('', 0))
38 self.daemon_port = sock.getsockname()[1]
39 sock.close()
40 self.logfile = os.path.join(
41 config.root, 'logs', 'hollow-{}.log'.format(self.daemon_port))
42 self.pidfile = os.path.join(
43 config.root, 'logs', 'hollow-{}.pid'.format(self.daemon_port))
44 assert self.daemon_port is not None
45
46 super(SwiftFixture, self).setUp(
47 spew, umask,
48 os.path.join(config.root, 'bin', 'py'),
49 os.path.join(config.root, 'bin', 'twistd'))
50
51 def cleanUp(self):
52 if self.logfile is not None and os.path.exists(self.logfile):
53 self.addDetail(
54 'log-file', testtools.content.content_from_stream(
55 open(self.logfile, 'r'), testtools.content_type.UTF8_TEXT))
56 os.unlink(self.logfile)
57 super(SwiftFixture, self).cleanUp()
58
59 def setUpRoot(self):
60 # Create a root directory.
61 if self.root is None or not os.path.isdir(self.root):
62 root_fixture = FunctionFixture(tempfile.mkdtemp, shutil.rmtree)
63 self.useFixture(root_fixture)
64 self.root = root_fixture.fn_result
65 os.chmod(self.root, 0o700)
66 assert os.path.isdir(self.root)
67
68 # Pass on options to the daemon.
69 os.environ['HOLLOW_ROOT'] = self.root
70 os.environ['HOLLOW_PORT'] = str(self.daemon_port)
71
72 def connect(
73 self, tenant_name=hollow.DEFAULT_TENANT_NAME,
74 username=hollow.DEFAULT_USERNAME, password=hollow.DEFAULT_PASSWORD):
75 """Return a valid connection to our mock Swift"""
76 port = self.daemon_port
77 client = swiftclient.Connection(
78 authurl="http://localhost:%d/keystone/v2.0/" % port,
79 auth_version="2.0", tenant_name=tenant_name,
80 user=username, key=password,
81 retries=0, insecure=True)
82 return client
83
84 def startup(self):
85 self.setUp()
86
87 def shutdown(self):
88 self.cleanUp()
89 while self._hasDaemonStarted():
90 time.sleep(0.1)
091
=== added file 'lib/lp/testing/swift/hollow.tac'
--- lib/lp/testing/swift/hollow.tac 1970-01-01 00:00:00 +0000
+++ lib/lp/testing/swift/hollow.tac 2013-09-09 10:28:33 +0000
@@ -0,0 +1,33 @@
1# -*- python -*-
2# Copyright 2013 Canonical Ltd. This software is licensed under the
3# GNU Affero General Public License version 3 (see the file LICENSE).
4
5'''Launch a mock Swift service.'''
6
7__metaclass__ = type
8__all__ = []
9
10import os.path
11import logging
12
13import twisted.web.server
14from twisted.application import internet, service
15
16logging.basicConfig()
17
18from s4 import hollow
19
20storedir = os.environ['HOLLOW_ROOT']
21assert os.path.exists(storedir)
22
23application = service.Application('hollow')
24root = hollow.Root(storage_dir=storedir, hostname='localhost')
25
26# make sure "the bucket" is created
27root.swift.addBucket("the bucket")
28site = twisted.web.server.Site(root)
29
30port = int(os.environ['HOLLOW_PORT'])
31
32sc = service.IServiceCollection(application)
33internet.TCPServer(port, site).setServiceParent(sc)
034
=== added directory 'lib/lp/testing/swift/tests'
=== added file 'lib/lp/testing/swift/tests/__init__.py'
=== added file 'lib/lp/testing/swift/tests/test_fixture.py'
--- lib/lp/testing/swift/tests/test_fixture.py 1970-01-01 00:00:00 +0000
+++ lib/lp/testing/swift/tests/test_fixture.py 2013-09-09 10:28:33 +0000
@@ -0,0 +1,75 @@
1# Copyright 2013 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Testing the mock Swift test fixture."""
5
6__metaclass__ = type
7__all__ = []
8
9import httplib
10
11from swiftclient import client as swiftclient
12
13from lp.testing import TestCase
14from lp.testing.layers import BaseLayer
15from lp.testing.swift.fixture import SwiftFixture
16
17
18class TestSwiftFixture(TestCase):
19 layer = BaseLayer
20
21 def setUp(self):
22 super(TestSwiftFixture, self).setUp()
23 self.swift_fixture = SwiftFixture()
24 self.useFixture(self.swift_fixture)
25
26 def test_basic(self):
27 client = self.swift_fixture.connect()
28 size = 30
29 headers, body = client.get_object("size", str(size))
30 self.assertEquals(body, "0" * size)
31 self.assertEqual(str(size), headers["content-length"])
32 self.assertEqual("text/plain", headers["content-type"])
33
34 def test_shutdown_and_startup(self):
35 # This test demonstrates how the Swift client deals with a
36 # flapping Swift server. In particular, that once a connection
37 # has started failing it will continue failing so we need to
38 # ensure that once we encounter a fail we open a fresh
39 # connection. This is probably a property of our mock Swift
40 # server rather than reality but the mock is a required target.
41 size = 30
42
43 # With no Swift server, a fresh connection fails with
44 # a swiftclient.ClientException when it fails to
45 # authenticate.
46 self.swift_fixture.shutdown()
47 client = self.swift_fixture.connect()
48 self.assertRaises(
49 swiftclient.ClientException,
50 client.get_object, "size", str(size))
51
52 # Things work fine when the Swift server is up.
53 self.swift_fixture.startup()
54 headers, body = client.get_object("size", str(size))
55 self.assertEquals(body, "0" * size)
56
57 # But if the Swift server goes away again, we end up with
58 # different failures since the connection has already
59 # authenticated.
60 self.swift_fixture.shutdown()
61 self.assertRaises(
62 httplib.HTTPException,
63 client.get_object, "size", str(size))
64
65 # And even if we bring it back up, existing connections
66 # continue to fail
67 self.swift_fixture.startup()
68 self.assertRaises(
69 httplib.HTTPException,
70 client.get_object, "size", str(size))
71
72 # But fresh connections are fine.
73 client = self.swift_fixture.connect()
74 headers, body = client.get_object("size", str(size))
75 self.assertEquals(body, "0" * size)
076
=== modified file 'setup.py'
--- setup.py 2013-04-15 07:03:38 +0000
+++ setup.py 2013-09-09 10:28:33 +0000
@@ -41,7 +41,9 @@
41 'html5browser',41 'html5browser',
42 'pygpgme',42 'pygpgme',
43 'python-debian',43 'python-debian',
44 'python-keystoneclient',
44 'python-subunit',45 'python-subunit',
46 'python-swiftclient',
45 'launchpadlib',47 'launchpadlib',
46 'lazr.batchnavigator',48 'lazr.batchnavigator',
47 'lazr.config',49 'lazr.config',
@@ -74,6 +76,7 @@
74 'python-openid',76 'python-openid',
75 'pytz',77 'pytz',
76 'rabbitfixture',78 'rabbitfixture',
79 's4',
77 'setproctitle',80 'setproctitle',
78 'setuptools',81 'setuptools',
79 'Sphinx',82 'Sphinx',
8083
=== modified file 'versions.cfg'
--- versions.cfg 2013-08-16 05:15:35 +0000
+++ versions.cfg 2013-09-09 10:28:33 +0000
@@ -20,6 +20,7 @@
20celery = 2.5.120celery = 2.5.1
21Chameleon = 2.1121Chameleon = 2.11
22cssutils = 0.9.1022cssutils = 0.9.10
23d2to1 = 0.2.10
23Django = 1.424Django = 1.4
24# Required by pydkim25# Required by pydkim
25dnspython = 1.10.026dnspython = 1.10.0
@@ -66,8 +67,12 @@
66oops-twisted = 0.0.667oops-twisted = 0.0.6
67oops-wsgi = 0.0.868oops-wsgi = 0.0.8
68ordereddict = 1.169ordereddict = 1.1
70oslo.config = 1.1.1
69paramiko = 1.7.7.271paramiko = 1.7.7.2
72pbr = 0.5.20
70pgbouncer = 0.0.873pgbouncer = 0.0.8
74pip = 1.4
75prettytable = 0.7.2
71psycopg2 = 2.4.676psycopg2 = 2.4.6
72pyasn1 = 0.1.677pyasn1 = 0.1.6
73pycrypto = 2.678pycrypto = 2.6
@@ -80,6 +85,7 @@
80pystache = 0.5.385pystache = 0.5.3
81python-dateutil = 1.586python-dateutil = 1.5
82python-debian = 0.1.2187python-debian = 0.1.21
88python-keystoneclient = 0.3.1
83# lp:python-memcached89# lp:python-memcached
84# r57 (includes a fix for bug 974632)90# r57 (includes a fix for bug 974632)
85# bzr branch lp:python-memcached -r57 memcached-1.4991# bzr branch lp:python-memcached -r57 memcached-1.49
@@ -94,15 +100,20 @@
94# reapplied a patch from wgrant to get codehosting going again.100# reapplied a patch from wgrant to get codehosting going again.
95python-openid = 2.2.5-fix1034376101python-openid = 2.2.5-fix1034376
96python-subunit = 0.0.8beta102python-subunit = 0.0.8beta
103python-swiftclient = 1.5.0
97# lp-1 Build of lp:~benji/rabbitfixture/longer-timeout revision: r30104# lp-1 Build of lp:~benji/rabbitfixture/longer-timeout revision: r30
98# lp-2 Build of lp:~frankban/rabbitfixture/longer-timeout revision: r32105# lp-2 Build of lp:~frankban/rabbitfixture/longer-timeout revision: r32
99# lp-3 Build of lp:~frankban/rabbitfixture/lp3 revision: r33106# lp-3 Build of lp:~frankban/rabbitfixture/lp3 revision: r33
100# lp-4 Build of lp:~frankban/rabbitfixture/lp4 revision: r34107# lp-4 Build of lp:~frankban/rabbitfixture/lp4 revision: r34
101# to build: python setup.py sdist108# to build: python setup.py sdist
102rabbitfixture = 0.3.3-lp-4109rabbitfixture = 0.3.3-lp-4
110requests = 1.2.3
111s4 = 0.1.1
103setproctitle = 1.1.7112setproctitle = 1.1.7
113setuptools-git = 1.0
104simplejson = 3.1.3114simplejson = 3.1.3
105SimpleTAL = 4.3115SimpleTAL = 4.3
116six = 1.3.0
106soupmatchers = 0.2117soupmatchers = 0.2
107sourcecodegen = 0.6.14118sourcecodegen = 0.6.14
108# lp:~launchpad-committers/storm/with-without-datetime119# lp:~launchpad-committers/storm/with-without-datetime