summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Burke <tim.burke@gmail.com>2019-10-09 16:59:23 -0700
committerTim Burke <tim.burke@gmail.com>2019-11-06 08:47:24 +0000
commitc4bef14fc1975f2e115a6ec8560e674e8aa5b1bf (patch)
tree222191341ec8c908f9a0989dc8e9d79bbfac16c8
parent1eda8f9f3eac55953b3d54e32754e2ec312ab348 (diff)
downloadpython-swiftclient-c4bef14fc1975f2e115a6ec8560e674e8aa5b1bf.tar.gz
v1auth: support endpoint_data_for() api
...so we can be used with openstacksdk. Also, add a few functests that use openstacksdk. Change-Id: Ie6987f5de48914ec8932254cde79a973a0264877
-rw-r--r--lower-constraints.txt1
-rw-r--r--swiftclient/authv1.py12
-rw-r--r--test-requirements.txt1
-rw-r--r--test/functional/__init__.py93
-rw-r--r--test/functional/test_openstacksdk.py92
-rw-r--r--test/functional/test_swiftclient.py85
6 files changed, 221 insertions, 63 deletions
diff --git a/lower-constraints.txt b/lower-constraints.txt
index 88d2865..ead0279 100644
--- a/lower-constraints.txt
+++ b/lower-constraints.txt
@@ -21,6 +21,7 @@ mccabe==0.2.1
mock==1.2.0
netaddr==0.7.10
openstackdocstheme==1.20.0
+openstacksdk==0.11.0
oslo.config==1.2.0
pbr==2.0.0
pep8==1.5.7
diff --git a/swiftclient/authv1.py b/swiftclient/authv1.py
index 55469ac..d70acac 100644
--- a/swiftclient/authv1.py
+++ b/swiftclient/authv1.py
@@ -45,6 +45,7 @@ from six.moves.urllib.parse import urljoin
# Note that while we import keystoneauth1 here, we *don't* need to add it to
# requirements.txt -- this entire module only makes sense (and should only be
# loaded) if keystoneauth is already installed.
+from keystoneauth1 import discover
from keystoneauth1 import plugin
from keystoneauth1 import exceptions
from keystoneauth1 import loading
@@ -110,11 +111,20 @@ class ServiceCatalogV1(object):
]
def url_for(self, **kwargs):
+ return self.endpoint_data_for(**kwargs).url
+
+ def endpoint_data_for(self, **kwargs):
kwargs.setdefault('interface', 'public')
kwargs.setdefault('service_type', None)
if kwargs['service_type'] == 'object-store':
- return self.storage_url
+ return discover.EndpointData(
+ service_type='object-store',
+ service_name='swift',
+ interface=kwargs['interface'],
+ region_name='default',
+ catalog_url=self.storage_url,
+ )
# Although our "catalog" includes an identity entry, nothing that uses
# url_for() (including `openstack endpoint list`) will know what to do
diff --git a/test-requirements.txt b/test-requirements.txt
index b3ca5f8..1373253 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -4,3 +4,4 @@ coverage!=4.4,>=4.0 # Apache-2.0
keystoneauth1>=3.4.0 # Apache-2.0
mock>=1.2.0 # BSD
stestr>=2.0.0 # Apache-2.0
+openstacksdk>=0.11.0 # Apache-2.0
diff --git a/test/functional/__init__.py b/test/functional/__init__.py
index e69de29..248875a 100644
--- a/test/functional/__init__.py
+++ b/test/functional/__init__.py
@@ -0,0 +1,93 @@
+# Copyright (c) 2014 Christian Schwede <christian.schwede@enovance.com>
+#
+# 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 os
+from six.moves import configparser
+
+TEST_CONFIG = None
+
+
+def _load_config(force_reload=False):
+ global TEST_CONFIG
+ if not force_reload and TEST_CONFIG is not None:
+ return TEST_CONFIG
+
+ config_file = os.environ.get('SWIFT_TEST_CONFIG_FILE',
+ '/etc/swift/test.conf')
+ parser = configparser.ConfigParser({'auth_version': '1'})
+ parser.read(config_file)
+ conf = {}
+ if parser.has_section('func_test'):
+ if parser.has_option('func_test', 'auth_uri'):
+ conf['auth_url'] = parser.get('func_test', 'auth_uri')
+ try:
+ conf['auth_version'] = parser.get('func_test', 'auth_version')
+ except configparser.NoOptionError:
+ last_piece = conf['auth_url'].rstrip('/').rsplit('/', 1)[1]
+ if last_piece.endswith('.0'):
+ last_piece = last_piece[:-2]
+ if last_piece in ('1', '2', '3'):
+ conf['auth_version'] = last_piece
+ else:
+ raise
+ else:
+ auth_host = parser.get('func_test', 'auth_host')
+ auth_port = parser.getint('func_test', 'auth_port')
+ auth_ssl = parser.getboolean('func_test', 'auth_ssl')
+ auth_prefix = parser.get('func_test', 'auth_prefix')
+ conf['auth_version'] = parser.get('func_test', 'auth_version')
+ if auth_ssl:
+ auth_url = "https://"
+ else:
+ auth_url = "http://"
+ auth_url += "%s:%s%s" % (auth_host, auth_port, auth_prefix)
+ if conf['auth_version'] == "1":
+ auth_url += 'v1.0'
+ conf['auth_url'] = auth_url
+
+ try:
+ conf['account_username'] = parser.get('func_test',
+ 'account_username')
+ except configparser.NoOptionError:
+ conf['account'] = parser.get('func_test', 'account')
+ conf['username'] = parser.get('func_test', 'username')
+ conf['account_username'] = "%s:%s" % (conf['account'],
+ conf['username'])
+ else:
+ # Still try to get separate account/usernames for keystone tests
+ try:
+ conf['account'] = parser.get('func_test', 'account')
+ conf['username'] = parser.get('func_test', 'username')
+ except configparser.NoOptionError:
+ pass
+
+ conf['password'] = parser.get('func_test', 'password')
+
+ # For keystone v3
+ try:
+ conf['account4'] = parser.get('func_test', 'account4')
+ conf['username4'] = parser.get('func_test', 'username4')
+ conf['domain4'] = parser.get('func_test', 'domain4')
+ conf['password4'] = parser.get('func_test', 'password4')
+ except configparser.NoOptionError:
+ pass
+
+ TEST_CONFIG = conf
+
+
+try:
+ _load_config()
+except configparser.NoOptionError:
+ TEST_CONFIG = None # sentinel used in test setup
diff --git a/test/functional/test_openstacksdk.py b/test/functional/test_openstacksdk.py
new file mode 100644
index 0000000..cee7f4e
--- /dev/null
+++ b/test/functional/test_openstacksdk.py
@@ -0,0 +1,92 @@
+# Copyright (c) 2019 Tim Burke <tim@swiftstack.com>
+#
+# 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 unittest
+import uuid
+
+import openstack
+
+from . import TEST_CONFIG
+
+PREFIX = 'test-swiftclient-'
+
+
+class TestOpenStackSDK(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ # NB: Only runs for v1 auth, to exercise our keystoneauth plugin
+ cls.skip_tests = (TEST_CONFIG is None or
+ TEST_CONFIG['auth_version'] != '1')
+ if not cls.skip_tests:
+ cls.conn = openstack.connect(
+ auth_type='v1password',
+ auth_url=TEST_CONFIG['auth_url'],
+ username=TEST_CONFIG['account_username'],
+ password=TEST_CONFIG['password'],
+ )
+ cls.object_store = cls.conn.object_store
+
+ def setUp(self):
+ if self.skip_tests:
+ raise unittest.SkipTest('SKIPPING V1-AUTH TESTS')
+
+ def tearDown(self):
+ if self.skip_tests:
+ return
+ for c in self.object_store.containers():
+ if c.name.startswith(PREFIX):
+ for o in self.object_store.objects(c.name):
+ self.object_store.delete_object(
+ o.name, container=c.name)
+ self.object_store.delete_container(c.name)
+
+ def test_containers(self):
+ meta = self.object_store.get_account_metadata()
+ count_before = meta.account_container_count
+ containers = sorted(PREFIX + str(uuid.uuid4())
+ for _ in range(10))
+ for c in containers:
+ self.object_store.create_container(c)
+ self.assertEqual([
+ c.name for c in self.object_store.containers()
+ if c.name.startswith(PREFIX)
+ ], containers)
+ meta = self.object_store.get_account_metadata()
+ self.assertEqual(count_before + len(containers),
+ meta.account_container_count)
+
+ def test_objects(self):
+ container = PREFIX + str(uuid.uuid4())
+ self.object_store.create_container(container)
+ objects = sorted(str(uuid.uuid4()) for _ in range(10))
+ for o in objects:
+ self.object_store.create_object(container, o, data=b'x')
+ self.assertEqual([
+ o.name for o in self.object_store.objects(container)
+ ], objects)
+ meta = self.object_store.get_container_metadata(container)
+ self.assertEqual(len(objects), meta.object_count)
+
+ def test_object_metadata(self):
+ container = PREFIX + str(uuid.uuid4())
+ self.object_store.create_container(container)
+ obj = str(uuid.uuid4())
+ obj_meta = {str(uuid.uuid4()): str(uuid.uuid4()) for _ in range(10)}
+ # NB: as of 0.36.0, create_object() doesn't play well with passing
+ # both data and metadata, so we do a PUT then POST
+ self.object_store.create_object(container, obj, data=b'x')
+ self.object_store.set_object_metadata(obj, container, **obj_meta)
+ meta = self.object_store.get_object_metadata(obj, container)
+ self.assertEqual(obj_meta, meta.metadata)
diff --git a/test/functional/test_swiftclient.py b/test/functional/test_swiftclient.py
index 9a74c63..54c514d 100644
--- a/test/functional/test_swiftclient.py
+++ b/test/functional/test_swiftclient.py
@@ -13,23 +13,23 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import os
import unittest
import time
from io import BytesIO
import six
-from six.moves import configparser
import swiftclient
+from . import TEST_CONFIG
class TestFunctional(unittest.TestCase):
def __init__(self, *args, **kwargs):
super(TestFunctional, self).__init__(*args, **kwargs)
- self.skip_tests = False
- self._get_config()
+ self.skip_tests = (TEST_CONFIG is None)
+ if not self.skip_tests:
+ self._get_config()
self.test_data = b'42' * 10
self.etag = '2704306ec982238d85d4b235c925d58e'
@@ -41,50 +41,10 @@ class TestFunctional(unittest.TestCase):
self.objectname_2 = self.objectname + '_second'
def _get_config(self):
- config_file = os.environ.get('SWIFT_TEST_CONFIG_FILE',
- '/etc/swift/test.conf')
- config = configparser.ConfigParser({'auth_version': '1'})
- config.read(config_file)
- self.config = config
- if config.has_section('func_test'):
- if config.has_option('func_test', 'auth_uri'):
- self.auth_url = config.get('func_test', 'auth_uri')
- try:
- self.auth_version = config.get('func_test', 'auth_version')
- except configparser.NoOptionError:
- last_piece = self.auth_url.rstrip('/').rsplit('/', 1)[1]
- if last_piece.endswith('.0'):
- last_piece = last_piece[:-2]
- if last_piece in ('1', '2', '3'):
- self.auth_version = last_piece
- else:
- raise
- else:
- auth_host = config.get('func_test', 'auth_host')
- auth_port = config.getint('func_test', 'auth_port')
- auth_ssl = config.getboolean('func_test', 'auth_ssl')
- auth_prefix = config.get('func_test', 'auth_prefix')
- self.auth_version = config.get('func_test', 'auth_version')
- self.auth_url = ""
- if auth_ssl:
- self.auth_url += "https://"
- else:
- self.auth_url += "http://"
- self.auth_url += "%s:%s%s" % (
- auth_host, auth_port, auth_prefix)
- if self.auth_version == "1":
- self.auth_url += 'v1.0'
-
- try:
- self.account_username = config.get('func_test',
- 'account_username')
- except configparser.NoOptionError:
- account = config.get('func_test', 'account')
- username = config.get('func_test', 'username')
- self.account_username = "%s:%s" % (account, username)
- self.password = config.get('func_test', 'password')
- else:
- self.skip_tests = True
+ self.auth_url = TEST_CONFIG['auth_url']
+ self.auth_version = TEST_CONFIG['auth_version']
+ self.account_username = TEST_CONFIG['account_username']
+ self.password = TEST_CONFIG['password']
def _get_connection(self):
"""
@@ -514,20 +474,20 @@ class TestUsingKeystone(TestFunctional):
"""
def _get_connection(self):
- account = username = password = None
+ account = username = None
if self.auth_version not in ('2', '3'):
self.skipTest('SKIPPING KEYSTONE-SPECIFIC FUNCTIONAL TESTS')
try:
- account = self.config.get('func_test', 'account')
- username = self.config.get('func_test', 'username')
- password = self.config.get('func_test', 'password')
- except Exception:
+ account = TEST_CONFIG['account']
+ username = TEST_CONFIG['username']
+ except KeyError:
self.skipTest('SKIPPING KEYSTONE-SPECIFIC FUNCTIONAL TESTS' +
' - NO CONFIG')
- os_options = {'tenant_name': account}
+
return swiftclient.Connection(
- self.auth_url, username, password, auth_version=self.auth_version,
- os_options=os_options)
+ self.auth_url, username, self.password,
+ auth_version=self.auth_version,
+ os_options={'tenant_name': account})
class TestUsingKeystoneV3(TestFunctional):
@@ -539,13 +499,14 @@ class TestUsingKeystoneV3(TestFunctional):
account = username = password = project_domain = user_domain = None
if self.auth_version != '3':
self.skipTest('SKIPPING KEYSTONE-V3-SPECIFIC FUNCTIONAL TESTS')
+
try:
- account = self.config.get('func_test', 'account4')
- username = self.config.get('func_test', 'username4')
- user_domain = self.config.get('func_test', 'domain4')
- project_domain = self.config.get('func_test', 'domain4')
- password = self.config.get('func_test', 'password4')
- except Exception:
+ account = TEST_CONFIG['account4']
+ username = TEST_CONFIG['username4']
+ user_domain = TEST_CONFIG['domain4']
+ project_domain = TEST_CONFIG['domain4']
+ password = TEST_CONFIG['password4']
+ except KeyError:
self.skipTest('SKIPPING KEYSTONE-V3-SPECIFIC FUNCTIONAL TESTS' +
' - NO CONFIG')