diff --git a/.zuul.yaml b/.zuul.yaml
index ed9c310267..eacc728d58 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -93,6 +93,18 @@
parent: swift-tox-func-ec
nodeset: centos-7
+- job:
+ name: swift-tox-func-domain-remap-staticweb
+ parent: swift-tox-base
+ description: |
+ Run functional tests for swift under cPython version 2.7.
+
+ Uses tox with the ``func-domain-remap-staticweb`` environment.
+ It sets TMPDIR to an XFS mount point created via
+ tools/test-setup.sh.
+ vars:
+ tox_envlist: func-domain-remap-staticweb
+
- job:
name: swift-probetests-centos-7
parent: unittests
@@ -113,6 +125,7 @@
- swift-tox-py35
- swift-tox-func
- swift-tox-func-encryption
+ - swift-tox-func-domain-remap-staticweb
- swift-tox-func-ec
- swift-probetests-centos-7
gate:
@@ -121,6 +134,7 @@
- swift-tox-py35
- swift-tox-func
- swift-tox-func-encryption
+ - swift-tox-func-domain-remap-staticweb
- swift-tox-func-ec
experimental:
jobs:
diff --git a/test/functional/__init__.py b/test/functional/__init__.py
index 30018d04d7..32f3c4a8d9 100644
--- a/test/functional/__init__.py
+++ b/test/functional/__init__.py
@@ -369,6 +369,49 @@ def _load_ec_as_default_policy(proxy_conf_file, swift_conf_file, **kwargs):
return proxy_conf_file, swift_conf_file
+def _load_domain_remap_staticweb(proxy_conf_file, swift_conf_file, **kwargs):
+ """
+ Load domain_remap and staticweb into proxy server pipeline.
+
+ :param proxy_conf_file: Source proxy conf filename
+ :param swift_conf_file: Source swift conf filename
+ :returns: Tuple of paths to the proxy conf file and swift conf file to use
+ :raises InProcessException: raised if proxy conf contents are invalid
+ """
+ _debug('Setting configuration for domain_remap')
+
+ # The global conf dict cannot be used to modify the pipeline.
+ # The pipeline loader requires the pipeline to be set in the local_conf.
+ # If pipeline is set in the global conf dict (which in turn populates the
+ # DEFAULTS options) then it prevents pipeline being loaded into the local
+ # conf during wsgi load_app.
+ # Therefore we must modify the [pipeline:main] section.
+
+ conf = ConfigParser()
+ conf.read(proxy_conf_file)
+ try:
+ section = 'pipeline:main'
+ old_pipeline = conf.get(section, 'pipeline')
+ pipeline = old_pipeline.replace(
+ "tempauth",
+ "domain_remap tempauth staticweb")
+ if pipeline == old_pipeline:
+ raise InProcessException(
+ "Failed to insert domain_remap and staticweb into pipeline: %s"
+ % old_pipeline)
+ conf.set(section, 'pipeline', pipeline)
+ except NoSectionError as err:
+ msg = 'Error problem with proxy conf file %s: %s' % \
+ (proxy_conf_file, err)
+ raise InProcessException(msg)
+
+ test_conf_file = os.path.join(_testdir, 'proxy-server.conf')
+ with open(test_conf_file, 'w') as fp:
+ conf.write(fp)
+
+ return test_conf_file, swift_conf_file
+
+
# Mapping from possible values of the variable
# SWIFT_TEST_IN_PROCESS_CONF_LOADER
# to the method to call for loading the associated configuration
@@ -376,7 +419,8 @@ def _load_ec_as_default_policy(proxy_conf_file, swift_conf_file, **kwargs):
# conf_filename_to_use loader(input_conf_filename, **kwargs)
conf_loaders = {
'encryption': _load_encryption,
- 'ec': _load_ec_as_default_policy
+ 'ec': _load_ec_as_default_policy,
+ 'domain_remap_staticweb': _load_domain_remap_staticweb,
}
diff --git a/test/functional/test_staticweb.py b/test/functional/test_staticweb.py
new file mode 100644
index 0000000000..9851aa1cff
--- /dev/null
+++ b/test/functional/test_staticweb.py
@@ -0,0 +1,400 @@
+#!/usr/bin/python -u
+# Copyright (c) 2010-2017 OpenStack Foundation
+#
+# 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 functools
+from unittest2 import SkipTest
+import test.functional as tf
+from test.functional import cluster_info
+from test.functional.tests import Utils, Base, BaseEnv
+from test.functional.swift_test_client import Account, Connection, \
+ ResponseError
+
+
+def setUpModule():
+ tf.setup_package()
+
+
+def tearDownModule():
+ tf.teardown_package()
+
+
+def requires_domain_remap(func):
+ @functools.wraps(func)
+ def wrapper(*args, **kwargs):
+ if 'domain_remap' not in cluster_info:
+ raise SkipTest('Domain Remap is not enabled')
+ return func(*args, **kwargs)
+ return wrapper
+
+
+class TestStaticWebEnv(BaseEnv):
+ static_web_enabled = None # tri-state: None initially, then True/False
+
+ @classmethod
+ def setUp(cls):
+ cls.conn = Connection(tf.config)
+ cls.conn.authenticate()
+
+ if cls.static_web_enabled is None:
+ cls.static_web_enabled = 'staticweb' in cluster_info
+ if not cls.static_web_enabled:
+ return
+
+ cls.account = Account(
+ cls.conn, tf.config.get('account', tf.config['username']))
+ cls.account.delete_containers()
+
+ cls.container = cls.account.container(Utils.create_name())
+ if not cls.container.create(
+ hdrs={'X-Container-Read': '.r:*,.rlistings'}):
+ raise ResponseError(cls.conn.response)
+
+ objects = ['index',
+ 'error',
+ 'listings_css',
+ 'dir/',
+ 'dir/obj',
+ 'dir/subdir/',
+ 'dir/subdir/obj']
+
+ cls.objects = {}
+ for item in sorted(objects):
+ parent = None
+ if '/' in item.rstrip('/'):
+ parent, _ = item.rstrip('/').rsplit('/', 1)
+ path = '%s/%s' % (cls.objects[parent + '/'].name,
+ Utils.create_name())
+ else:
+ path = Utils.create_name()
+
+ if item[-1] == '/':
+ cls.objects[item] = cls.container.file(path)
+ cls.objects[item].write(hdrs={
+ 'Content-Type': 'application/directory'})
+ else:
+ cls.objects[item] = cls.container.file(path)
+ cls.objects[item].write('%s contents' % item)
+
+
+class TestStaticWeb(Base):
+ env = TestStaticWebEnv
+ set_up = False
+
+ def setUp(self):
+ super(TestStaticWeb, self).setUp()
+ if self.env.static_web_enabled is False:
+ raise SkipTest("Static Web not enabled")
+ elif self.env.static_web_enabled is not True:
+ # just some sanity checking
+ raise Exception(
+ "Expected static_web_enabled to be True/False, got %r" %
+ (self.env.static_web_enabled,))
+
+ _, _, acct = self.env.account.conn.storage_url.split('/')
+
+ self.domain_remap_acct = '%s.example.com' % acct
+
+ self.domain_remap_cont = '%s.%s.example.com' % (
+ self.env.container.name, acct)
+
+ def _set_staticweb_headers(self, index=False, listings=False,
+ listings_css=False, error=False):
+ objects = self.env.objects
+ headers = {}
+ if index:
+ headers['X-Container-Meta-Web-Index'] = objects['index'].name
+ else:
+ headers['X-Remove-Container-Meta-Web-Index'] = 'true'
+
+ if listings:
+ headers['X-Container-Meta-Web-Listings'] = 'true'
+ else:
+ headers['X-Remove-Container-Meta-Web-Listings'] = 'true'
+
+ if listings_css:
+ headers['X-Container-Meta-Web-Listings-Css'] = \
+ objects['listings_css'].name
+ else:
+ headers['X-Remove-Container-Meta-Web-Listings-Css'] = 'true'
+
+ if error:
+ headers['X-Container-Meta-Web-Error'] = objects['error'].name
+ else:
+ headers['X-Remove-Container-Meta-Web-Error'] = 'true'
+
+ self.assertTrue(self.env.container.update_metadata(hdrs=headers))
+
+ def _test_redirect_with_slash(self, host, path, anonymous=False):
+ self._set_staticweb_headers(listings=True)
+ self.env.account.conn.make_request('GET', path,
+ hdrs={'X-Web-Mode': not anonymous,
+ 'Host': host},
+ cfg={'no_auth_token': anonymous,
+ 'absolute_path': True})
+
+ self.assert_status(301)
+ self.assertRegexpMatches(self.env.conn.response.getheader('location'),
+ 'http[s]?://%s%s/' % (host, path))
+
+ def _test_redirect_slash_direct(self, anonymous):
+ host = self.env.account.conn.storage_netloc
+ path = '%s/%s' % (self.env.account.conn.storage_url,
+ self.env.container.name)
+ self._test_redirect_with_slash(host, path, anonymous=anonymous)
+
+ path = '%s/%s/%s' % (self.env.account.conn.storage_url,
+ self.env.container.name,
+ self.env.objects['dir/'].name)
+ self._test_redirect_with_slash(host, path, anonymous=anonymous)
+
+ def test_redirect_slash_auth_direct(self):
+ self._test_redirect_slash_direct(False)
+
+ def test_redirect_slash_anon_direct(self):
+ self._test_redirect_slash_direct(True)
+
+ @requires_domain_remap
+ def _test_redirect_slash_remap_acct(self, anonymous):
+ host = self.domain_remap_acct
+ path = '/%s' % self.env.container.name
+ self._test_redirect_with_slash(host, path, anonymous=anonymous)
+
+ path = '/%s/%s' % (self.env.container.name,
+ self.env.objects['dir/'].name)
+ self._test_redirect_with_slash(host, path, anonymous=anonymous)
+
+ def test_redirect_slash_auth_remap_acct(self):
+ self._test_redirect_slash_remap_acct(False)
+
+ def test_redirect_slash_anon_remap_acct(self):
+ self._test_redirect_slash_remap_acct(True)
+
+ @requires_domain_remap
+ def _test_redirect_slash_remap_cont(self, anonymous):
+ host = self.domain_remap_cont
+ path = '/%s' % self.env.objects['dir/'].name
+ self._test_redirect_with_slash(host, path, anonymous=anonymous)
+
+ def test_redirect_slash_auth_remap_cont(self):
+ self._test_redirect_slash_remap_cont(False)
+
+ def test_redirect_slash_anon_remap_cont(self):
+ self._test_redirect_slash_remap_cont(True)
+
+ def _test_get_path(self, host, path, anonymous=False, expected_status=200,
+ expected_in=[], expected_not_in=[]):
+ self.env.account.conn.make_request('GET', path,
+ hdrs={'X-Web-Mode': not anonymous,
+ 'Host': host},
+ cfg={'no_auth_token': anonymous,
+ 'absolute_path': True})
+ self.assert_status(expected_status)
+ body = self.env.account.conn.response.read()
+ for string in expected_in:
+ self.assertIn(string, body)
+ for string in expected_not_in:
+ self.assertNotIn(string, body)
+
+ def _test_listing(self, host, path, title=None, links=[], notins=[],
+ css=None, anonymous=False):
+ self._set_staticweb_headers(listings=True,
+ listings_css=(css is not None))
+ if title is None:
+ title = path
+ expected_in = ['Listing of %s' % title] + [
+ '{0}'.format(link) for link in links]
+ expected_not_in = notins
+ if css:
+ expected_in.append('' % css)
+ self._test_get_path(host, path, anonymous=anonymous,
+ expected_in=expected_in,
+ expected_not_in=expected_not_in)
+
+ def _test_listing_direct(self, anonymous, listings_css):
+ objects = self.env.objects
+ host = self.env.account.conn.storage_netloc
+ path = '%s/%s/' % (self.env.account.conn.storage_url,
+ self.env.container.name)
+ css = objects['listings_css'].name if listings_css else None
+ self._test_listing(host, path, anonymous=True, css=css,
+ links=[objects['index'].name,
+ objects['dir/'].name + '/'],
+ notins=[objects['dir/obj'].name])
+
+ path = '%s/%s/%s/' % (self.env.account.conn.storage_url,
+ self.env.container.name,
+ objects['dir/'].name)
+ css = '../%s' % objects['listings_css'].name if listings_css else None
+ self._test_listing(host, path, anonymous=anonymous, css=css,
+ links=[objects['dir/obj'].name.split('/')[-1],
+ objects['dir/subdir/'].name.split('/')[-1]
+ + '/'],
+ notins=[objects['index'].name,
+ objects['dir/subdir/obj'].name])
+
+ def test_listing_auth_direct_without_css(self):
+ self._test_listing_direct(False, False)
+
+ def test_listing_anon_direct_without_css(self):
+ self._test_listing_direct(True, False)
+
+ def test_listing_auth_direct_with_css(self):
+ self._test_listing_direct(False, True)
+
+ def test_listing_anon_direct_with_css(self):
+ self._test_listing_direct(True, True)
+
+ @requires_domain_remap
+ def _test_listing_remap_acct(self, anonymous, listings_css):
+ objects = self.env.objects
+ host = self.domain_remap_acct
+ path = '/%s/' % self.env.container.name
+ css = objects['listings_css'].name if listings_css else None
+ title = '%s/%s/' % (self.env.account.conn.storage_url,
+ self.env.container.name)
+ self._test_listing(host, path, title=title, anonymous=anonymous,
+ css=css,
+ links=[objects['index'].name,
+ objects['dir/'].name + '/'],
+ notins=[objects['dir/obj'].name])
+
+ path = '/%s/%s/' % (self.env.container.name, objects['dir/'].name)
+ css = '../%s' % objects['listings_css'].name if listings_css else None
+ title = '%s/%s/%s/' % (self.env.account.conn.storage_url,
+ self.env.container.name,
+ objects['dir/'])
+ self._test_listing(host, path, title=title, anonymous=anonymous,
+ css=css,
+ links=[objects['dir/obj'].name.split('/')[-1],
+ objects['dir/subdir/'].name.split('/')[-1]
+ + '/'],
+ notins=[objects['index'].name,
+ objects['dir/subdir/obj'].name])
+
+ def test_listing_auth_remap_acct_without_css(self):
+ self._test_listing_remap_acct(False, False)
+
+ def test_listing_anon_remap_acct_without_css(self):
+ self._test_listing_remap_acct(True, False)
+
+ def test_listing_auth_remap_acct_with_css(self):
+ self._test_listing_remap_acct(False, True)
+
+ def test_listing_anon_remap_acct_with_css(self):
+ self._test_listing_remap_acct(True, True)
+
+ @requires_domain_remap
+ def _test_listing_remap_cont(self, anonymous, listings_css):
+ objects = self.env.objects
+ host = self.domain_remap_cont
+ path = '/'
+ css = objects['listings_css'].name if listings_css else None
+ title = '%s/%s/' % (self.env.account.conn.storage_url,
+ self.env.container.name)
+ self._test_listing(host, path, title=title, anonymous=anonymous,
+ css=css,
+ links=[objects['index'].name,
+ objects['dir/'].name + '/'],
+ notins=[objects['dir/obj'].name])
+
+ path = '/%s/' % objects['dir/'].name
+ css = '../%s' % objects['listings_css'].name if listings_css else None
+ title = '%s/%s/%s/' % (self.env.account.conn.storage_url,
+ self.env.container.name,
+ objects['dir/'])
+ self._test_listing(host, path, title=title, anonymous=anonymous,
+ css=css,
+ links=[objects['dir/obj'].name.split('/')[-1],
+ objects['dir/subdir/'].name.split('/')[-1]
+ + '/'],
+ notins=[objects['index'].name,
+ objects['dir/subdir/obj'].name])
+
+ def test_listing_auth_remap_cont_without_css(self):
+ self._test_listing_remap_cont(False, False)
+
+ def test_listing_anon_remap_cont_without_css(self):
+ self._test_listing_remap_cont(True, False)
+
+ def test_listing_auth_remap_cont_with_css(self):
+ self._test_listing_remap_cont(False, True)
+
+ def test_listing_anon_remap_cont_with_css(self):
+ self._test_listing_remap_cont(True, True)
+
+ def _test_index(self, host, path, anonymous=False, expected_status=200):
+ self._set_staticweb_headers(index=True)
+ if expected_status == 200:
+ expected_in = ['index contents']
+ expected_not_in = ['Listing']
+ else:
+ expected_in = []
+ expected_not_in = []
+ self._test_get_path(host, path, anonymous=anonymous,
+ expected_status=expected_status,
+ expected_in=expected_in,
+ expected_not_in=expected_not_in)
+
+ def _test_index_direct(self, anonymous):
+ objects = self.env.objects
+ host = self.env.account.conn.storage_netloc
+ path = '%s/%s/' % (self.env.account.conn.storage_url,
+ self.env.container.name)
+ self._test_index(host, path, anonymous=anonymous)
+
+ path = '%s/%s/%s/' % (self.env.account.conn.storage_url,
+ self.env.container.name,
+ objects['dir/'].name)
+ self._test_index(host, path, anonymous=anonymous, expected_status=404)
+
+ def test_index_auth_direct(self):
+ self._test_index_direct(False)
+
+ def test_index_anon_direct(self):
+ self._test_index_direct(True)
+
+ @requires_domain_remap
+ def _test_index_remap_acct(self, anonymous):
+ objects = self.env.objects
+ host = self.domain_remap_acct
+ path = '/%s/' % self.env.container.name
+ self._test_index(host, path, anonymous=anonymous)
+
+ path = '/%s/%s/' % (self.env.container.name, objects['dir/'].name)
+ self._test_index(host, path, anonymous=anonymous, expected_status=404)
+
+ def test_index_auth_remap_acct(self):
+ self._test_index_remap_acct(False)
+
+ def test_index_anon_remap_acct(self):
+ self._test_index_remap_acct(True)
+
+ @requires_domain_remap
+ def _test_index_remap_cont(self, anonymous):
+ objects = self.env.objects
+ host = self.domain_remap_cont
+ path = '/'
+ self._test_index(host, path, anonymous=anonymous)
+
+ path = '/%s/' % objects['dir/'].name
+ self._test_index(host, path, anonymous=anonymous, expected_status=404)
+
+ def test_index_auth_remap_cont(self):
+ self._test_index_remap_cont(False)
+
+ def test_index_anon_remap_cont(self):
+ self._test_index_remap_cont(True)
diff --git a/tox.ini b/tox.ini
index 9da8180366..dfa4e34966 100644
--- a/tox.ini
+++ b/tox.ini
@@ -76,6 +76,11 @@ commands = ./.functests {posargs}
setenv = SWIFT_TEST_IN_PROCESS=1
SWIFT_TEST_IN_PROCESS_CONF_LOADER=encryption
+[testenv:func-domain-remap-staticweb]
+commands = ./.functests {posargs}
+setenv = SWIFT_TEST_IN_PROCESS=1
+ SWIFT_TEST_IN_PROCESS_CONF_LOADER=domain_remap_staticweb
+
[testenv:func-ec]
commands = ./.functests {posargs}
setenv = SWIFT_TEST_IN_PROCESS=1