Skip to content

Commit

Permalink
v1auth: support endpoint_data_for() api
Browse files Browse the repository at this point in the history
...so we can be used with openstacksdk.

Also, add a few functests that use openstacksdk.

Change-Id: Ie6987f5de48914ec8932254cde79a973a0264877
  • Loading branch information
tipabu committed Nov 6, 2019
1 parent 1eda8f9 commit c4bef14
Show file tree
Hide file tree
Showing 6 changed files with 221 additions and 63 deletions.
1 change: 1 addition & 0 deletions lower-constraints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 11 additions & 1 deletion swiftclient/authv1.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
# 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
Expand Down Expand Up @@ -110,11 +111,20 @@ def catalog(self):
]

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
Expand Down
1 change: 1 addition & 0 deletions test-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
93 changes: 93 additions & 0 deletions test/functional/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Copyright (c) 2014 Christian Schwede <[email protected]>
#
# 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
92 changes: 92 additions & 0 deletions test/functional/test_openstacksdk.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Copyright (c) 2019 Tim Burke <[email protected]>
#
# 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)
85 changes: 23 additions & 62 deletions test/functional/test_swiftclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -41,50 +41,10 @@ def __init__(self, *args, **kwargs):
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):
"""
Expand Down Expand Up @@ -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):
Expand All @@ -539,13 +499,14 @@ def _get_connection(self):
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')

Expand Down

0 comments on commit c4bef14

Please sign in to comment.