Introduce API micro version
The patch adds 'OpenStack-API-Version' header for each requests. Change-Id: I621a4268685a1c188a004dcfa576775b424d2610 Implements: blueprint api-microversion
This commit is contained in:
parent
fa61ba016e
commit
20ca8186c6
@ -35,6 +35,7 @@ USER_AGENT = 'python-zunclient'
|
|||||||
CHUNKSIZE = 1024 * 64 # 64kB
|
CHUNKSIZE = 1024 * 64 # 64kB
|
||||||
|
|
||||||
API_VERSION = '/v1'
|
API_VERSION = '/v1'
|
||||||
|
DEFAULT_API_VERSION = 'latest'
|
||||||
|
|
||||||
|
|
||||||
def _extract_error_json(body):
|
def _extract_error_json(body):
|
||||||
@ -65,10 +66,11 @@ def _extract_error_json(body):
|
|||||||
|
|
||||||
class HTTPClient(object):
|
class HTTPClient(object):
|
||||||
|
|
||||||
def __init__(self, endpoint, **kwargs):
|
def __init__(self, endpoint, api_version=DEFAULT_API_VERSION, **kwargs):
|
||||||
self.endpoint = endpoint
|
self.endpoint = endpoint
|
||||||
self.auth_token = kwargs.get('token')
|
self.auth_token = kwargs.get('token')
|
||||||
self.auth_ref = kwargs.get('auth_ref')
|
self.auth_ref = kwargs.get('auth_ref')
|
||||||
|
self.api_version = api_version
|
||||||
self.connection_params = self.get_connection_params(endpoint, **kwargs)
|
self.connection_params = self.get_connection_params(endpoint, **kwargs)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -155,6 +157,10 @@ class HTTPClient(object):
|
|||||||
# Copy the kwargs so we can reuse the original in case of redirects
|
# Copy the kwargs so we can reuse the original in case of redirects
|
||||||
kwargs['headers'] = copy.deepcopy(kwargs.get('headers', {}))
|
kwargs['headers'] = copy.deepcopy(kwargs.get('headers', {}))
|
||||||
kwargs['headers'].setdefault('User-Agent', USER_AGENT)
|
kwargs['headers'].setdefault('User-Agent', USER_AGENT)
|
||||||
|
if self.api_version:
|
||||||
|
version_string = 'container %s' % self.api_version
|
||||||
|
kwargs['headers'].setdefault(
|
||||||
|
'OpenStack-API-Version', version_string)
|
||||||
if self.auth_token:
|
if self.auth_token:
|
||||||
kwargs['headers'].setdefault('X-Auth-Token', self.auth_token)
|
kwargs['headers'].setdefault('X-Auth-Token', self.auth_token)
|
||||||
|
|
||||||
@ -307,7 +313,10 @@ class VerifiedHTTPSConnection(six.moves.http_client.HTTPSConnection):
|
|||||||
class SessionClient(adapter.LegacyJsonAdapter):
|
class SessionClient(adapter.LegacyJsonAdapter):
|
||||||
"""HTTP client based on Keystone client session."""
|
"""HTTP client based on Keystone client session."""
|
||||||
|
|
||||||
def __init__(self, user_agent=USER_AGENT, logger=LOG, *args, **kwargs):
|
def __init__(self, user_agent=USER_AGENT, logger=LOG,
|
||||||
|
api_version=DEFAULT_API_VERSION, *args, **kwargs):
|
||||||
|
self.user_agent = USER_AGENT
|
||||||
|
self.api_version = api_version
|
||||||
super(SessionClient, self).__init__(*args, **kwargs)
|
super(SessionClient, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def _http_request(self, url, method, **kwargs):
|
def _http_request(self, url, method, **kwargs):
|
||||||
@ -318,6 +327,14 @@ class SessionClient(adapter.LegacyJsonAdapter):
|
|||||||
kwargs.setdefault('auth', self.auth)
|
kwargs.setdefault('auth', self.auth)
|
||||||
kwargs.setdefault('endpoint_override', self.endpoint_override)
|
kwargs.setdefault('endpoint_override', self.endpoint_override)
|
||||||
|
|
||||||
|
# Copy the kwargs so we can reuse the original in case of redirects
|
||||||
|
kwargs['headers'] = copy.deepcopy(kwargs.get('headers', {}))
|
||||||
|
kwargs['headers'].setdefault('User-Agent', self.user_agent)
|
||||||
|
if self.api_version:
|
||||||
|
version_string = 'container %s' % self.api_version
|
||||||
|
kwargs['headers'].setdefault(
|
||||||
|
'OpenStack-API-Version', version_string)
|
||||||
|
|
||||||
# NOTE(kevinz): osprofiler_web.get_trace_id_headers does not add any
|
# NOTE(kevinz): osprofiler_web.get_trace_id_headers does not add any
|
||||||
# headers in case if osprofiler is not initialized.
|
# headers in case if osprofiler is not initialized.
|
||||||
if osprofiler_web:
|
if osprofiler_web:
|
||||||
|
@ -55,11 +55,12 @@ except ImportError:
|
|||||||
from zunclient.common.apiclient import auth
|
from zunclient.common.apiclient import auth
|
||||||
from zunclient.common import cliutils
|
from zunclient.common import cliutils
|
||||||
from zunclient import exceptions as exc
|
from zunclient import exceptions as exc
|
||||||
|
from zunclient.i18n import _
|
||||||
from zunclient.v1 import client as client_v1
|
from zunclient.v1 import client as client_v1
|
||||||
from zunclient.v1 import shell as shell_v1
|
from zunclient.v1 import shell as shell_v1
|
||||||
from zunclient import version
|
from zunclient import version
|
||||||
|
|
||||||
DEFAULT_API_VERSION = '1'
|
LATEST_API_VERSION = ('1', 'latest')
|
||||||
DEFAULT_ENDPOINT_TYPE = 'publicURL'
|
DEFAULT_ENDPOINT_TYPE = 'publicURL'
|
||||||
DEFAULT_SERVICE_TYPE = 'container'
|
DEFAULT_SERVICE_TYPE = 'container'
|
||||||
|
|
||||||
@ -331,7 +332,7 @@ class OpenStackZunShell(object):
|
|||||||
metavar='<zun-api-ver>',
|
metavar='<zun-api-ver>',
|
||||||
default=cliutils.env(
|
default=cliutils.env(
|
||||||
'ZUN_API_VERSION',
|
'ZUN_API_VERSION',
|
||||||
default=DEFAULT_API_VERSION),
|
default='latest'),
|
||||||
help='Accepts "api", '
|
help='Accepts "api", '
|
||||||
'defaults to env[ZUN_API_VERSION].')
|
'defaults to env[ZUN_API_VERSION].')
|
||||||
parser.add_argument('--zun_api_version',
|
parser.add_argument('--zun_api_version',
|
||||||
@ -386,7 +387,7 @@ class OpenStackZunShell(object):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
actions_modules = {
|
actions_modules = {
|
||||||
'1': shell_v1.COMMAND_MODULES,
|
'1': shell_v1.COMMAND_MODULES
|
||||||
}[version]
|
}[version]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
actions_modules = shell_v1.COMMAND_MODULES
|
actions_modules = shell_v1.COMMAND_MODULES
|
||||||
@ -445,6 +446,34 @@ class OpenStackZunShell(object):
|
|||||||
logging.basicConfig(level=logging.CRITICAL,
|
logging.basicConfig(level=logging.CRITICAL,
|
||||||
format=streamformat)
|
format=streamformat)
|
||||||
|
|
||||||
|
def _check_version(self, api_version):
|
||||||
|
if api_version == 'latest':
|
||||||
|
return LATEST_API_VERSION
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
versions = tuple(int(i) for i in api_version.split('.'))
|
||||||
|
except ValueError:
|
||||||
|
versions = ()
|
||||||
|
if len(versions) == 1:
|
||||||
|
# Default value of zun_api_version is '1'.
|
||||||
|
# If user not specify the value of api version, not passing
|
||||||
|
# headers at all.
|
||||||
|
zun_api_version = None
|
||||||
|
elif len(versions) == 2:
|
||||||
|
zun_api_version = api_version
|
||||||
|
# In the case of '1.0'
|
||||||
|
if versions[1] == 0:
|
||||||
|
zun_api_version = None
|
||||||
|
else:
|
||||||
|
msg = _("The requested API version %(ver)s is an unexpected "
|
||||||
|
"format. Acceptable formats are 'X', 'X.Y', or the "
|
||||||
|
"literal string '%(latest)s'."
|
||||||
|
) % {'ver': api_version, 'latest': 'latest'}
|
||||||
|
raise exc.CommandError(msg)
|
||||||
|
|
||||||
|
api_major_version = versions[0]
|
||||||
|
return (api_major_version, zun_api_version)
|
||||||
|
|
||||||
def main(self, argv):
|
def main(self, argv):
|
||||||
|
|
||||||
# NOTE(Christoph Jansen): With Python 3.4 argv somehow becomes a Map.
|
# NOTE(Christoph Jansen): With Python 3.4 argv somehow becomes a Map.
|
||||||
@ -464,8 +493,12 @@ class OpenStackZunShell(object):
|
|||||||
spot = argv.index('--endpoint_type')
|
spot = argv.index('--endpoint_type')
|
||||||
argv[spot] = '--endpoint-type'
|
argv[spot] = '--endpoint-type'
|
||||||
|
|
||||||
|
# build available subcommands based on version
|
||||||
|
(api_major_version, zun_api_version) = (
|
||||||
|
self._check_version(options.zun_api_version))
|
||||||
|
|
||||||
subcommand_parser = (
|
subcommand_parser = (
|
||||||
self.get_subcommand_parser(options.zun_api_version)
|
self.get_subcommand_parser(api_major_version)
|
||||||
)
|
)
|
||||||
self.parser = subcommand_parser
|
self.parser = subcommand_parser
|
||||||
|
|
||||||
@ -488,12 +521,13 @@ class OpenStackZunShell(object):
|
|||||||
os_user_domain_id, os_user_domain_name,
|
os_user_domain_id, os_user_domain_name,
|
||||||
os_project_domain_id, os_project_domain_name,
|
os_project_domain_id, os_project_domain_name,
|
||||||
os_auth_url, os_auth_system, endpoint_type,
|
os_auth_url, os_auth_system, endpoint_type,
|
||||||
service_type, bypass_url, insecure) = (
|
service_type, bypass_url, insecure, zun_api_version) = (
|
||||||
(args.os_username, args.os_project_name, args.os_project_id,
|
(args.os_username, args.os_project_name, args.os_project_id,
|
||||||
args.os_user_domain_id, args.os_user_domain_name,
|
args.os_user_domain_id, args.os_user_domain_name,
|
||||||
args.os_project_domain_id, args.os_project_domain_name,
|
args.os_project_domain_id, args.os_project_domain_name,
|
||||||
args.os_auth_url, args.os_auth_system, args.endpoint_type,
|
args.os_auth_url, args.os_auth_system, args.endpoint_type,
|
||||||
args.service_type, args.bypass_url, args.insecure)
|
args.service_type, args.bypass_url, args.insecure,
|
||||||
|
args.zun_api_version)
|
||||||
)
|
)
|
||||||
|
|
||||||
if os_auth_system and os_auth_system != "keystone":
|
if os_auth_system and os_auth_system != "keystone":
|
||||||
@ -571,7 +605,7 @@ class OpenStackZunShell(object):
|
|||||||
try:
|
try:
|
||||||
client = {
|
client = {
|
||||||
'1': client_v1,
|
'1': client_v1,
|
||||||
}[options.zun_api_version]
|
}[api_major_version]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
client = client_v1
|
client = client_v1
|
||||||
|
|
||||||
@ -593,6 +627,7 @@ class OpenStackZunShell(object):
|
|||||||
zun_url=bypass_url,
|
zun_url=bypass_url,
|
||||||
endpoint_type=endpoint_type,
|
endpoint_type=endpoint_type,
|
||||||
insecure=insecure,
|
insecure=insecure,
|
||||||
|
api_version=zun_api_version,
|
||||||
**kwargs)
|
**kwargs)
|
||||||
|
|
||||||
args.func(self.cs, args)
|
args.func(self.cs, args)
|
||||||
|
@ -248,7 +248,7 @@ class ShellTest(utils.TestCase):
|
|||||||
service_type='container', region_name=expected_region_name,
|
service_type='container', region_name=expected_region_name,
|
||||||
project_domain_id='', project_domain_name='',
|
project_domain_id='', project_domain_name='',
|
||||||
user_domain_id='', user_domain_name='', profile=None,
|
user_domain_id='', user_domain_name='', profile=None,
|
||||||
zun_url=None, insecure=False)
|
zun_url=None, insecure=False, api_version='latest')
|
||||||
|
|
||||||
def test_main_option_region(self):
|
def test_main_option_region(self):
|
||||||
self.make_env()
|
self.make_env()
|
||||||
@ -275,7 +275,7 @@ class ShellTest(utils.TestCase):
|
|||||||
service_type='container', region_name=None,
|
service_type='container', region_name=None,
|
||||||
project_domain_id='', project_domain_name='',
|
project_domain_id='', project_domain_name='',
|
||||||
user_domain_id='', user_domain_name='', profile=None,
|
user_domain_id='', user_domain_name='', profile=None,
|
||||||
zun_url=None, insecure=False)
|
zun_url=None, insecure=False, api_version='latest')
|
||||||
|
|
||||||
@mock.patch('zunclient.v1.client.Client')
|
@mock.patch('zunclient.v1.client.Client')
|
||||||
def test_main_endpoint_internal(self, mock_client):
|
def test_main_endpoint_internal(self, mock_client):
|
||||||
@ -288,7 +288,7 @@ class ShellTest(utils.TestCase):
|
|||||||
service_type='container', region_name=None,
|
service_type='container', region_name=None,
|
||||||
project_domain_id='', project_domain_name='',
|
project_domain_id='', project_domain_name='',
|
||||||
user_domain_id='', user_domain_name='', profile=None,
|
user_domain_id='', user_domain_name='', profile=None,
|
||||||
zun_url=None, insecure=False)
|
zun_url=None, insecure=False, api_version='latest')
|
||||||
|
|
||||||
|
|
||||||
class ShellTestKeystoneV3(ShellTest):
|
class ShellTestKeystoneV3(ShellTest):
|
||||||
@ -318,4 +318,5 @@ class ShellTestKeystoneV3(ShellTest):
|
|||||||
service_type='container', region_name=None,
|
service_type='container', region_name=None,
|
||||||
project_domain_id='', project_domain_name='Default',
|
project_domain_id='', project_domain_name='Default',
|
||||||
user_domain_id='', user_domain_name='Default',
|
user_domain_id='', user_domain_name='Default',
|
||||||
zun_url=None, insecure=False, profile=None)
|
zun_url=None, insecure=False, profile=None,
|
||||||
|
api_version='latest')
|
||||||
|
@ -31,7 +31,8 @@ class ClientTest(testtools.TestCase):
|
|||||||
region_name=None,
|
region_name=None,
|
||||||
service_name=None,
|
service_name=None,
|
||||||
service_type='container',
|
service_type='container',
|
||||||
session=session)
|
session=session,
|
||||||
|
api_version=None)
|
||||||
|
|
||||||
@mock.patch('zunclient.common.httpclient.SessionClient')
|
@mock.patch('zunclient.common.httpclient.SessionClient')
|
||||||
@mock.patch('keystoneauth1.token_endpoint.Token')
|
@mock.patch('keystoneauth1.token_endpoint.Token')
|
||||||
@ -51,7 +52,8 @@ class ClientTest(testtools.TestCase):
|
|||||||
region_name=None,
|
region_name=None,
|
||||||
service_name=None,
|
service_name=None,
|
||||||
service_type='container',
|
service_type='container',
|
||||||
session=session)
|
session=session,
|
||||||
|
api_version=None)
|
||||||
|
|
||||||
@mock.patch('zunclient.common.httpclient.SessionClient')
|
@mock.patch('zunclient.common.httpclient.SessionClient')
|
||||||
@mock.patch('keystoneauth1.loading.get_plugin_loader')
|
@mock.patch('keystoneauth1.loading.get_plugin_loader')
|
||||||
@ -76,7 +78,8 @@ class ClientTest(testtools.TestCase):
|
|||||||
region_name=None,
|
region_name=None,
|
||||||
service_name=None,
|
service_name=None,
|
||||||
service_type='container',
|
service_type='container',
|
||||||
session=mock.ANY)
|
session=mock.ANY,
|
||||||
|
api_version=None)
|
||||||
|
|
||||||
@mock.patch('zunclient.common.httpclient.SessionClient')
|
@mock.patch('zunclient.common.httpclient.SessionClient')
|
||||||
@mock.patch('keystoneauth1.loading.get_plugin_loader')
|
@mock.patch('keystoneauth1.loading.get_plugin_loader')
|
||||||
@ -102,7 +105,8 @@ class ClientTest(testtools.TestCase):
|
|||||||
region_name=None,
|
region_name=None,
|
||||||
service_name=None,
|
service_name=None,
|
||||||
service_type='container',
|
service_type='container',
|
||||||
session=mock.ANY)
|
session=mock.ANY,
|
||||||
|
api_version=None)
|
||||||
|
|
||||||
@mock.patch('zunclient.common.httpclient.SessionClient')
|
@mock.patch('zunclient.common.httpclient.SessionClient')
|
||||||
@mock.patch('keystoneauth1.loading.get_plugin_loader')
|
@mock.patch('keystoneauth1.loading.get_plugin_loader')
|
||||||
@ -142,7 +146,8 @@ class ClientTest(testtools.TestCase):
|
|||||||
service_name=None,
|
service_name=None,
|
||||||
service_type='container',
|
service_type='container',
|
||||||
session=session,
|
session=session,
|
||||||
endpoint_override='zunurl')
|
endpoint_override='zunurl',
|
||||||
|
api_version=None)
|
||||||
|
|
||||||
@mock.patch('zunclient.common.httpclient.SessionClient')
|
@mock.patch('zunclient.common.httpclient.SessionClient')
|
||||||
@mock.patch('keystoneauth1.session.Session')
|
@mock.patch('keystoneauth1.session.Session')
|
||||||
@ -158,4 +163,5 @@ class ClientTest(testtools.TestCase):
|
|||||||
service_name=None,
|
service_name=None,
|
||||||
service_type='container',
|
service_type='container',
|
||||||
session=session,
|
session=session,
|
||||||
endpoint_override='zunurl')
|
endpoint_override='zunurl',
|
||||||
|
api_version=None)
|
||||||
|
@ -35,7 +35,7 @@ class Client(object):
|
|||||||
interface='public', service_name=None, insecure=False,
|
interface='public', service_name=None, insecure=False,
|
||||||
user_domain_id=None, user_domain_name=None,
|
user_domain_id=None, user_domain_name=None,
|
||||||
project_domain_id=None, project_domain_name=None,
|
project_domain_id=None, project_domain_name=None,
|
||||||
**kwargs):
|
api_version=None, **kwargs):
|
||||||
|
|
||||||
# We have to keep the api_key are for backwards compat, but let's
|
# We have to keep the api_key are for backwards compat, but let's
|
||||||
# remove it from the rest of our code since it's not a keystone
|
# remove it from the rest of our code since it's not a keystone
|
||||||
@ -111,6 +111,7 @@ class Client(object):
|
|||||||
interface=interface,
|
interface=interface,
|
||||||
region_name=region_name,
|
region_name=region_name,
|
||||||
session=session,
|
session=session,
|
||||||
|
api_version=api_version,
|
||||||
**client_kwargs)
|
**client_kwargs)
|
||||||
self.containers = containers.ContainerManager(self.http_client)
|
self.containers = containers.ContainerManager(self.http_client)
|
||||||
self.images = images.ImageManager(self.http_client)
|
self.images = images.ImageManager(self.http_client)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user