Merge "Allow glance tests to run on Windows"
This commit is contained in:
commit
d501799a6a
@ -32,7 +32,6 @@ CONF = cfg.CONF
|
||||
_registry_client = 'glance.registry.client'
|
||||
CONF.import_opt('registry_client_protocol', _registry_client)
|
||||
CONF.import_opt('registry_client_key_file', _registry_client)
|
||||
CONF.import_opt('registry_client_cert_file', _registry_client)
|
||||
CONF.import_opt('registry_client_ca_file', _registry_client)
|
||||
CONF.import_opt('registry_client_insecure', _registry_client)
|
||||
CONF.import_opt('registry_client_timeout', _registry_client)
|
||||
|
@ -13,6 +13,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import os
|
||||
|
||||
import eventlet
|
||||
# NOTE(jokke): As per the eventlet commit
|
||||
# b756447bab51046dfc6f1e0e299cc997ab343701 there's circular import happening
|
||||
@ -20,7 +22,13 @@ import eventlet
|
||||
# before calling monkey_patch(). This is solved in eventlet 0.22.0 but we
|
||||
# need to address it before that is widely used around.
|
||||
eventlet.hubs.get_hub()
|
||||
eventlet.patcher.monkey_patch()
|
||||
|
||||
if os.name == 'nt':
|
||||
# eventlet monkey patching the os module causes subprocess.Popen to fail
|
||||
# on Windows when using pipes due to missing non-blocking IO support.
|
||||
eventlet.patcher.monkey_patch(os=False)
|
||||
else:
|
||||
eventlet.patcher.monkey_patch()
|
||||
|
||||
# See http://code.google.com/p/python-nose/issues/detail?id=373
|
||||
# The code below enables tests to work with i18n _() blocks
|
||||
|
@ -21,6 +21,7 @@ and Registry server, grabbing the logs of each, cleaning up pidfiles,
|
||||
and spinning down the servers.
|
||||
"""
|
||||
|
||||
import abc
|
||||
import atexit
|
||||
import datetime
|
||||
import errno
|
||||
@ -28,12 +29,16 @@ import os
|
||||
import platform
|
||||
import shutil
|
||||
import signal
|
||||
import six
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
import fixtures
|
||||
from os_win import utilsfactory as os_win_utilsfactory
|
||||
from oslo_config import cfg
|
||||
from oslo_serialization import jsonutils
|
||||
# NOTE(jokke): simplified transition to py3, behaves like py2 xrange
|
||||
from six.moves import range
|
||||
@ -48,8 +53,18 @@ from glance.tests import utils as test_utils
|
||||
execute, get_unused_port = test_utils.execute, test_utils.get_unused_port
|
||||
tracecmd_osmap = {'Linux': 'strace', 'FreeBSD': 'truss'}
|
||||
|
||||
if os.name == 'nt':
|
||||
SQLITE_CONN_TEMPLATE = 'sqlite:///%s/tests.sqlite'
|
||||
else:
|
||||
SQLITE_CONN_TEMPLATE = 'sqlite:////%s/tests.sqlite'
|
||||
|
||||
class Server(object):
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.import_opt('registry_host', 'glance.registry')
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class BaseServer(object):
|
||||
"""
|
||||
Class used to easily manage starting and stopping
|
||||
a server during functional test runs.
|
||||
@ -131,6 +146,78 @@ class Server(object):
|
||||
|
||||
return self.conf_file_name, overridden
|
||||
|
||||
@abc.abstractmethod
|
||||
def start(self, expect_exit=True, expected_exitcode=0, **kwargs):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def stop(self):
|
||||
pass
|
||||
|
||||
def reload(self, expect_exit=True, expected_exitcode=0, **kwargs):
|
||||
"""
|
||||
Start and stop the service to reload
|
||||
|
||||
Any kwargs passed to this method will override the configuration
|
||||
value in the conf file used in starting the servers.
|
||||
"""
|
||||
self.stop()
|
||||
return self.start(expect_exit=expect_exit,
|
||||
expected_exitcode=expected_exitcode, **kwargs)
|
||||
|
||||
def create_database(self):
|
||||
"""Create database if required for this server"""
|
||||
if self.needs_database:
|
||||
conf_dir = os.path.join(self.test_dir, 'etc')
|
||||
utils.safe_mkdirs(conf_dir)
|
||||
conf_filepath = os.path.join(conf_dir, 'glance-manage.conf')
|
||||
|
||||
with open(conf_filepath, 'w') as conf_file:
|
||||
conf_file.write('[DEFAULT]\n')
|
||||
conf_file.write('sql_connection = %s' % self.sql_connection)
|
||||
conf_file.flush()
|
||||
|
||||
glance_db_env = 'GLANCE_DB_TEST_SQLITE_FILE'
|
||||
if glance_db_env in os.environ:
|
||||
# use the empty db created and cached as a tempfile
|
||||
# instead of spending the time creating a new one
|
||||
db_location = os.environ[glance_db_env]
|
||||
shutil.copyfile(db_location, "%s/tests.sqlite" % self.test_dir)
|
||||
else:
|
||||
cmd = ('%s -m glance.cmd.manage --config-file %s db sync' %
|
||||
(sys.executable, conf_filepath))
|
||||
execute(cmd, no_venv=self.no_venv, exec_env=self.exec_env,
|
||||
expect_exit=True)
|
||||
|
||||
# copy the clean db to a temp location so that it
|
||||
# can be reused for future tests
|
||||
(osf, db_location) = tempfile.mkstemp()
|
||||
os.close(osf)
|
||||
shutil.copyfile('%s/tests.sqlite' % self.test_dir, db_location)
|
||||
os.environ[glance_db_env] = db_location
|
||||
|
||||
# cleanup the temp file when the test suite is
|
||||
# complete
|
||||
def _delete_cached_db():
|
||||
try:
|
||||
os.remove(os.environ[glance_db_env])
|
||||
except Exception:
|
||||
glance_tests.logger.exception(
|
||||
"Error cleaning up the file %s" %
|
||||
os.environ[glance_db_env])
|
||||
atexit.register(_delete_cached_db)
|
||||
|
||||
def dump_log(self):
|
||||
if not self.log_file:
|
||||
return "log_file not set for {name}".format(name=self.server_name)
|
||||
elif not os.path.exists(self.log_file):
|
||||
return "{log_file} for {name} did not exist".format(
|
||||
log_file=self.log_file, name=self.server_name)
|
||||
with open(self.log_file, 'r') as fptr:
|
||||
return fptr.read().strip()
|
||||
|
||||
|
||||
class PosixServer(BaseServer):
|
||||
def start(self, expect_exit=True, expected_exitcode=0, **kwargs):
|
||||
"""
|
||||
Starts the server.
|
||||
@ -190,61 +277,6 @@ class Server(object):
|
||||
self.sock = None
|
||||
return (rc, '', '')
|
||||
|
||||
def reload(self, expect_exit=True, expected_exitcode=0, **kwargs):
|
||||
"""
|
||||
Start and stop the service to reload
|
||||
|
||||
Any kwargs passed to this method will override the configuration
|
||||
value in the conf file used in starting the servers.
|
||||
"""
|
||||
self.stop()
|
||||
return self.start(expect_exit=expect_exit,
|
||||
expected_exitcode=expected_exitcode, **kwargs)
|
||||
|
||||
def create_database(self):
|
||||
"""Create database if required for this server"""
|
||||
if self.needs_database:
|
||||
conf_dir = os.path.join(self.test_dir, 'etc')
|
||||
utils.safe_mkdirs(conf_dir)
|
||||
conf_filepath = os.path.join(conf_dir, 'glance-manage.conf')
|
||||
|
||||
with open(conf_filepath, 'w') as conf_file:
|
||||
conf_file.write('[DEFAULT]\n')
|
||||
conf_file.write('sql_connection = %s' % self.sql_connection)
|
||||
conf_file.flush()
|
||||
|
||||
glance_db_env = 'GLANCE_DB_TEST_SQLITE_FILE'
|
||||
if glance_db_env in os.environ:
|
||||
# use the empty db created and cached as a tempfile
|
||||
# instead of spending the time creating a new one
|
||||
db_location = os.environ[glance_db_env]
|
||||
os.system('cp %s %s/tests.sqlite'
|
||||
% (db_location, self.test_dir))
|
||||
else:
|
||||
cmd = ('%s -m glance.cmd.manage --config-file %s db sync' %
|
||||
(sys.executable, conf_filepath))
|
||||
execute(cmd, no_venv=self.no_venv, exec_env=self.exec_env,
|
||||
expect_exit=True)
|
||||
|
||||
# copy the clean db to a temp location so that it
|
||||
# can be reused for future tests
|
||||
(osf, db_location) = tempfile.mkstemp()
|
||||
os.close(osf)
|
||||
os.system('cp %s/tests.sqlite %s'
|
||||
% (self.test_dir, db_location))
|
||||
os.environ[glance_db_env] = db_location
|
||||
|
||||
# cleanup the temp file when the test suite is
|
||||
# complete
|
||||
def _delete_cached_db():
|
||||
try:
|
||||
os.remove(os.environ[glance_db_env])
|
||||
except Exception:
|
||||
glance_tests.logger.exception(
|
||||
"Error cleaning up the file %s" %
|
||||
os.environ[glance_db_env])
|
||||
atexit.register(_delete_cached_db)
|
||||
|
||||
def stop(self):
|
||||
"""
|
||||
Spin down the server.
|
||||
@ -257,14 +289,80 @@ class Server(object):
|
||||
rc = test_utils.wait_for_fork(self.process_pid, raise_error=False)
|
||||
return (rc, '', '')
|
||||
|
||||
def dump_log(self):
|
||||
if not self.log_file:
|
||||
return "log_file not set for {name}".format(name=self.server_name)
|
||||
elif not os.path.exists(self.log_file):
|
||||
return "{log_file} for {name} did not exist".format(
|
||||
log_file=self.log_file, name=self.server_name)
|
||||
with open(self.log_file, 'r') as fptr:
|
||||
return fptr.read().strip()
|
||||
|
||||
class Win32Server(BaseServer):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Win32Server, self).__init__(*args, **kwargs)
|
||||
|
||||
self._processutils = os_win_utilsfactory.get_processutils()
|
||||
|
||||
def start(self, expect_exit=True, expected_exitcode=0, **kwargs):
|
||||
"""
|
||||
Starts the server.
|
||||
|
||||
Any kwargs passed to this method will override the configuration
|
||||
value in the conf file used in starting the servers.
|
||||
"""
|
||||
|
||||
# Ensure the configuration file is written
|
||||
self.write_conf(**kwargs)
|
||||
|
||||
self.create_database()
|
||||
|
||||
cmd = ("%(server_module)s --config-file %(conf_file_name)s"
|
||||
% {"server_module": self.server_module,
|
||||
"conf_file_name": self.conf_file_name})
|
||||
cmd = "%s -m %s" % (sys.executable, cmd)
|
||||
|
||||
# Passing socket objects on Windows is a bit more cumbersome.
|
||||
# We don't really have to do it.
|
||||
if self.sock:
|
||||
self.sock.close()
|
||||
self.sock = None
|
||||
|
||||
self.process = subprocess.Popen(
|
||||
cmd,
|
||||
env=self.exec_env)
|
||||
self.process_pid = self.process.pid
|
||||
|
||||
try:
|
||||
self.job_handle = self._processutils.kill_process_on_job_close(
|
||||
self.process_pid)
|
||||
except Exception:
|
||||
# Could not associate child process with a job, killing it.
|
||||
self.process.kill()
|
||||
raise
|
||||
|
||||
self.stop_kill = not expect_exit
|
||||
if self.pid_file:
|
||||
pf = open(self.pid_file, 'w')
|
||||
pf.write('%d\n' % self.process_pid)
|
||||
pf.close()
|
||||
|
||||
rc = 0
|
||||
if expect_exit:
|
||||
self.process.communicate()
|
||||
rc = self.process.returncode
|
||||
|
||||
return (rc, '', '')
|
||||
|
||||
def stop(self):
|
||||
"""
|
||||
Spin down the server.
|
||||
"""
|
||||
if not self.process_pid:
|
||||
raise Exception('Server "%s" process not running.'
|
||||
% self.server_name)
|
||||
|
||||
if self.stop_kill:
|
||||
self.process.terminate()
|
||||
return (0, '', '')
|
||||
|
||||
|
||||
if os.name == 'nt':
|
||||
Server = Win32Server
|
||||
else:
|
||||
Server = PosixServer
|
||||
|
||||
|
||||
class ApiServer(Server):
|
||||
@ -305,7 +403,7 @@ class ApiServer(Server):
|
||||
self.disable_path = None
|
||||
|
||||
self.needs_database = True
|
||||
default_sql_connection = 'sqlite:////%s/tests.sqlite' % self.test_dir
|
||||
default_sql_connection = SQLITE_CONN_TEMPLATE % self.test_dir
|
||||
self.sql_connection = os.environ.get('GLANCE_TEST_SQL_CONNECTION',
|
||||
default_sql_connection)
|
||||
self.data_api = kwargs.get("data_api",
|
||||
@ -488,7 +586,7 @@ class ApiServerForMultipleBackend(Server):
|
||||
self.disable_path = None
|
||||
|
||||
self.needs_database = True
|
||||
default_sql_connection = 'sqlite:////%s/tests.sqlite' % self.test_dir
|
||||
default_sql_connection = SQLITE_CONN_TEMPLATE % self.test_dir
|
||||
self.sql_connection = os.environ.get('GLANCE_TEST_SQL_CONNECTION',
|
||||
default_sql_connection)
|
||||
self.data_api = kwargs.get("data_api",
|
||||
@ -646,7 +744,7 @@ class RegistryServer(Server):
|
||||
self.server_module = 'glance.cmd.%s' % self.server_name
|
||||
|
||||
self.needs_database = True
|
||||
default_sql_connection = 'sqlite:////%s/tests.sqlite' % self.test_dir
|
||||
default_sql_connection = SQLITE_CONN_TEMPLATE % self.test_dir
|
||||
self.sql_connection = os.environ.get('GLANCE_TEST_SQL_CONNECTION',
|
||||
default_sql_connection)
|
||||
|
||||
@ -732,7 +830,7 @@ class ScrubberDaemon(Server):
|
||||
self.metadata_encryption_key = "012345678901234567890123456789ab"
|
||||
self.lock_path = self.test_dir
|
||||
|
||||
default_sql_connection = 'sqlite:////%s/tests.sqlite' % self.test_dir
|
||||
default_sql_connection = SQLITE_CONN_TEMPLATE % self.test_dir
|
||||
self.sql_connection = os.environ.get('GLANCE_TEST_SQL_CONNECTION',
|
||||
default_sql_connection)
|
||||
self.policy_file = policy_file
|
||||
@ -790,6 +888,11 @@ class FunctionalTest(test_utils.BaseTestCase):
|
||||
# False in the test SetUps that do not require Scrubber to run.
|
||||
self.include_scrubber = True
|
||||
|
||||
# The clients will try to connect to this address. Let's make sure
|
||||
# we're not using the default '0.0.0.0'
|
||||
self.config(bind_host='127.0.0.1',
|
||||
registry_host='127.0.0.1')
|
||||
|
||||
self.tracecmd = tracecmd_osmap.get(platform.system())
|
||||
|
||||
conf_dir = os.path.join(self.test_dir, 'etc')
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
import copy
|
||||
import datetime
|
||||
import time
|
||||
import uuid
|
||||
|
||||
import mock
|
||||
@ -1340,6 +1341,10 @@ class DriverTests(object):
|
||||
'deleted': False}
|
||||
self.assertEqual(expected, member)
|
||||
|
||||
# The clock may not be very accurate, for which reason we may
|
||||
# get identical timestamps.
|
||||
time.sleep(0.01)
|
||||
|
||||
member = self.db_api.image_member_update(self.context,
|
||||
member_id,
|
||||
{'status': 'accepted'})
|
||||
|
@ -346,6 +346,8 @@ class TestScrubber(functional.FunctionalTest):
|
||||
def test_scrubber_restore_image_with_daemon_running(self):
|
||||
self.cleanup()
|
||||
self.scrubber_daemon.start(daemon=True)
|
||||
# Give the scrubber some time to start.
|
||||
time.sleep(5)
|
||||
|
||||
exe_cmd = "%s -m glance.cmd.scrubber" % sys.executable
|
||||
cmd = ("%s --restore fake_image_id" % exe_cmd)
|
||||
|
@ -45,7 +45,7 @@ class BaseCacheMiddlewareTest(object):
|
||||
self.start_servers(**self.__dict__.copy())
|
||||
|
||||
# Add an image and verify success
|
||||
path = "http://%s:%d/v2/images" % ("0.0.0.0", self.api_port)
|
||||
path = "http://%s:%d/v2/images" % ("127.0.0.1", self.api_port)
|
||||
http = httplib2.Http()
|
||||
headers = {'content-type': 'application/json'}
|
||||
image_entity = {
|
||||
@ -61,7 +61,7 @@ class BaseCacheMiddlewareTest(object):
|
||||
data = jsonutils.loads(content)
|
||||
image_id = data['id']
|
||||
|
||||
path = "http://%s:%d/v2/images/%s/file" % ("0.0.0.0", self.api_port,
|
||||
path = "http://%s:%d/v2/images/%s/file" % ("127.0.0.1", self.api_port,
|
||||
image_id)
|
||||
headers = {'content-type': 'application/octet-stream'}
|
||||
image_data = "*" * FIVE_KB
|
||||
@ -87,7 +87,7 @@ class BaseCacheMiddlewareTest(object):
|
||||
|
||||
# Now, we delete the image from the server and verify that
|
||||
# the image cache no longer contains the deleted image
|
||||
path = "http://%s:%d/v2/images/%s" % ("0.0.0.0", self.api_port,
|
||||
path = "http://%s:%d/v2/images/%s" % ("127.0.0.1", self.api_port,
|
||||
image_id)
|
||||
http = httplib2.Http()
|
||||
response, content = http.request(path, 'DELETE')
|
||||
@ -107,7 +107,7 @@ class BaseCacheMiddlewareTest(object):
|
||||
self.start_servers(**self.__dict__.copy())
|
||||
|
||||
# Add an image and verify success
|
||||
path = "http://%s:%d/v2/images" % ("0.0.0.0", self.api_port)
|
||||
path = "http://%s:%d/v2/images" % ("127.0.0.1", self.api_port)
|
||||
http = httplib2.Http()
|
||||
headers = {'content-type': 'application/json'}
|
||||
image_entity = {
|
||||
@ -123,7 +123,7 @@ class BaseCacheMiddlewareTest(object):
|
||||
data = jsonutils.loads(content)
|
||||
image_id = data['id']
|
||||
|
||||
path = "http://%s:%d/v2/images/%s/file" % ("0.0.0.0", self.api_port,
|
||||
path = "http://%s:%d/v2/images/%s/file" % ("127.0.0.1", self.api_port,
|
||||
image_id)
|
||||
headers = {'content-type': 'application/octet-stream'}
|
||||
image_data = b'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
@ -187,7 +187,7 @@ class BaseCacheMiddlewareTest(object):
|
||||
self.start_servers(**self.__dict__.copy())
|
||||
|
||||
# Add an image and verify success
|
||||
path = "http://%s:%d/v2/images" % ("0.0.0.0", self.api_port)
|
||||
path = "http://%s:%d/v2/images" % ("127.0.0.1", self.api_port)
|
||||
http = httplib2.Http()
|
||||
headers = {'content-type': 'application/json'}
|
||||
image_entity = {
|
||||
@ -203,7 +203,7 @@ class BaseCacheMiddlewareTest(object):
|
||||
data = jsonutils.loads(content)
|
||||
image_id = data['id']
|
||||
|
||||
path = "http://%s:%d/v2/images/%s/file" % ("0.0.0.0", self.api_port,
|
||||
path = "http://%s:%d/v2/images/%s/file" % ("127.0.0.1", self.api_port,
|
||||
image_id)
|
||||
headers = {'content-type': 'application/octet-stream'}
|
||||
image_data = b'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
@ -283,7 +283,7 @@ class BaseCacheMiddlewareTest(object):
|
||||
self.start_servers(**self.__dict__.copy())
|
||||
|
||||
# Add an image and verify success
|
||||
path = "http://%s:%d/v2/images" % ("0.0.0.0", self.api_port)
|
||||
path = "http://%s:%d/v2/images" % ("127.0.0.1", self.api_port)
|
||||
http = httplib2.Http()
|
||||
headers = {'content-type': 'application/json'}
|
||||
image_entity = {
|
||||
@ -299,7 +299,7 @@ class BaseCacheMiddlewareTest(object):
|
||||
data = jsonutils.loads(content)
|
||||
image_id = data['id']
|
||||
|
||||
path = "http://%s:%d/v2/images/%s/file" % ("0.0.0.0", self.api_port,
|
||||
path = "http://%s:%d/v2/images/%s/file" % ("127.0.0.1", self.api_port,
|
||||
image_id)
|
||||
headers = {'content-type': 'application/octet-stream'}
|
||||
image_data = "*" * FIVE_KB
|
||||
@ -324,7 +324,7 @@ class BaseCacheMiddlewareTest(object):
|
||||
|
||||
# Now, we delete the image from the server and verify that
|
||||
# the image cache no longer contains the deleted image
|
||||
path = "http://%s:%d/v2/images/%s" % ("0.0.0.0", self.api_port,
|
||||
path = "http://%s:%d/v2/images/%s" % ("127.0.0.1", self.api_port,
|
||||
image_id)
|
||||
http = httplib2.Http()
|
||||
response, content = http.request(path, 'DELETE')
|
||||
@ -347,7 +347,7 @@ class TestImageCacheXattr(functional.FunctionalTest,
|
||||
filesystem)
|
||||
"""
|
||||
if getattr(self, 'disabled', False):
|
||||
return
|
||||
raise self.skipException('Test disabled.')
|
||||
|
||||
if not getattr(self, 'inited', False):
|
||||
try:
|
||||
@ -356,7 +356,7 @@ class TestImageCacheXattr(functional.FunctionalTest,
|
||||
self.inited = True
|
||||
self.disabled = True
|
||||
self.disabled_message = ("python-xattr not installed.")
|
||||
return
|
||||
raise self.skipException(self.disabled_message)
|
||||
|
||||
self.inited = True
|
||||
self.disabled = False
|
||||
@ -370,7 +370,7 @@ class TestImageCacheXattr(functional.FunctionalTest,
|
||||
self.inited = True
|
||||
self.disabled = True
|
||||
self.disabled_message = ("filesystem does not support xattr")
|
||||
return
|
||||
raise self.skipException(self.disabled_message)
|
||||
|
||||
def tearDown(self):
|
||||
super(TestImageCacheXattr, self).tearDown()
|
||||
|
@ -28,7 +28,6 @@ from glance.common import wsgi
|
||||
from glance.tests import functional
|
||||
from glance.tests import utils
|
||||
|
||||
|
||||
eventlet.patcher.monkey_patch(socket=True)
|
||||
|
||||
|
||||
|
@ -85,6 +85,12 @@ class TestLogging(functional.FunctionalTest):
|
||||
"""
|
||||
Test that we notice when our log file has been rotated
|
||||
"""
|
||||
|
||||
# Moving in-use files is not supported on Windows.
|
||||
# The log handler itself may be configured to rotate files.
|
||||
if os.name == 'nt':
|
||||
raise self.skipException("Unsupported platform.")
|
||||
|
||||
self.cleanup()
|
||||
self.start_servers()
|
||||
|
||||
|
@ -32,7 +32,7 @@ class TestSqlite(functional.FunctionalTest):
|
||||
self.cleanup()
|
||||
self.start_servers(**self.__dict__.copy())
|
||||
|
||||
cmd = "sqlite3 tests.sqlite '.schema'"
|
||||
cmd = 'sqlite3 tests.sqlite ".schema"'
|
||||
exitcode, out, err = execute(cmd, raise_error=True)
|
||||
|
||||
self.assertNotIn('BIGINT', out)
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
"""Tests for `glance.wsgi`."""
|
||||
|
||||
import os
|
||||
import socket
|
||||
import time
|
||||
|
||||
@ -52,4 +53,9 @@ class TestWSGIServer(testtools.TestCase):
|
||||
# Should succeed - no timeout
|
||||
self.assertIn(greetings, get_request())
|
||||
# Should fail - connection timed out so we get nothing from the server
|
||||
self.assertFalse(get_request(delay=1.1))
|
||||
if os.name == 'nt':
|
||||
self.assertRaises(ConnectionAbortedError,
|
||||
get_request,
|
||||
delay=1.1)
|
||||
else:
|
||||
self.assertFalse(get_request(delay=1.1))
|
||||
|
@ -14,8 +14,6 @@
|
||||
# under the License.
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
import signal
|
||||
import uuid
|
||||
|
||||
from oslo_serialization import jsonutils
|
||||
@ -48,16 +46,17 @@ class TestImages(functional.FunctionalTest):
|
||||
for i in range(3):
|
||||
ret = test_utils.start_http_server("foo_image_id%d" % i,
|
||||
"foo_image%d" % i)
|
||||
setattr(self, 'http_server%d_pid' % i, ret[0])
|
||||
setattr(self, 'http_port%d' % i, ret[1])
|
||||
setattr(self, 'http_server%d' % i, ret[1])
|
||||
setattr(self, 'http_port%d' % i, ret[2])
|
||||
self.api_server.use_user_token = True
|
||||
self.api_server.send_identity_credentials = True
|
||||
|
||||
def tearDown(self):
|
||||
for i in range(3):
|
||||
pid = getattr(self, 'http_server%d_pid' % i, None)
|
||||
if pid:
|
||||
os.kill(pid, signal.SIGKILL)
|
||||
httpd = getattr(self, 'http_server%d' % i, None)
|
||||
if httpd:
|
||||
httpd.shutdown()
|
||||
httpd.server_close()
|
||||
|
||||
super(TestImages, self).tearDown()
|
||||
|
||||
@ -219,7 +218,7 @@ class TestImages(functional.FunctionalTest):
|
||||
func_utils.wait_for_status(request_path=path,
|
||||
request_headers=self._headers(),
|
||||
status='active',
|
||||
max_sec=2,
|
||||
max_sec=10,
|
||||
delay_sec=0.2)
|
||||
expect_c = six.text_type(hashlib.md5(image_data).hexdigest())
|
||||
expect_h = six.text_type(hashlib.sha512(image_data).hexdigest())
|
||||
@ -343,7 +342,7 @@ class TestImages(functional.FunctionalTest):
|
||||
})
|
||||
|
||||
# Start http server locally
|
||||
pid, port = test_utils.start_standalone_http_server()
|
||||
thread, httpd, port = test_utils.start_standalone_http_server()
|
||||
|
||||
image_data_uri = 'http://localhost:%s/' % port
|
||||
data = jsonutils.dumps({'method': {
|
||||
@ -373,7 +372,8 @@ class TestImages(functional.FunctionalTest):
|
||||
status='active')
|
||||
|
||||
# kill the local http server
|
||||
os.kill(pid, signal.SIGKILL)
|
||||
httpd.shutdown()
|
||||
httpd.server_close()
|
||||
|
||||
# Deleting image should work
|
||||
path = self._url('/v2/images/%s' % image_id)
|
||||
@ -1609,8 +1609,8 @@ class TestImages(functional.FunctionalTest):
|
||||
path = self._url('/v2/images/%s' % image_id)
|
||||
media_type = 'application/openstack-images-v2.1-json-patch'
|
||||
headers = self._headers({'content-type': media_type})
|
||||
http_server_pid, http_port = test_utils.start_http_server(image_id,
|
||||
"image-1")
|
||||
thread, httpd, http_port = test_utils.start_http_server(image_id,
|
||||
"image-1")
|
||||
values = [{'url': 'http://127.0.0.1:%s/image-1' % http_port,
|
||||
'metadata': {'idx': '0'}}]
|
||||
doc = [{'op': 'replace',
|
||||
@ -1627,7 +1627,8 @@ class TestImages(functional.FunctionalTest):
|
||||
self.assertEqual(http.OK, response.status_code)
|
||||
|
||||
# Stop http server used to update image location
|
||||
os.kill(http_server_pid, signal.SIGKILL)
|
||||
httpd.shutdown()
|
||||
httpd.server_close()
|
||||
|
||||
# Download an image should raise HTTPServiceUnavailable
|
||||
path = self._url('/v2/images/%s/file' % image_id)
|
||||
@ -3895,14 +3896,15 @@ class TestImageLocationSelectionStrategy(functional.FunctionalTest):
|
||||
for i in range(3):
|
||||
ret = test_utils.start_http_server("foo_image_id%d" % i,
|
||||
"foo_image%d" % i)
|
||||
setattr(self, 'http_server%d_pid' % i, ret[0])
|
||||
setattr(self, 'http_port%d' % i, ret[1])
|
||||
setattr(self, 'http_server%d' % i, ret[1])
|
||||
setattr(self, 'http_port%d' % i, ret[2])
|
||||
|
||||
def tearDown(self):
|
||||
for i in range(3):
|
||||
pid = getattr(self, 'http_server%d_pid' % i, None)
|
||||
if pid:
|
||||
os.kill(pid, signal.SIGKILL)
|
||||
httpd = getattr(self, 'http_server%d' % i, None)
|
||||
if httpd:
|
||||
httpd.shutdown()
|
||||
httpd.server_close()
|
||||
|
||||
super(TestImageLocationSelectionStrategy, self).tearDown()
|
||||
|
||||
@ -4453,14 +4455,15 @@ class TestImagesMultipleBackend(functional.MultipleBackendFunctionalTest):
|
||||
for i in range(3):
|
||||
ret = test_utils.start_http_server("foo_image_id%d" % i,
|
||||
"foo_image%d" % i)
|
||||
setattr(self, 'http_server%d_pid' % i, ret[0])
|
||||
setattr(self, 'http_port%d' % i, ret[1])
|
||||
setattr(self, 'http_server%d' % i, ret[1])
|
||||
setattr(self, 'http_port%d' % i, ret[2])
|
||||
|
||||
def tearDown(self):
|
||||
for i in range(3):
|
||||
pid = getattr(self, 'http_server%d_pid' % i, None)
|
||||
if pid:
|
||||
os.kill(pid, signal.SIGKILL)
|
||||
httpd = getattr(self, 'http_server%d' % i, None)
|
||||
if httpd:
|
||||
httpd.shutdown()
|
||||
httpd.server_close()
|
||||
|
||||
super(TestImagesMultipleBackend, self).tearDown()
|
||||
|
||||
@ -4605,7 +4608,7 @@ class TestImagesMultipleBackend(functional.MultipleBackendFunctionalTest):
|
||||
func_utils.wait_for_status(request_path=path,
|
||||
request_headers=self._headers(),
|
||||
status='active',
|
||||
max_sec=2,
|
||||
max_sec=15,
|
||||
delay_sec=0.2)
|
||||
expect_c = six.text_type(hashlib.md5(image_data).hexdigest())
|
||||
expect_h = six.text_type(hashlib.sha512(image_data).hexdigest())
|
||||
@ -4766,7 +4769,7 @@ class TestImagesMultipleBackend(functional.MultipleBackendFunctionalTest):
|
||||
func_utils.wait_for_status(request_path=path,
|
||||
request_headers=self._headers(),
|
||||
status='active',
|
||||
max_sec=2,
|
||||
max_sec=15,
|
||||
delay_sec=0.2)
|
||||
expect_c = six.text_type(hashlib.md5(image_data).hexdigest())
|
||||
expect_h = six.text_type(hashlib.sha512(image_data).hexdigest())
|
||||
@ -4909,7 +4912,7 @@ class TestImagesMultipleBackend(functional.MultipleBackendFunctionalTest):
|
||||
})
|
||||
|
||||
# Start http server locally
|
||||
pid, port = test_utils.start_standalone_http_server()
|
||||
thread, httpd, port = test_utils.start_standalone_http_server()
|
||||
|
||||
image_data_uri = 'http://localhost:%s/' % port
|
||||
data = jsonutils.dumps({'method': {
|
||||
@ -4939,7 +4942,8 @@ class TestImagesMultipleBackend(functional.MultipleBackendFunctionalTest):
|
||||
status='active')
|
||||
|
||||
# kill the local http server
|
||||
os.kill(pid, signal.SIGKILL)
|
||||
httpd.shutdown()
|
||||
httpd.server_close()
|
||||
# Ensure image is created in default backend
|
||||
path = self._url('/v2/images/%s' % image_id)
|
||||
response = requests.get(path, headers=self._headers())
|
||||
@ -5069,7 +5073,7 @@ class TestImagesMultipleBackend(functional.MultipleBackendFunctionalTest):
|
||||
})
|
||||
|
||||
# Start http server locally
|
||||
pid, port = test_utils.start_standalone_http_server()
|
||||
thread, httpd, port = test_utils.start_standalone_http_server()
|
||||
|
||||
image_data_uri = 'http://localhost:%s/' % port
|
||||
data = jsonutils.dumps({'method': {
|
||||
@ -5099,7 +5103,8 @@ class TestImagesMultipleBackend(functional.MultipleBackendFunctionalTest):
|
||||
status='active')
|
||||
|
||||
# kill the local http server
|
||||
os.kill(pid, signal.SIGKILL)
|
||||
httpd.shutdown()
|
||||
httpd.server_close()
|
||||
|
||||
# Ensure image is created in different backend
|
||||
path = self._url('/v2/images/%s' % image_id)
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
import atexit
|
||||
import os.path
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
import fixtures
|
||||
@ -163,8 +164,7 @@ class ApiTest(test_utils.BaseTestCase):
|
||||
# use the empty db created and cached as a tempfile
|
||||
# instead of spending the time creating a new one
|
||||
db_location = os.environ[glance_db_env]
|
||||
test_utils.execute('cp %s %s/tests.sqlite'
|
||||
% (db_location, self.test_dir))
|
||||
shutil.copyfile(db_location, "%s/tests.sqlite" % self.test_dir)
|
||||
else:
|
||||
test_utils.db_sync()
|
||||
|
||||
@ -172,8 +172,7 @@ class ApiTest(test_utils.BaseTestCase):
|
||||
# can be reused for future tests
|
||||
(osf, db_location) = tempfile.mkstemp()
|
||||
os.close(osf)
|
||||
test_utils.execute('cp %s/tests.sqlite %s'
|
||||
% (self.test_dir, db_location))
|
||||
shutil.copyfile('%s/tests.sqlite' % self.test_dir, db_location)
|
||||
os.environ[glance_db_env] = db_location
|
||||
|
||||
# cleanup the temp file when the test suite is
|
||||
|
@ -135,8 +135,8 @@ class TestImportTask(test_utils.BaseTestCase):
|
||||
self.assertFalse(os.path.exists(tmp_image_path))
|
||||
self.assertTrue(os.path.exists(image_path))
|
||||
self.assertEqual(1, len(list(self.image.locations)))
|
||||
self.assertEqual("file://%s/%s" % (self.test_dir,
|
||||
self.image.image_id),
|
||||
self.assertEqual("file://%s%s%s" % (self.test_dir, os.sep,
|
||||
self.image.image_id),
|
||||
self.image.locations[0]['url'])
|
||||
|
||||
self._assert_qemu_process_limits(tmock)
|
||||
|
@ -108,11 +108,9 @@ class IsolatedUnitTest(StoreClearingUnitTest):
|
||||
DEFAULT_REGISTRY_PORT = 9191
|
||||
DEFAULT_API_PORT = 9292
|
||||
|
||||
if (client.port == DEFAULT_API_PORT and
|
||||
client.host == '0.0.0.0'):
|
||||
if client.port == DEFAULT_API_PORT:
|
||||
return stubs.FakeGlanceConnection
|
||||
elif (client.port == DEFAULT_REGISTRY_PORT and
|
||||
client.host == '0.0.0.0'):
|
||||
elif client.port == DEFAULT_REGISTRY_PORT:
|
||||
return stubs.FakeRegistryConnection(registry=self.registry)
|
||||
|
||||
self.patcher = mock.patch(
|
||||
|
@ -588,8 +588,11 @@ class ServerTest(test_utils.BaseTestCase):
|
||||
keepalive=False,
|
||||
socket_timeout=900)
|
||||
|
||||
def test_number_of_workers(self):
|
||||
def test_number_of_workers_posix(self):
|
||||
"""Ensure the number of workers matches num cpus limited to 8."""
|
||||
if os.name == 'nt':
|
||||
raise self.skipException("Unsupported platform.")
|
||||
|
||||
def pid():
|
||||
i = 1
|
||||
while True:
|
||||
|
@ -281,6 +281,7 @@ class ImageCacheTestCase(object):
|
||||
self.assertEqual(['0', '1', '2'],
|
||||
self.cache.get_queued_images())
|
||||
|
||||
@skip_if_disabled
|
||||
def test_open_for_write_good(self):
|
||||
"""
|
||||
Test to see if open_for_write works in normal case
|
||||
@ -300,6 +301,7 @@ class ImageCacheTestCase(object):
|
||||
self.assertFalse(os.path.exists(incomplete_file_path))
|
||||
self.assertFalse(os.path.exists(invalid_file_path))
|
||||
|
||||
@skip_if_disabled
|
||||
def test_open_for_write_with_exception(self):
|
||||
"""
|
||||
Test to see if open_for_write works in a failure case for each driver
|
||||
@ -324,6 +326,7 @@ class ImageCacheTestCase(object):
|
||||
self.assertFalse(os.path.exists(incomplete_file_path))
|
||||
self.assertTrue(os.path.exists(invalid_file_path))
|
||||
|
||||
@skip_if_disabled
|
||||
def test_caching_iterator(self):
|
||||
"""
|
||||
Test to see if the caching iterator interacts properly with the driver
|
||||
@ -351,6 +354,7 @@ class ImageCacheTestCase(object):
|
||||
self.assertFalse(os.path.exists(incomplete_file_path))
|
||||
self.assertFalse(os.path.exists(invalid_file_path))
|
||||
|
||||
@skip_if_disabled
|
||||
def test_caching_iterator_handles_backend_failure(self):
|
||||
"""
|
||||
Test that when the backend fails, caching_iter does not continue trying
|
||||
@ -374,6 +378,7 @@ class ImageCacheTestCase(object):
|
||||
# make sure bad image was not cached
|
||||
self.assertFalse(self.cache.is_cached(image_id))
|
||||
|
||||
@skip_if_disabled
|
||||
def test_caching_iterator_falloffend(self):
|
||||
"""
|
||||
Test to see if the caching iterator interacts properly with the driver
|
||||
@ -402,6 +407,7 @@ class ImageCacheTestCase(object):
|
||||
self.assertFalse(os.path.exists(incomplete_file_path))
|
||||
self.assertTrue(os.path.exists(invalid_file_path))
|
||||
|
||||
@skip_if_disabled
|
||||
def test_gate_caching_iter_good_checksum(self):
|
||||
image = b"12345678990abcdefghijklmnop"
|
||||
image_id = 123
|
||||
@ -417,6 +423,7 @@ class ImageCacheTestCase(object):
|
||||
# checksum is valid, fake image should be cached:
|
||||
self.assertTrue(cache.is_cached(image_id))
|
||||
|
||||
@skip_if_disabled
|
||||
def test_gate_caching_iter_bad_checksum(self):
|
||||
image = b"12345678990abcdefghijklmnop"
|
||||
image_id = 123
|
||||
|
@ -165,7 +165,9 @@ class TestImagesController(base.IsolatedUnitTest):
|
||||
'metadata': {}, 'status': 'active'}],
|
||||
disk_format='raw',
|
||||
container_format='bare',
|
||||
status='active'),
|
||||
status='active',
|
||||
created_at=DATETIME,
|
||||
updated_at=DATETIME),
|
||||
_db_fixture(UUID2, owner=TENANT1, checksum=CHKSUM1,
|
||||
os_hash_algo=FAKEHASHALGO, os_hash_value=MULTIHASH2,
|
||||
name='2', size=512, virtual_size=2048,
|
||||
@ -175,13 +177,19 @@ class TestImagesController(base.IsolatedUnitTest):
|
||||
status='active',
|
||||
tags=['redhat', '64bit', 'power'],
|
||||
properties={'hypervisor_type': 'kvm', 'foo': 'bar',
|
||||
'bar': 'foo'}),
|
||||
'bar': 'foo'},
|
||||
created_at=DATETIME + datetime.timedelta(seconds=1),
|
||||
updated_at=DATETIME + datetime.timedelta(seconds=1)),
|
||||
_db_fixture(UUID3, owner=TENANT3, checksum=CHKSUM1,
|
||||
os_hash_algo=FAKEHASHALGO, os_hash_value=MULTIHASH2,
|
||||
name='3', size=512, virtual_size=2048,
|
||||
visibility='public', tags=['windows', '64bit', 'x86']),
|
||||
visibility='public', tags=['windows', '64bit', 'x86'],
|
||||
created_at=DATETIME + datetime.timedelta(seconds=2),
|
||||
updated_at=DATETIME + datetime.timedelta(seconds=2)),
|
||||
_db_fixture(UUID4, owner=TENANT4, name='4',
|
||||
size=1024, virtual_size=3072),
|
||||
size=1024, virtual_size=3072,
|
||||
created_at=DATETIME + datetime.timedelta(seconds=3),
|
||||
updated_at=DATETIME + datetime.timedelta(seconds=3)),
|
||||
]
|
||||
[self.db.image_create(None, image) for image in self.images]
|
||||
|
||||
@ -4649,7 +4657,8 @@ class TestMultiImagesController(base.MultiIsolatedUnitTest):
|
||||
'metadata': {}, 'status': 'active'}],
|
||||
disk_format='raw',
|
||||
container_format='bare',
|
||||
status='active'),
|
||||
status='active',
|
||||
created_at=DATETIME),
|
||||
_db_fixture(UUID2, owner=TENANT1, checksum=CHKSUM1,
|
||||
name='2', size=512, virtual_size=2048,
|
||||
visibility='public',
|
||||
@ -4658,12 +4667,15 @@ class TestMultiImagesController(base.MultiIsolatedUnitTest):
|
||||
status='active',
|
||||
tags=['redhat', '64bit', 'power'],
|
||||
properties={'hypervisor_type': 'kvm', 'foo': 'bar',
|
||||
'bar': 'foo'}),
|
||||
'bar': 'foo'},
|
||||
created_at=DATETIME + datetime.timedelta(seconds=1)),
|
||||
_db_fixture(UUID3, owner=TENANT3, checksum=CHKSUM1,
|
||||
name='3', size=512, virtual_size=2048,
|
||||
visibility='public', tags=['windows', '64bit', 'x86']),
|
||||
visibility='public', tags=['windows', '64bit', 'x86'],
|
||||
created_at=DATETIME + datetime.timedelta(seconds=2)),
|
||||
_db_fixture(UUID4, owner=TENANT4, name='4',
|
||||
size=1024, virtual_size=3072),
|
||||
size=1024, virtual_size=3072,
|
||||
created_at=DATETIME + datetime.timedelta(seconds=3)),
|
||||
]
|
||||
[self.db.image_create(None, image) for image in self.images]
|
||||
|
||||
|
@ -79,7 +79,7 @@ class TestRegistryV2Client(base.IsolatedUnitTest,
|
||||
created_at=uuid2_time)]
|
||||
self.destroy_fixtures()
|
||||
self.create_fixtures()
|
||||
self.client = rclient.RegistryClient("0.0.0.0")
|
||||
self.client = rclient.RegistryClient("127.0.0.1")
|
||||
|
||||
def tearDown(self):
|
||||
"""Clear the test environment"""
|
||||
|
@ -22,6 +22,7 @@ import shlex
|
||||
import shutil
|
||||
import socket
|
||||
import subprocess
|
||||
import threading
|
||||
|
||||
from alembic import command as alembic_command
|
||||
import fixtures
|
||||
@ -176,7 +177,11 @@ class depends_on_exe(object):
|
||||
|
||||
def __call__(self, func):
|
||||
def _runner(*args, **kw):
|
||||
cmd = 'which %s' % self.exe
|
||||
if os.name != 'nt':
|
||||
cmd = 'which %s' % self.exe
|
||||
else:
|
||||
cmd = 'where.exe', '%s' % self.exe
|
||||
|
||||
exitcode, out, err = execute(cmd, raise_error=False)
|
||||
if exitcode != 0:
|
||||
args[0].disabled_message = 'test requires exe: %s' % self.exe
|
||||
@ -325,7 +330,11 @@ def execute(cmd,
|
||||
path_ext = [os.path.join(os.getcwd(), 'bin')]
|
||||
|
||||
# Also jack in the path cmd comes from, if it's absolute
|
||||
args = shlex.split(cmd)
|
||||
if os.name != 'nt':
|
||||
args = shlex.split(cmd)
|
||||
else:
|
||||
args = cmd
|
||||
|
||||
executable = args[0]
|
||||
if os.path.isabs(executable):
|
||||
path_ext.append(os.path.dirname(executable))
|
||||
@ -484,7 +493,7 @@ def start_http_server(image_id, image_data):
|
||||
self.send_response(http.OK)
|
||||
self.send_header('Content-Length', str(len(fixture)))
|
||||
self.end_headers()
|
||||
self.wfile.write(fixture)
|
||||
self.wfile.write(six.b(fixture))
|
||||
return
|
||||
|
||||
def do_HEAD(self):
|
||||
@ -510,11 +519,11 @@ def start_http_server(image_id, image_data):
|
||||
httpd = BaseHTTPServer.HTTPServer(server_address, handler_class)
|
||||
port = httpd.socket.getsockname()[1]
|
||||
|
||||
pid = os.fork()
|
||||
if pid == 0:
|
||||
httpd.serve_forever()
|
||||
else:
|
||||
return pid, port
|
||||
thread = threading.Thread(target=httpd.serve_forever)
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
|
||||
return thread, httpd, port
|
||||
|
||||
|
||||
class RegistryAPIMixIn(object):
|
||||
@ -730,8 +739,8 @@ def start_standalone_http_server():
|
||||
httpd = BaseHTTPServer.HTTPServer(server_address, handler_class)
|
||||
port = httpd.socket.getsockname()[1]
|
||||
|
||||
pid = os.fork()
|
||||
if pid == 0:
|
||||
httpd.serve_forever()
|
||||
else:
|
||||
return pid, port
|
||||
thread = threading.Thread(target=httpd.serve_forever)
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
|
||||
return thread, httpd, port
|
||||
|
Loading…
Reference in New Issue
Block a user