Skip to content

Commit

Permalink
Fix wsgiref script use with oslo.config
Browse files Browse the repository at this point in the history
The script generated for wsgi-script entrypoint adds a argument for
listening on a specific port using argparse. Unfortunately it's not
compatible with oslo.config handling of config options, as used by
keystone for example. This fixes the situation by only parsing known
options.

Change-Id: I37f82e8d78a4288323854282da300c123561218a
Closes-Bug: #1501756
  • Loading branch information
Thomas Herve committed Apr 14, 2016
1 parent 6aa1149 commit 8778c64
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 51 deletions.
15 changes: 14 additions & 1 deletion pbr/packaging.py
Expand Up @@ -265,10 +265,23 @@ def have_nose():
my_ip = socket.gethostbyname(socket.gethostname())
parser = argparse.ArgumentParser(
description=%(import_target)s.__doc__,
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
usage='%%(prog)s [-h] [--port PORT] -- [passed options]')
parser.add_argument('--port', '-p', type=int, default=8000,
help='TCP port to listen on')
parser.add_argument('args',
nargs=argparse.REMAINDER,
metavar='-- [passed options]',
help="'--' is the separator of the arguments used "
"to start the WSGI server and the arguments passed "
"to the WSGI application.")
args = parser.parse_args()
if args.args:
if args.args[0] == '--':
args.args.pop(0)
else:
parser.error("unrecognized arguments: %%s" %% ' '.join(args.args))
sys.argv[1:] = args.args
server = wss.make_server('', args.port, %(invoke_target)s())
print("*" * 80)
Expand Down
104 changes: 58 additions & 46 deletions pbr/tests/test_wsgi.py
Expand Up @@ -23,15 +23,23 @@
# python 3
from urllib.request import urlopen

import fixtures

from pbr.tests import base


class TestWsgiScripts(base.BaseTestCase):

cmd_names = ('pbr_test_wsgi', 'pbr_test_wsgi_with_class')

def _get_path(self):
if os.path.isdir("%s/lib64" % self.temp_dir):
path = "%s/lib64" % self.temp_dir
else:
path = "%s/lib" % self.temp_dir
return ".:%s/python%s.%s/site-packages" % (
path,
sys.version_info[0],
sys.version_info[1])

def test_wsgi_script_install(self):
"""Test that we install a non-pkg-resources wsgi script."""
if os.name == 'nt':
Expand All @@ -40,13 +48,6 @@ def test_wsgi_script_install(self):
stdout, _, return_code = self.run_setup(
'install', '--prefix=%s' % self.temp_dir)

self.useFixture(
fixtures.EnvironmentVariable(
'PYTHONPATH', ".:%s/lib/python%s.%s/site-packages" % (
self.temp_dir,
sys.version_info[0],
sys.version_info[1])))

self._check_wsgi_install_content(stdout)

def test_wsgi_script_run(self):
Expand All @@ -63,56 +64,58 @@ def test_wsgi_script_run(self):
stdout, _, return_code = self.run_setup(
'install', '--prefix=%s' % self.temp_dir)

self.useFixture(
fixtures.EnvironmentVariable(
'PYTHONPATH', ".:%s/lib/python%s.%s/site-packages" % (
self.temp_dir,
sys.version_info[0],
sys.version_info[1])))
self._check_wsgi_install_content(stdout)

# Live test run the scripts and see that they respond to wsgi
# requests.
self._test_wsgi()

def _test_wsgi(self):
for cmd_name in self.cmd_names:
cmd = os.path.join(self.temp_dir, 'bin', cmd_name)
print("Running %s -p 0" % cmd)
p = subprocess.Popen([cmd, '-p', '0'], stdout=subprocess.PIPE,
stderr=subprocess.PIPE, cwd=self.temp_dir)
self.addCleanup(p.kill)
self._test_wsgi(cmd_name, b'Hello World')

def _test_wsgi(self, cmd_name, output, extra_args=None):
cmd = os.path.join(self.temp_dir, 'bin', cmd_name)
print("Running %s -p 0" % cmd)
popen_cmd = [cmd, '-p', '0']
if extra_args:
popen_cmd.extend(extra_args)

env = {'PYTHONPATH': self._get_path()}

p = subprocess.Popen(popen_cmd, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, cwd=self.temp_dir,
env=env)
self.addCleanup(p.kill)

stdoutdata = p.stdout.readline() # ****...
stdoutdata = p.stdout.readline() # ****...

stdoutdata = p.stdout.readline() # STARTING test server...
self.assertIn(
b"STARTING test server pbr_testpackage.wsgi",
stdoutdata)
stdoutdata = p.stdout.readline() # STARTING test server...
self.assertIn(
b"STARTING test server pbr_testpackage.wsgi",
stdoutdata)

stdoutdata = p.stdout.readline() # Available at ...
print(stdoutdata)
m = re.search(b'(http://[^:]+:\d+)/', stdoutdata)
self.assertIsNotNone(m, "Regex failed to match on %s" % stdoutdata)
stdoutdata = p.stdout.readline() # Available at ...
print(stdoutdata)
m = re.search(b'(http://[^:]+:\d+)/', stdoutdata)
self.assertIsNotNone(m, "Regex failed to match on %s" % stdoutdata)

stdoutdata = p.stdout.readline() # DANGER! ...
self.assertIn(
b"DANGER! For testing only, do not use in production",
stdoutdata)
stdoutdata = p.stdout.readline() # DANGER! ...
self.assertIn(
b"DANGER! For testing only, do not use in production",
stdoutdata)

stdoutdata = p.stdout.readline() # ***...
stdoutdata = p.stdout.readline() # ***...

f = urlopen(m.group(1).decode('utf-8'))
self.assertEqual(b"Hello World", f.read())
f = urlopen(m.group(1).decode('utf-8'))
self.assertEqual(output, f.read())

# Request again so that the application can force stderr.flush(),
# otherwise the log is buffered and the next readline() will hang.
urlopen(m.group(1).decode('utf-8'))
# Request again so that the application can force stderr.flush(),
# otherwise the log is buffered and the next readline() will hang.
urlopen(m.group(1).decode('utf-8'))

stdoutdata = p.stderr.readline()
# we should have logged an HTTP request, return code 200, that
# returned 11 bytes
self.assertIn(b'"GET / HTTP/1.1" 200 11', stdoutdata)
stdoutdata = p.stderr.readline()
# we should have logged an HTTP request, return code 200, that
# returned the right amount of bytes
status = '"GET / HTTP/1.1" 200 %d' % len(output)
self.assertIn(status.encode('utf-8'), stdoutdata)

def _check_wsgi_install_content(self, install_stdout):
for cmd_name in self.cmd_names:
Expand Down Expand Up @@ -145,3 +148,12 @@ def _check_wsgi_install_content(self, install_stdout):
self.assertIn(main_block, script_txt)
self.assertIn(starting_block, script_txt)
self.assertIn(else_block, script_txt)

def test_with_argument(self):
if os.name == 'nt':
self.skipTest('Windows support is passthrough')

stdout, _, return_code = self.run_setup(
'install', '--prefix=%s' % self.temp_dir)

self._test_wsgi('pbr_test_wsgi', b'Foo Bar', ["--", "-c", "Foo Bar"])
14 changes: 10 additions & 4 deletions pbr/tests/testpackage/pbr_testpackage/wsgi.py
Expand Up @@ -14,21 +14,27 @@
# limitations under the License.
from __future__ import print_function

import argparse
import functools
import sys


def application(env, start_response):
def application(env, start_response, data):
sys.stderr.flush() # Force the previous request log to be written.
start_response('200 OK', [('Content-Type', 'text/html')])
return [b"Hello World"]
return [data.encode('utf-8')]


def main():
return application
parser = argparse.ArgumentParser(description='Return a string.')
parser.add_argument('--content', '-c', help='String returned',
default='Hello World')
args = parser.parse_args()
return functools.partial(application, data=args.content)


class WSGI(object):

@classmethod
def app(self):
return application
return functools.partial(application, data='Hello World')

0 comments on commit 8778c64

Please sign in to comment.