v1auth: support endpoint_data_for() api

...so we can be used with openstacksdk.

Also, add a few functests that use openstacksdk.

Change-Id: Ie6987f5de48914ec8932254cde79a973a0264877
This commit is contained in:
Tim Burke
2019-10-09 16:59:23 -07:00
parent 1eda8f9f3e
commit c4bef14fc1
6 changed files with 221 additions and 63 deletions

View File

@@ -21,6 +21,7 @@ mccabe==0.2.1
mock==1.2.0 mock==1.2.0
netaddr==0.7.10 netaddr==0.7.10
openstackdocstheme==1.20.0 openstackdocstheme==1.20.0
openstacksdk==0.11.0
oslo.config==1.2.0 oslo.config==1.2.0
pbr==2.0.0 pbr==2.0.0
pep8==1.5.7 pep8==1.5.7

View File

@@ -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 # 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 # requirements.txt -- this entire module only makes sense (and should only be
# loaded) if keystoneauth is already installed. # loaded) if keystoneauth is already installed.
from keystoneauth1 import discover
from keystoneauth1 import plugin from keystoneauth1 import plugin
from keystoneauth1 import exceptions from keystoneauth1 import exceptions
from keystoneauth1 import loading from keystoneauth1 import loading
@@ -110,11 +111,20 @@ class ServiceCatalogV1(object):
] ]
def url_for(self, **kwargs): def url_for(self, **kwargs):
return self.endpoint_data_for(**kwargs).url
def endpoint_data_for(self, **kwargs):
kwargs.setdefault('interface', 'public') kwargs.setdefault('interface', 'public')
kwargs.setdefault('service_type', None) kwargs.setdefault('service_type', None)
if kwargs['service_type'] == 'object-store': 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 # Although our "catalog" includes an identity entry, nothing that uses
# url_for() (including `openstack endpoint list`) will know what to do # url_for() (including `openstack endpoint list`) will know what to do

View File

@@ -4,3 +4,4 @@ coverage!=4.4,>=4.0 # Apache-2.0
keystoneauth1>=3.4.0 # Apache-2.0 keystoneauth1>=3.4.0 # Apache-2.0
mock>=1.2.0 # BSD mock>=1.2.0 # BSD
stestr>=2.0.0 # Apache-2.0 stestr>=2.0.0 # Apache-2.0
openstacksdk>=0.11.0 # Apache-2.0

View File

@@ -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

View File

@@ -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)

View File

@@ -13,23 +13,23 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import os
import unittest import unittest
import time import time
from io import BytesIO from io import BytesIO
import six import six
from six.moves import configparser
import swiftclient import swiftclient
from . import TEST_CONFIG
class TestFunctional(unittest.TestCase): class TestFunctional(unittest.TestCase):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(TestFunctional, self).__init__(*args, **kwargs) super(TestFunctional, self).__init__(*args, **kwargs)
self.skip_tests = False self.skip_tests = (TEST_CONFIG is None)
self._get_config() if not self.skip_tests:
self._get_config()
self.test_data = b'42' * 10 self.test_data = b'42' * 10
self.etag = '2704306ec982238d85d4b235c925d58e' self.etag = '2704306ec982238d85d4b235c925d58e'
@@ -41,50 +41,10 @@ class TestFunctional(unittest.TestCase):
self.objectname_2 = self.objectname + '_second' self.objectname_2 = self.objectname + '_second'
def _get_config(self): def _get_config(self):
config_file = os.environ.get('SWIFT_TEST_CONFIG_FILE', self.auth_url = TEST_CONFIG['auth_url']
'/etc/swift/test.conf') self.auth_version = TEST_CONFIG['auth_version']
config = configparser.ConfigParser({'auth_version': '1'}) self.account_username = TEST_CONFIG['account_username']
config.read(config_file) self.password = TEST_CONFIG['password']
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
def _get_connection(self): def _get_connection(self):
""" """
@@ -514,20 +474,20 @@ class TestUsingKeystone(TestFunctional):
""" """
def _get_connection(self): def _get_connection(self):
account = username = password = None account = username = None
if self.auth_version not in ('2', '3'): if self.auth_version not in ('2', '3'):
self.skipTest('SKIPPING KEYSTONE-SPECIFIC FUNCTIONAL TESTS') self.skipTest('SKIPPING KEYSTONE-SPECIFIC FUNCTIONAL TESTS')
try: try:
account = self.config.get('func_test', 'account') account = TEST_CONFIG['account']
username = self.config.get('func_test', 'username') username = TEST_CONFIG['username']
password = self.config.get('func_test', 'password') except KeyError:
except Exception:
self.skipTest('SKIPPING KEYSTONE-SPECIFIC FUNCTIONAL TESTS' + self.skipTest('SKIPPING KEYSTONE-SPECIFIC FUNCTIONAL TESTS' +
' - NO CONFIG') ' - NO CONFIG')
os_options = {'tenant_name': account}
return swiftclient.Connection( return swiftclient.Connection(
self.auth_url, username, password, auth_version=self.auth_version, self.auth_url, username, self.password,
os_options=os_options) auth_version=self.auth_version,
os_options={'tenant_name': account})
class TestUsingKeystoneV3(TestFunctional): class TestUsingKeystoneV3(TestFunctional):
@@ -539,13 +499,14 @@ class TestUsingKeystoneV3(TestFunctional):
account = username = password = project_domain = user_domain = None account = username = password = project_domain = user_domain = None
if self.auth_version != '3': if self.auth_version != '3':
self.skipTest('SKIPPING KEYSTONE-V3-SPECIFIC FUNCTIONAL TESTS') self.skipTest('SKIPPING KEYSTONE-V3-SPECIFIC FUNCTIONAL TESTS')
try: try:
account = self.config.get('func_test', 'account4') account = TEST_CONFIG['account4']
username = self.config.get('func_test', 'username4') username = TEST_CONFIG['username4']
user_domain = self.config.get('func_test', 'domain4') user_domain = TEST_CONFIG['domain4']
project_domain = self.config.get('func_test', 'domain4') project_domain = TEST_CONFIG['domain4']
password = self.config.get('func_test', 'password4') password = TEST_CONFIG['password4']
except Exception: except KeyError:
self.skipTest('SKIPPING KEYSTONE-V3-SPECIFIC FUNCTIONAL TESTS' + self.skipTest('SKIPPING KEYSTONE-V3-SPECIFIC FUNCTIONAL TESTS' +
' - NO CONFIG') ' - NO CONFIG')