diff --git a/glanceclient/common/utils.py b/glanceclient/common/utils.py index 4aec25de..c7728222 100644 --- a/glanceclient/common/utils.py +++ b/glanceclient/common/utils.py @@ -17,6 +17,7 @@ from __future__ import print_function import errno import os +import re import sys import uuid @@ -315,3 +316,15 @@ def get_data_file(args): else: # (3) no image data provided return None + + +def strip_version(endpoint): + """Strip version from the last component of endpoint if present.""" + # Get rid of trailing '/' if present + if endpoint.endswith('/'): + endpoint = endpoint[:-1] + url_bits = endpoint.split('/') + # regex to match 'v1' or 'v2.0' etc + if re.match('v\d+\.?\d*', url_bits[-1]): + endpoint = '/'.join(url_bits[:-1]) + return endpoint diff --git a/glanceclient/shell.py b/glanceclient/shell.py index 8140d35c..321b7e90 100644 --- a/glanceclient/shell.py +++ b/glanceclient/shell.py @@ -24,7 +24,6 @@ import json import logging import os from os.path import expanduser -import re import sys from keystoneclient.v2_0 import client as ksclient @@ -306,21 +305,6 @@ class OpenStackImagesShell(object): subparser.add_argument(*args, **kwargs) subparser.set_defaults(func=callback) - # TODO(dtroyer): move this into the common client support? - # Compatibility check to remove API version as the trailing component - # in a service endpoint; also removes a trailing '/' - def _strip_version(self, endpoint): - """Strip version from the last component of endpoint if present.""" - - # Get rid of trailing '/' if present - if endpoint.endswith('/'): - endpoint = endpoint[:-1] - url_bits = endpoint.split('/') - # regex to match 'v1' or 'v2.0' etc - if re.match('v\d+\.?\d*', url_bits[-1]): - endpoint = '/'.join(url_bits[:-1]) - return endpoint - def _get_ksclient(self, **kwargs): """Get an endpoint and auth token from Keystone. @@ -349,8 +333,7 @@ class OpenStackImagesShell(object): endpoint_kwargs['attr'] = 'region' endpoint_kwargs['filter_value'] = kwargs.get('region_name') - endpoint = client.service_catalog.url_for(**endpoint_kwargs) - return self._strip_version(endpoint) + return client.service_catalog.url_for(**endpoint_kwargs) def _get_image_url(self, args): """Translate the available url-related options into a single string. diff --git a/glanceclient/v1/client.py b/glanceclient/v1/client.py index 2b6a888d..23bb7377 100644 --- a/glanceclient/v1/client.py +++ b/glanceclient/v1/client.py @@ -14,6 +14,7 @@ # under the License. from glanceclient.common import http +from glanceclient.common import utils from glanceclient.v1 import image_members from glanceclient.v1 import images @@ -28,8 +29,9 @@ class Client(object): http requests. (optional) """ - def __init__(self, *args, **kwargs): + def __init__(self, endpoint, *args, **kwargs): """Initialize a new client for the Images v1 API.""" - self.http_client = http.HTTPClient(*args, **kwargs) + self.http_client = http.HTTPClient(utils.strip_version(endpoint), + *args, **kwargs) self.images = images.ImageManager(self.http_client) self.image_members = image_members.ImageMemberManager(self.http_client) diff --git a/glanceclient/v2/client.py b/glanceclient/v2/client.py index baf0bc4a..b3060964 100644 --- a/glanceclient/v2/client.py +++ b/glanceclient/v2/client.py @@ -16,6 +16,7 @@ import warlock from glanceclient.common import http +from glanceclient.common import utils from glanceclient.v2 import image_members from glanceclient.v2 import image_tags from glanceclient.v2 import images @@ -32,8 +33,9 @@ class Client(object): http requests. (optional) """ - def __init__(self, *args, **kwargs): - self.http_client = http.HTTPClient(*args, **kwargs) + def __init__(self, endpoint, *args, **kwargs): + self.http_client = http.HTTPClient(utils.strip_version(endpoint), + *args, **kwargs) self.schemas = schemas.Controller(self.http_client) image_model = self._get_image_model() self.images = images.Controller(self.http_client, diff --git a/tests/v1/test_client.py b/tests/v1/test_client.py new file mode 100644 index 00000000..c31c6b29 --- /dev/null +++ b/tests/v1/test_client.py @@ -0,0 +1,36 @@ +# Copyright 2013 OpenStack LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import testtools + +from glanceclient.v1 import client + + +class ClientTest(testtools.TestCase): + + def setUp(self): + super(ClientTest, self).setUp() + + def test_endpoint(self): + gc = client.Client("http://example.com") + self.assertEqual(gc.http_client.endpoint, "http://example.com") + + def test_versioned_endpoint(self): + gc = client.Client("http://example.com/v1") + self.assertEqual(gc.http_client.endpoint, "http://example.com") + + def test_versioned_endpoint_with_minor_revision(self): + gc = client.Client("http://example.com/v1.1") + self.assertEqual(gc.http_client.endpoint, "http://example.com") diff --git a/tests/v2/test_client.py b/tests/v2/test_client.py new file mode 100644 index 00000000..94752e3e --- /dev/null +++ b/tests/v2/test_client.py @@ -0,0 +1,44 @@ +# Copyright 2013 OpenStack LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from mox3 import mox +import testtools + +from glanceclient.v2 import client + + +class ClientTest(testtools.TestCase): + + def setUp(self): + super(ClientTest, self).setUp() + self.mock = mox.Mox() + self.mock.StubOutWithMock(client.Client, '_get_image_model') + self.mock.StubOutWithMock(client.Client, '_get_member_model') + + def tearDown(self): + super(ClientTest, self).tearDown() + self.mock.UnsetStubs() + + def test_endpoint(self): + gc = client.Client("http://example.com") + self.assertEqual(gc.http_client.endpoint, "http://example.com") + + def test_versioned_endpoint(self): + gc = client.Client("http://example.com/v2") + self.assertEqual(gc.http_client.endpoint, "http://example.com") + + def test_versioned_endpoint_with_minor_revision(self): + gc = client.Client("http://example.com/v2.1") + self.assertEqual(gc.http_client.endpoint, "http://example.com")