Allow request timeout to be specified.
Add a new cli argument (--timeout) which is by default 600 seconds which will be set in the requests library so that timeouts can occur correctly. Change-Id: I845c55dfb6f6b8345663ccdb5b150a2655f20026
This commit is contained in:
@@ -60,6 +60,11 @@ class HTTPClient(object):
|
|||||||
cert=None, insecure=False, original_ip=None, debug=False,
|
cert=None, insecure=False, original_ip=None, debug=False,
|
||||||
auth_ref=None, use_keyring=False, force_new_token=False,
|
auth_ref=None, use_keyring=False, force_new_token=False,
|
||||||
stale_duration=None):
|
stale_duration=None):
|
||||||
|
"""Construct a new http client
|
||||||
|
|
||||||
|
@param: timeout the request libary timeout in seconds (default None)
|
||||||
|
|
||||||
|
"""
|
||||||
self.version = 'v2.0'
|
self.version = 'v2.0'
|
||||||
# set baseline defaults
|
# set baseline defaults
|
||||||
self.username = None
|
self.username = None
|
||||||
@@ -69,6 +74,10 @@ class HTTPClient(object):
|
|||||||
self.token = None
|
self.token = None
|
||||||
self.auth_token = None
|
self.auth_token = None
|
||||||
self.management_url = None
|
self.management_url = None
|
||||||
|
if timeout is not None:
|
||||||
|
self.timeout = float(timeout)
|
||||||
|
else:
|
||||||
|
self.timeout = None
|
||||||
# if loading from a dictionary passed in via auth_ref,
|
# if loading from a dictionary passed in via auth_ref,
|
||||||
# load values from AccessInfo parsing that dictionary
|
# load values from AccessInfo parsing that dictionary
|
||||||
self.auth_ref = access.AccessInfo(**auth_ref) if auth_ref else None
|
self.auth_ref = access.AccessInfo(**auth_ref) if auth_ref else None
|
||||||
@@ -320,6 +329,8 @@ class HTTPClient(object):
|
|||||||
del request_kwargs['body']
|
del request_kwargs['body']
|
||||||
if self.cert:
|
if self.cert:
|
||||||
request_kwargs['cert'] = self.cert
|
request_kwargs['cert'] = self.cert
|
||||||
|
if self.timeout is not None:
|
||||||
|
request_kwargs.setdefault('timeout', self.timeout)
|
||||||
|
|
||||||
self.http_log_req((url, method,), request_kwargs)
|
self.http_log_req((url, method,), request_kwargs)
|
||||||
resp = requests.request(
|
resp = requests.request(
|
||||||
|
@@ -33,6 +33,20 @@ from keystoneclient.generic import shell as shell_generic
|
|||||||
from keystoneclient.contrib.bootstrap import shell as shell_bootstrap
|
from keystoneclient.contrib.bootstrap import shell as shell_bootstrap
|
||||||
|
|
||||||
|
|
||||||
|
def positive_non_zero_float(argument_value):
|
||||||
|
if argument_value is None:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
value = float(argument_value)
|
||||||
|
except ValueError:
|
||||||
|
msg = "%s must be a float" % argument_value
|
||||||
|
raise argparse.ArgumentTypeError(msg)
|
||||||
|
if value <= 0:
|
||||||
|
msg = "%s must be greater than 0" % argument_value
|
||||||
|
raise argparse.ArgumentTypeError(msg)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
def env(*vars, **kwargs):
|
def env(*vars, **kwargs):
|
||||||
"""Search for the first defined of possibly many env vars
|
"""Search for the first defined of possibly many env vars
|
||||||
|
|
||||||
@@ -49,8 +63,11 @@ def env(*vars, **kwargs):
|
|||||||
|
|
||||||
class OpenStackIdentityShell(object):
|
class OpenStackIdentityShell(object):
|
||||||
|
|
||||||
|
def __init__(self, parser_class=argparse.ArgumentParser):
|
||||||
|
self.parser_class = parser_class
|
||||||
|
|
||||||
def get_base_parser(self):
|
def get_base_parser(self):
|
||||||
parser = argparse.ArgumentParser(
|
parser = self.parser_class(
|
||||||
prog='keystone',
|
prog='keystone',
|
||||||
description=__doc__.strip(),
|
description=__doc__.strip(),
|
||||||
epilog='See "keystone help COMMAND" '
|
epilog='See "keystone help COMMAND" '
|
||||||
@@ -74,6 +91,12 @@ class OpenStackIdentityShell(object):
|
|||||||
action='store_true',
|
action='store_true',
|
||||||
help=argparse.SUPPRESS)
|
help=argparse.SUPPRESS)
|
||||||
|
|
||||||
|
parser.add_argument('--timeout',
|
||||||
|
default=600,
|
||||||
|
type=positive_non_zero_float,
|
||||||
|
metavar='<seconds>',
|
||||||
|
help="Set request timeout (in seconds)")
|
||||||
|
|
||||||
parser.add_argument('--os-username',
|
parser.add_argument('--os-username',
|
||||||
metavar='<auth-user-name>',
|
metavar='<auth-user-name>',
|
||||||
default=env('OS_USERNAME'),
|
default=env('OS_USERNAME'),
|
||||||
@@ -385,7 +408,8 @@ class OpenStackIdentityShell(object):
|
|||||||
key=args.os_key,
|
key=args.os_key,
|
||||||
cert=args.os_cert,
|
cert=args.os_cert,
|
||||||
insecure=args.insecure,
|
insecure=args.insecure,
|
||||||
debug=args.debug)
|
debug=args.debug,
|
||||||
|
timeout=args.timeout)
|
||||||
else:
|
else:
|
||||||
token = None
|
token = None
|
||||||
if args.os_token and args.os_endpoint:
|
if args.os_token and args.os_endpoint:
|
||||||
@@ -407,7 +431,8 @@ class OpenStackIdentityShell(object):
|
|||||||
debug=args.debug,
|
debug=args.debug,
|
||||||
use_keyring=args.os_cache,
|
use_keyring=args.os_cache,
|
||||||
force_new_token=args.force_new_token,
|
force_new_token=args.force_new_token,
|
||||||
stale_duration=args.stale_duration)
|
stale_duration=args.stale_duration,
|
||||||
|
timeout=args.timeout)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
args.func(self.cs, args)
|
args.func(self.cs, args)
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import os
|
import argparse
|
||||||
|
import json
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
import fixtures
|
import fixtures
|
||||||
@@ -17,6 +18,11 @@ DEFAULT_TENANT_NAME = 'tenant_name'
|
|||||||
DEFAULT_AUTH_URL = 'http://127.0.0.1:5000/v2.0/'
|
DEFAULT_AUTH_URL = 'http://127.0.0.1:5000/v2.0/'
|
||||||
|
|
||||||
|
|
||||||
|
class NoExitArgumentParser(argparse.ArgumentParser):
|
||||||
|
def error(self, message):
|
||||||
|
raise exceptions.CommandError(message)
|
||||||
|
|
||||||
|
|
||||||
class ShellTest(utils.TestCase):
|
class ShellTest(utils.TestCase):
|
||||||
|
|
||||||
FAKE_ENV = {
|
FAKE_ENV = {
|
||||||
@@ -27,6 +33,10 @@ class ShellTest(utils.TestCase):
|
|||||||
'OS_AUTH_URL': DEFAULT_AUTH_URL,
|
'OS_AUTH_URL': DEFAULT_AUTH_URL,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def _tolerant_shell(self, cmd):
|
||||||
|
t_shell = openstack_shell.OpenStackIdentityShell(NoExitArgumentParser)
|
||||||
|
t_shell.main(cmd.split())
|
||||||
|
|
||||||
# Patch os.environ to avoid required auth info.
|
# Patch os.environ to avoid required auth info.
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
||||||
@@ -292,6 +302,34 @@ class ShellTest(utils.TestCase):
|
|||||||
shell('ec2-credentials-delete')
|
shell('ec2-credentials-delete')
|
||||||
assert do_shell_mock.called
|
assert do_shell_mock.called
|
||||||
|
|
||||||
|
def test_timeout_parse_invalid_type(self):
|
||||||
|
for f in ['foobar', 'xyz']:
|
||||||
|
cmd = '--timeout %s endpoint-create' % (f)
|
||||||
|
self.assertRaises(exceptions.CommandError,
|
||||||
|
self._tolerant_shell, cmd)
|
||||||
|
|
||||||
|
def test_timeout_parse_invalid_number(self):
|
||||||
|
for f in [-1, 0]:
|
||||||
|
cmd = '--timeout %s endpoint-create' % (f)
|
||||||
|
self.assertRaises(exceptions.CommandError,
|
||||||
|
self._tolerant_shell, cmd)
|
||||||
|
|
||||||
|
def test_do_timeout(self):
|
||||||
|
response_mock = mock.MagicMock()
|
||||||
|
response_mock.status_code = 200
|
||||||
|
response_mock.text = json.dumps({
|
||||||
|
'endpoints': [],
|
||||||
|
})
|
||||||
|
request_mock = mock.MagicMock(return_value=response_mock)
|
||||||
|
with mock.patch('requests.request', request_mock):
|
||||||
|
shell(('--timeout 2 --os-token=blah --os-endpoint=blah'
|
||||||
|
' --os-auth-url=blah.com endpoint-list'))
|
||||||
|
request_mock.assert_called_with(mock.ANY, mock.ANY,
|
||||||
|
timeout=2,
|
||||||
|
headers=mock.ANY,
|
||||||
|
verify=mock.ANY,
|
||||||
|
config=mock.ANY)
|
||||||
|
|
||||||
def test_do_endpoints(self):
|
def test_do_endpoints(self):
|
||||||
do_shell_mock = mock.MagicMock()
|
do_shell_mock = mock.MagicMock()
|
||||||
# grab the decorators for do_endpoint_create
|
# grab the decorators for do_endpoint_create
|
||||||
|
Reference in New Issue
Block a user